Xcode 9 only: tvOS permission denied to create directory under "Library"

When I upgraded from Xcode 8 to 9, existing code that creates a directory started to fail. This code fails on Xcode 9 on a tvOS device. It runs fine in the simulator, and in Xcode 8 it runs fine in the simulator and on a device.


It's easy to repro. Create a new tvOS project with Xcode 9 with the following app delegate:


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions
        launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
    {
        var url = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
        url = url.appendingPathComponent("Testing123", isDirectory: true)
        print("URL = \(url)")
        print("Exists = \(fileExists(url: url))")

        do {
            try FileManager.default.createDirectory(at: url,
                withIntermediateDirectories: true, attributes: nil)
        }
        catch {
            assertionFailure("Could not create \(url): \(error)")
        }

        return true
    }

    private func fileExists(url: URL) -> Bool {
        return (try? url.checkResourceIsReachable()) ?? false
    }

}


Here's the output when running on a device:


URL = file:///var/mobile/Containers/Data/Application/1ECA9AED-6A8E-4208-A9B4-220ACE92621B/Library/Testing123/


Exists = false


fatal error: Could not create file:///var/mobile/Containers/Data/Application/1ECA9AED-6A8E-4208-A9B4-220ACE92621B/Library/Testing123/: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “Testing123” in the folder “Library”." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/1ECA9AED-6A8E-4208-A9B4-220ACE92621B/Library/Testing123, NSUnderlyingError=0x1c0053800 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}: file /Users/.../AppDelegate.swift, line 30


This reproduces with two different tvOS devices, and with two different development teams / app store companies / bundle IDs.


Is anyone else having this problem? Any insights would be appreciated, thanks!

Accepted Reply

If you have any ideas about [the] preferred location to store data on the file system, please let me know.

I can certainly talk to that.

The first thing to understand here is that tvOS was designed as an always connected system, and thus apps generally don’t store user data on the Apple TV. This is pretty clearly outlined in the Local Storage for Your App Is Limited section of the App Programming Guide for tvOS.

So, the best place to store stuff on tvOS very much depends on the type of stuff you’re storing. What is that in your case?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Does this problem affect both tvOS 10 and tvOS 11?

Have you tried passing

false
to
withIntermediateDirectories
?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes I tried passing false for withIntermediateDirectories, and it made no difference. Creating a directory under "Library" is pretty boilerplate code that I've used in projects for years, following the guidelines communicated in the last paragraph here:


https://developer.apple.com/library/content/qa/qa1699

These are the results from different environments:

1. All simulators in all environments succeed, regardless of target platform or OS version.

2. Running on an iOS 11 iPad with the deployment target set to iOS 11 - success.

3. Running on an iOS 11 iPad with the deployment target set to iOS 9.3 - success.

4. Running on an iOS 9.3 iPod touch with the deployment target set to iOS 9.3 - success.

5. Running on a tvOS 11 device with the deployment target set to tvOS 11 - FAILS.

6. Running on a tvOS 11 device with the deployment target set to tvOS 10.2 - FAILS.


I'm unable to try Xcode 8 with a tvOS 11 device since Xcode 8 doesn't support it, but I had been using Xcode 8 with tvOS 10 and everything worked fine. Things broke when I upgraded to Xcode 9. What I don't know is if the combination of Xcode 9 and tvOS 10.2 would succeed or not.


This is a blocking issue since my app can't write anything to the file system, which it is heavily dependent on. I've never run into anything like this before.

Creating a directory under "Library" is pretty boilerplate code that I've used in projects for years …

Right. I didn’t mean to imply that this shouldn’t work; I was just suggesting diagnostics.

What I don't know is if the combination of Xcode 9 and tvOS 10.2 would succeed or not.

Bummer. That test might have told us whether the issue is with tvOS itself or how Xcode built the executable.

I was going to ask whether this problem reproduces with other Apple TVs, but then I realised I updated my own Apple TV over the weekend so I can try it myself. And the answer is that it does. Given that, I recommend that you start by filing a bug about this. Please post your bug number, just for the record.

I set a breakpoint on

mkdir
(the system call) and I definitely see it fail with
EPERM
. Come to think of it though,
EPERM
is interesting because file system permission problems usually come back as
EACCES
(13).
EPERM
typically indicates some sort of sandbox restriction. And lo! look what shows up in the system log.
Oct  9 09:56:27 Apple-TV kernel(Sandbox)[0] <Notice>: Sandbox: xxsv(300) deny(1) file-write-create /private/var/mobile/Containers/Data/Application/AEB560D1-C786-4F4A-914A-32FF9D4A1CC1/Library/Testing123

It’s possible that this is a deliberate change (gated behind a ‘linked on or later’ check, which is why you’re only seeing it when building with Xcode 9). tvOS as a platform really wants folks to put data in specific locations to support the platform-specific storage model.

However, that’s really just speculation on my part. If you want to get to the bottom of this I recommend that you open a DTS tech support incident so you can discuss it with one of my colleagues (the author of QA1699 :-).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for taking the time to reproduce this. I've filed a bug as requested (34885748). If you have any ideas about a workaround or a preferred location to store data on the file system, please let me know.

If you have any ideas about [the] preferred location to store data on the file system, please let me know.

I can certainly talk to that.

The first thing to understand here is that tvOS was designed as an always connected system, and thus apps generally don’t store user data on the Apple TV. This is pretty clearly outlined in the Local Storage for Your App Is Limited section of the App Programming Guide for tvOS.

So, the best place to store stuff on tvOS very much depends on the type of stuff you’re storing. What is that in your case?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for the link. I can see how not allowing the creation of subdirectories could be an intentional change with this in mind. Thanks again, I appreciate the help.

Was there ever a solution for this issue as I’m in a similar situation myself...

Any solution will depend on the attributes of the data you’re trying to store. Please elaborate.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I have a need to create temporary cached data while my app is running on tvOS. I'm not able to create the subdirectories and subsequent files due to permission errors.


These files might be as large as a MB, with a life of 10's of seconds. I can see the base directories through an external program like iExplorer, I just can't add to them.


Please provide some direction on whether this has to be done all in memory or elsewhere. Also, does this affect keychain storage, where I have need to store some keys for decryption purposes.

To start, none of what’s been discussed so far on this thread applies to the keychain. Keychain items are stored in a system-wide database, not in your app’s container.

With regards your “temporary cached data”, have you tried using the Caches (

-URLForDirectory:inDomain:appropriateForURL:create:error:
with the
NSCachesDirectory
selector) directory? Or the Temporary directory (
temporaryDirectory
property on
NSFileManager
)? Those seems like the best options based on the info you’ve posted so far.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes I did already try that, here's the code that is failing:


NSURL *documentsDir = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *dtcp_home = [documentsDir URLByAppendingPathComponent:@"DTCP_HOME"];
    NSError *err;
    NSString *dtcpDir = [[AMSMyUtils getDocumentsDirectory] stringByAppendingPathComponent:@"DTCP_HOME"];
    if (![[NSFileManager defaultManager] createDirectoryAtPath:[dtcp_home absoluteString] withIntermediateDirectories:YES attributes:nil error:&err]) {
        error_print(@"error: %@", [err localizedDescription]);
        return NO;
    }

And here is the error return:


2018-10-16 16:17:38.602621-0700 CTA Prototype[1196:100264] [1] AppDelegate.m:61 error: You don’t have permission to save the file “DTCP_HOME” in the folder “Caches”.


This is happening across the board - documents, library, Application Support, and Caches.


This code has been working on iOS for years, so I didn't expect to have to change it for tvOS.

I’m not sure why your code worked previously, but the reason why it’s failing now is a bug on line 5. For the

path
parameter you pass in
[dtcp_home absoluteString]
, which returns a file URL, not a path. That is, you get this:
file:///Users/quinn/Library/Developer/CoreSimulator/Devices/3975DC48-86DA-4987-B96F-9525993DEE2E/data/Containers/Data/Application/B9D91E27-3BEE-46DA-81C7-932D38E87836/Library/Caches/DTCP_HOME

when you’re expecting to get this:

/Users/quinn/Library/Developer/CoreSimulator/Devices/3975DC48-86DA-4987-B96F-9525993DEE2E/data/Containers/Data/Application/B9D91E27-3BEE-46DA-81C7-932D38E87836/Library/Caches/DTCP_HOME

The quick fix is to change your code to use

[dtcp_home path]
but I recommend that you instead change your code to work in URL space. For example:
NSURL * cachesDir = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
assert(cachesDir != nil);

NSURL * docDir = [cachesDir URLByAppendingPathComponent:@"DTCP_HOME"];

NSError * err;
BOOL success = [[NSFileManager defaultManager] createDirectoryAtURL:docDir withIntermediateDirectories:NO attributes:nil error:&err];
if ( ! success ) {
    NSLog(@"failed, %@", err);
} else {
    NSLog(@"succeeded");
}

Why URL space? Well, it’s best to be consistent about whether to use paths or file URLs, and we generally recommend file URLs for new code because they can do things that paths can’t do (for example, deal with security scopes).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Quinn for this. The string paths have worked for years using the string methods as this example shows.


    NSString *dtcpDataDir = [[AMSMyUtils getDocumentsDirectory] stringByAppendingPathComponent:@"DTCP_HOME/Data"];
    if (![[NSFileManager defaultManager] createDirectoryAtPath:dtcpDataDir withIntermediateDirectories:YES attributes:nil error:&err]) {
        error_print(@"error: %@", [err localizedDescription]);
        return NO;
    }

I will have to convert to NSURL based methods for Apple TV, not a problem and I have already tested my critical operations with the new approach.


I have another question related to storage, will data in the cache or tmp directory be purged by tvOS while the app is running?

I have another question related to storage …

It’d be best to put that in a new thread; this thread is already long enough.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"