7

I have an app which loads a bundled m4a audio file as a local resource and it has worked well for many years now. I'm updating the app to iOS 11.3/XCode 9.3 and it is now failing on iPad (works on iPhone) when I press my play button:

2018-05-13 20:45:24.437626-0700 my app[6175:218735] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
2018-05-13 20:45:24.437791-0700 my app[6175:218735] Cannot start load of Task <117E064E-ABB3-45F2-8D64-76397B140092>.<0> since it does not conform to ATS policy
2018-05-13 20:45:24.437948-0700 my app[6175:218732] NSURLConnection finished with error - code -1022

My code:

NSURL *medURL   = [[NSBundle mainBundle] URLForResource: @"MyFile"
                                          withExtension: @"m4a"];
NSError *playerError = nil;
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: medURL error: &playerError];
self.appSoundPlayer = newPlayer;

// "Preparing to play" attaches to the audio hardware and ensures that playback
//      starts quickly when the user taps Play
[appSoundPlayer prepareToPlay];

It fails on the prepareToPlay.

Reading other answers I've found a lot of information on the ATS error so I added this to my plist file for the app, cleaned, rebuilt and ran it -- but I am still getting the error:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsLocalNetworking</key>
    <true/>
    <key>NSAllowsArbitraryLoadsForMedia</key>
    <true/>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSAllowsArbitraryLoadsInWebContent</key>
    <true/>
    <key>NSExceptionAllowsInsecureHTTPLoads</key>
    <true/> </dict>

I can't use the NSExceptionDomain key because this is a local resource, there is no domain. How can I fix this?

Note: to be clear, this file is bundled into the app and has not been downloaded at any time. This is not an "http" url, it is a "file:" url.

Update: Looking at the CFNetworkLog I realized that there was a NSURLConnection to the network happening just before the audio prep in AppDelegate. When I removed this call everything started working. Since both work independently, it appears to me that there is some sort of conflict in NSURLConnection which is happening -- possibly a bug in the API? It would be good to know what the proper work-around is for this, I have gotten it to work well enough by removing the prepareToPlay call above -- this is not ideal as it takes longer when the user goes to play the audio file.

Another update: the app started working today for no apparent reason, I hadn't changed anything since the last time I tried it. Unfortunately I can't determine if fixes work at this point! To be sure, it does appear that my plist keys are working in my iOS 11.3 simulator.

Alan Moore
  • 6,525
  • 6
  • 55
  • 68
  • Any API/Network Call is there in your app? I think you are downloading song and saving to local but due to ATS the song file not able save in you local as HTTP call fails correct me if i'm wrong. – Abhishek Thapliyal May 14 '18 at 04:19
  • @AbhishekThapliyal -- you are wrong, this file is bundled into the app, it is not downloaded. – Alan Moore May 14 '18 at 04:22
  • Any other API call you have in your app? – Abhishek Thapliyal May 14 '18 at 04:24
  • There are many API calls, but this is where the debugger breaks on the exception so I assume that means that this is what caused the exception. Is that a bad assumption? – Alan Moore May 14 '18 at 04:26
  • I should also mention that the exception does not occur during the other API calls, it occurs when I click my play button and this is the code that is executed on Play -- there are no API network calls in this handler. – Alan Moore May 14 '18 at 04:27
  • No Bad assumption , just for test simply can you add ...... NSAppTransportSecurity NSAllowsArbitraryLoads – Abhishek Thapliyal May 14 '18 at 04:27
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/170975/discussion-between-abhishek-thapliyal-and-alan-moore). – Abhishek Thapliyal May 14 '18 at 04:27
  • 1
    You added a bounty, but you haven't provided the details the chat. Please try the things in the chat. ATS should not be blocking anything that is referenced in the local file system. I think something else is going on here. Specifically turning on the CFNetwork Logging so you can see the URL it is trying to load. There might be another problem going on with how the prepareToPlay method works. – wottle May 20 '18 at 20:26
  • Have you tried with different audio files? A different m4a? An mp3? Also, have you tried adding the `NSAllowsArbitraryLoadsForMedia` key? Instead of diabling all ATS, just for media (I know you shouldn't have to, but this is at least a less dangerous solution). – wottle May 22 '18 at 20:28
  • I have determined a workaround -- just don't call PrepareToPlay. I have tried everything in the chat. @wottle I have tried NSAllowsArbitraryLoadsForMedia but it didn't work. The audio file works fine on iPhone so I don't think there is anything wrong with it. – Alan Moore May 23 '18 at 21:16
  • Re: "I can't use the NSExceptionDomain key because this is a local resource, there is no domain. How can I fix this?" *Actually there is a domain*, "localhost", maybe try using that. [Here's a quick example](https://gist.github.com/ParityError/ca6b16a04b8c44a580ab6c39527f567e). And yes, without PrepareToPlay users with older devices might experience so much lag they'd rather Quit than Play... – l'L'l May 23 '18 at 22:05
  • Also, by using `NSAllowsArbitraryLoadsInWebContent`, `NSAllowsLocalNetworking`, `NSAllowsArbitraryLoadsForMedia` you are overriding `NSAllowsArbitraryLoads`. I recommend reading ["App Transport Security dictionary primary keys"](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW34) thoroughly to understand what keys should be included. Including conflicting keys is pointless; you'll just end up with the same undesired behavior. – l'L'l May 23 '18 at 22:23
  • Yes @l'L'l, but it's important to note that the override you mention only applies in iOS10 and later. In iOS 9, including both gives you the benefit that it will work in iOS9, where `NSAllowsArbitraryLoadsForMedia` was not yet supported, by still honoring the blanket 'NSAllowsArbitraryLoads`. So I wouldn't make claims of something being pointless when it is a specific strategy by Apple to allow backwards compatibility. And it certainly doesn't result in the same behavior on different iOS versions. I would recommend fully understanding the keys before commenting false information. – wottle May 24 '18 at 01:01
  • @wottle: I might suggest reading the second sentence of the OPs question "I'm updating the app to iOS 11.3". – l'L'l May 24 '18 at 01:03
  • In case you need to know the detail, it is in the link you referenced: "In iOS 10 and later, and macOS 10.12 and later, the value of this key (`NSAllowsArbitraryLoads`) is ignored—resulting in an effective value for this key of its default value of NO—if any of the following keys are present in your app’s Info.plist file: NSAllowsArbitraryLoadsForMedia NSAllowsArbitraryLoadsInWebContent NSAllowsLocalNetworking" – wottle May 24 '18 at 01:03
  • Regardless of the question, your comment is wrong. If you had clarified your comment with "If you can guarantee all your users are on iOS 10 and later...", it would have at least been closer to correct. But the OP didn't state his app would only be run on iOS 11.3. He's simply having an issue on iOS 11.3 devices. That doesn't mean he wants users on iOS9 to have an issue if he puts in a bad fix. – wottle May 24 '18 at 01:04
  • @wottle: By the question asked it's assumed the app is meant to be run on iOS 11.3. Having the three keys mentioned along with `NSAllowsArbitraryLoads` would do them absolutely no good; which part of that are you not understanding? What is not assumed is that the OP wants to, or has ever mentioned the app requires iOS 9. Exactly why would putting the correct keys in result in a "bad fix"? That's ludicrous. – l'L'l May 24 '18 at 01:24
  • did you try use `pathForResource:ofType:` – Bimawa May 24 '18 at 11:05
  • This is a legacy app, I want it to continue working on older iOS versions. When I removed the other NSURLConnection, I found that my NSAllows* keys were working correctly so I don't think that is the problem. – Alan Moore May 25 '18 at 02:49
  • @l'L'l You made a blanket statement that the additional keys are "conflicting". They are not. On different iOS versions, the keys can be combines in a way that make them useful. The opposite of pointless. If you had made your statement with the clarification that the keys will override each other on newer versions of iOS, at least you would have been closer to the truth. The fact is that you tried to call out something as wrong, when it wasn't. – wottle May 25 '18 at 03:13
  • @AlanMoore I agree that the keys are likely not your problem. The concern I have is that if you were to put in the 'NSAllowsArbitraryLoads` that would, in theory, make it so ATS should not block any connections. Technically, ATS shouldn't block anything for a local file url. I'm still not sure why it would, but I was wondering if there was something about the .mp4 that tries to download some metadata about the the audio file. When you turned on CFNetwork Logging, it should have given you the exact URL that was being blocked by ATS, as opposed to the generic message. Did you see that? – wottle May 25 '18 at 03:16
  • The url appears to be: file:///Users/alyanm/Library/Developer/CoreSimulator/Devices/8F2A97B1-72E8-4951-B694-52197EDA1AE3/data/Containers/Data/Application/12C2C580-A2B0-4B14-A1D0-81A0BAD3B3EB/Library/Cookies/com.mydomain.med.myappname.binarycookies -- but CFNetworkLogging doesn't report the error? Strangely, it has started working just now. Might be a race condition which I may have trouble reproducing! – Alan Moore May 25 '18 at 04:08
  • Yeah, you really need to have the CFNetwork Logging turned up when you get the message that a url was blocked by ATS. I think there may have been another call happening that was causing this. – wottle May 26 '18 at 04:46
  • Possible, but I never saw it in the log when it was failing. – Alan Moore May 26 '18 at 05:31

1 Answers1

5

In my project is using AVAudioPlayer to play audio, and my source code:

NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"MyFile"  ofType:@"m4a"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
AVAudioPlayer* audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
audioPlayer.numberOfLoops = -1; //Infinite
[audioPlayer setVolume:1];
[audioPlayer play];
chaunv
  • 833
  • 6
  • 15
  • I'm afraid the problem went away, your suggestion does work but unfortunately I can't tell if it fixes the bug I was seeing before today! – Alan Moore May 25 '18 at 04:12