Post

Replies

Boosts

Views

Activity

Reply to When is the unverified branch of AppTransaction.shared entered?
Well that certainly doesn't look good. Must be a prod regression. Agree it's a frustrating experience, because there seems to be no rhyme or reason for when the unverified branch is entered. Would be great if we could get some official documentation on the conditions that lead to it. Sounds like you already opened a TSI. Sadly, TSIs that require hard digging go to the void (I generally try to resolve things myself, but I've filed 2 in the last year only when I had to, and neither were resolved with satisfaction). Not really sure what you could do, sorry you are getting 1 star reviews from it. Good luck to you
Topic: App & System Services SubTopic: StoreKit Tags:
Apr ’25
Reply to Drop from URLSession to Network framework for SNI
I think this has a good chance of working! I've been fiddling with my wifi's dns settings by putting in resolver IPs that go nowhere. With this snippet, my SDK can still reach the proper destination (and without this snippet, it cannot): /// The constants below are taken from Cloudflare's DNS over TLS reference: /// https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-tls/ /// /// The approach is from Quinn the Eskimo's forum post: /// https://developer.apple.com/forums/thread/780602 func myURLSession() -> URLSession { let host = NWEndpoint.hostPort(host: "one.one.one.one", port: 853) let endpoints: [NWEndpoint] = [ .hostPort(host: "1.1.1.1", port: 853), .hostPort(host: "1.0.0.1", port: 853), .hostPort(host: "2606:4700:4700::1111", port: 853), .hostPort(host: "2606:4700:4700::1001", port: 853) ] let c = NWParameters.PrivacyContext.default c.requireEncryptedNameResolution(true, fallbackResolver: .tls(host, serverAddresses: endpoints)) return URLSession( configuration: .ephemeral, delegate: self.delegate, delegateQueue: nil ) } I'm not sure what happens when the system can access a valid resolver, but one that is missing my A records. Perhaps apple's framework will trust the first response as authoritative, without falling back to fallbackResolver to try again. This is likely a nonissue, though, because I have my doubts that Comcast has a resolver that will satisfy requireEncryptedNameResolution to begin with. Going to try a limited test in the wild. Thank you Quinn!
Apr ’25
Reply to Change the OSLog level that is written to Xcode console of a dependent library
Thanks Quinn the Eskimo! I don't like that quite as much, because then all logs from my lib will be at the same level (the one passed in by the user of the lib). The something-is-very-broken logs would be drowned out in a sea of debug logs. For example, there are debug logs that emit on each audio buffer coming from the mic, containing the sample count in the buffer. These are frequent and only meant to be 'turned on' when I'm trying to debug an issue. I'll share what I ended up doing. It makes log lines a little cumbersome for me, but thankfully users of my lib don't have to deal with that. import OSLog public enum AIProxyLogLevel: Int { case debug case info case warning case error case critical func isAtOrAboveThresholdLevel(_ threshold: AIProxyLogLevel) -> Bool { return self.rawValue >= threshold.rawValue } } internal var aiproxyCallerDesiredLogLevel = AIProxyLogLevel.warning internal let aiproxyLogger = Logger( subsystem: Bundle.main.bundleIdentifier ?? "UnknownApp", category: "AIProxy" ) // Why not create a wrapper around OSLog instead of forcing log callsites to include an `if ll(<level>)` check? // Because I like the Xcode log feature that links to the source location of the log. // If you create a wrapper, even one that is inlined, the Xcode source feature always links to the wrapper location. @inline(__always) internal func ll(_ logLevel: AIProxyLogLevel) -> Bool { return logLevel.isAtOrAboveThresholdLevel(aiproxyCallerDesiredLogLevel) } And then my lib adds log lines like so: if ll(.warning) { aiproxyLogger.warning("this is a warning log") } if ll(.debug) { aiproxyLogger.debug("this is a debug log") } ... And I expose a simple interface to the user of the lib that internally sets aiproxyCallerDesiredLogLevel. It's a little unorthodox, but it's getting the job done for me, and Xcode's link-to-log-source functionality still works. Big fan of your work. Every time I stumble on a great forum or technical note thread it always has your name on it. ps. long live macnetworkprog! Lou
Mar ’25
Reply to AudioComponentInstanceNew takes up to five seconds to complete
I am also seeing this with the AVAudioEngine APIs. @oliverpahl-mitel did you ever hear back from Apple on your feedback assistant post? At runtime here are the errors that get dumped to console (I get the KeystrokeSuppressorCore.cpp:44 ERROR, but I don't see the AggInpStreamsChanged wait failed you are getting). If I setup an input node with inputNode.setVoiceProcessingEnabled(true), here are the errors that get dumped to console: AddInstanceForFactory: No factory registered for id <CFUUID 0x60000300f780> F8BB1C28-BAE8-11D6-9C31-00039315CD46 throwing -10877 throwing -10877 vpPlatformUtil.mm:312 Cannot retrieve theDeviceBoardID string... vpPlatformUtil.mm:312 Cannot retrieve theDeviceBoardID string... AudioHardware-mac-imp.cpp:409 AudioObjectHasProperty: no object with given ID 0 AUVPAggregate.cpp:4929 Failed to get current tap stream physical format, err=2003332927 vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed. vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0 vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo HALB_IOThread.cpp:326 HALB_IOThread::_Start: there already is a thread HALB_IOThread.cpp:326 HALB_IOThread::_Start: there already is a thread For reference, if I switch inputNode.setVoiceProcessingEnabled(false) and keep all of my surrounding code the same, the following messages are dumped to console: AddInstanceForFactory: No factory registered for id <CFUUID 0x600003ff2500> F8BB1C28-BAE8-11D6-9C31-00039315CD46 throwing -10877 throwing -10877
Topic: Media Technologies SubTopic: Audio Tags:
Dec ’24
Reply to Turning on setVoiceProcessingEnabled bumps channel count to 5
I just found something interesting. While AVAudioConverter doesn't play nicely with the five channels, it seems like AVAudioEngine's built in converters do. Because if I specify a tap like this: let desiredTapFormat = AVAudioFormat( commonFormat: .pcmFormatInt16, sampleRate: inputPCMFormat.sampleRate, channels: 1, interleaved: false ) inputNode.installTap(onBus: 0, bufferSize: 256, format: desiredTapFormat) { buffer, when in ... } I find that the buffer argument already has a single channel, and it's not silence!
Topic: Community SubTopic: Apple Developers Tags:
Dec ’24
Reply to How to get in contact with team that manages DeviceCheck
(Post replies, not comments.) Thanks for the tip. No, what I mean is, if I, an attacker, have my own trivial app, and I collect tokens that my users send to my server (no MITM needed). Then I send those tokens to your app's API. These would return non-200s from Apple's servers when the token was validated in the server-to-server call. It's actually quite tricky to get DeviceCheck to pass in the first place. A bunch of things need to be true: An App Identifier in your developer dashboard must match the bundle identifier of your app exactly The app must be signed using a certificate from the team account that has that App Identifier A DeviceCheck secret key (used for the backend-to-backend communication) must be created from the same account as 1 and 2. The fact that it's hard to get right gave me some confidence that it was also hard to forge. Now, I'm not so sure
Dec ’24