I’m creating a .pdf reader for tvOS. I’d like to store the .pdfs in an iCloud Drive folder and access them read-only. Not suprisingly, I’m encountering difficulty getting iCloud to work: FileManager.default.url(forUbiquityContainerIdentifier: nil) always returns nil.
I’m assuming that I need to added the entry to info.plist (why doesn’t Xcode do this?) I’m including the following. The key matches my bundle ID with a prefix of ‘iCloud’.
<dict>
<key>iCloud.com.spiffcleanser.books</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>books</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
I have a number of questions but all are related so I hope its acceptable to ask them all at once. At this point I’m merely trying to get the container URL.
I’m using the default ubiquity container. I’ve created an iCloud entitlement and have checked CloudKit. Do I need to specify a container from the list or is the default container used implicitly if I don’t specify one?
My identifier on the developer sigh indicates that I have an iCloud entitilement. The container assignment matches what I specify in Xcode. If I’m using the default container, should the list be empty or should I configure a container with the bundle ID of my app in the same manner as in the .plist file?
I know that after modifying the entitlements I need to rebuild the provisioning profile, is it sufficient to toggle ‘Automatically manage signing’ or do I need to explicitly create a profile?
Is it necessary to bump the version number each time I change the config? I assume incrementing the build number is sufficient.
Thanks for any help.
I've solved this. iCloud support for documents is not available on tvOS. This is due to only minimal local storage being available.
I have been using an iCloud container without a problem during my developing phase.
The container app had until now a default NSUbiquitousContainerName, using the bundle if of the application.
Now I would like to have a friendlier name for the folder appearing in iCloud Drive, so I have changed the NSUbiquitousContainerName value to my app's name.
<key>NSUbiquitousContainers</key>
<dict>
<key>container-id</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>AppName</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
I have then bumped the build number, and run the app.
I used the following folder to export documents:
FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
And still the documents are exported to the old folder, instead of the new one.
What should I do to rename the container's folder?
If change the container name, you should increase build number.
I managed to fix the issue creating a new container and using that one instead of the default.
I had the same problem using iOS 13. It appears the old folder name is cached (as is the app icon image used as an overlay on the iCloud folder icon).
Changing NSUbiquitousContainerName in the plist didn't have an impact when using an iCloud account that I'd already used with the old container name, but I got the new folder name (and new app icon) when using with an iCloud account that had never before been connected to the app.
This caching persisted even when running the app on a new device and deleting the old folder from iCloud.
I have been struggling to get my iOS app to enable iCloud.
I have followed documentation and have switched on iCloud with CloudKit in my project in Xcode.
But, when I then upload the app to App Store Connect from Xcode, iCloud is not there. It does not show up under "Capabilities" and in using the app through TestFlight, iCloud is not enabled.
Is there something special I need to do? Using Xcode version 10.1 + Mac OS High Sierra 10.13.
Note: there is a bunch of detail below. If you want to skip the detail, the summary is that I have followed the straight forward route to enabling iCloud, but it is not yet showing up in App Store Connect or on device through TestFlight.
Steps:
I have followed documentation and tried a few solutions like here and here.
1) I open my project in xcode.
2) In the xcode Capabilities pane, I switch on iCloud, check CloudKit, and use default container (a container named: "iCloud.com.[bundle identifier]").
3) At developer.apple.com, I verify that the app Id I am using has automatically enabled iCloud and CloudKit (it shows as green there).
4) At developer.apple.com, create a provisioning profile using that app id. iCloud is listed among the "Enabled Services" for this profile.
5) Back in xcode, in the General pane, turn off automatically manage signing and select this provisioning profile. If I select "automatically manage signing", it uses an "iPhone Developer" account for the signing certificate, which seems incorrect; when I do manual signing and select the updated prov profile, the Signing Certificate correctly reads "Iphone Distribution: [team name]"
6) Archive app: product -> archive. (I have double-checked here that iCloud remains selected in the Capabilities pane, without error)
7) Upload app: window -> organizer -> select archive, click distribute app, and take it from there.
Result: Build gets uploaded to App Store Connect successfully, but no iCloud support anywhere to be seen.
What else do I need to do?
NOTES:
When I archive the app, the entitlements file (listed under AppName -> AppName -> AppName.entitlements), includes this:
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.$(CFBundleIdentifier)</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
</plist>
The aps-environment says "development". I have tried archiving and uploading this way, and it hasn't worked to enable icloud in App Store Connect. I have also tried manually switching this to "production" and archiving/uploading--but the result was the same there too.
--The underlying app is made with Nativescript, but I don't think that should matter for this stage, as I am uploading through xcode. (I have already gone through Nativescript info on this.)
--when I go into cloudkit dashboard, the Container says "in development". I haven't seen a way to change that without error
It's a very long story, therefore, it's hard to follow and get the whole picture, but base on your comments...
It seems that you are missing the console Cloud activation somehow. I guess you may be mixing the development and distribution profiles.
My advice now that you're moving to the App Store Connect is move EVERYTHING to Production environment and if you haven't, update the profiles/provisionings.
I quote
On the cloud kit dashboard, each container will have a blue box for
the Development environment and green box for the Production
environment.
You can create and delete record types on the fly in the (blue) Development environment. But once you deploy to the (green) Production environment, that schema becomes permanent in that container. You can't delete record types from the (green) Production environment.
Deleting is harsh...
Try this approach and let me know if I hit the spot! ;) If I didn't, don't vote my down just jet lol, comment and I'll try to help you further.
This tutorial is pretty well known but I consider it's great for iCloud first comers, check it out if you haven't:
https://www.raywenderlich.com/1000-cloudkit-tutorial-getting-started
Good luck!
I've figured out the essential issue here: I was confusing what is, basically, two different iClouds (or at least two very different ways to use iCloud as a developer).
This will be obvious to many/most people familiar with iOS programming, but it was the key point I did not grasp.
Without this information, I was thrown off by some people saying iCloud just "automatically works", while others would go into depth about the need to do configuration. They both were right--just talking about different things.
So this is what I did not grasp before: These are the two different uses of iCloud from the developer perspective:
1) Automatic, backup iCloud. This is the system-wide feature, that allows users to back up data from their device. Users turn it on through turning on “iCloud Backup” (settings --> apple id --> iCloud). Once on, the device automatically backs up the user's data regularly. This feature will automatically back up data from your app that is included in the app/Documents directory. Further info here.
See here for details on preventing certain data from being backed up if you want.
Other than putting data you want backed up in the system wide back up in the documents director, there is nothing you as an app developer need to do. You do not even need to enable iCloud through Xcode (I think--though others please correct me if I am wrong).
This will not load data from iCloud onto your app when the app is loaded. Rather, it saves a backup (of your app data and the other data on the user's phone)--the main benefit of which is that if the user loses their device they can recover that data on a different device.
2) App specific cloud storage through iCloud. This affirmatively does not happen automatically, and requires meaningful configuration.
This is where you can use iCloud like a backend cloud solution. For example, you can send specific data to iCloud when that data is created, and then later get that data from iCloud onto the app when the user loads the app (instead of storing that data locally on the user's device).
This is where CloudKit comes in.
To enable this, you need to generate the proper entitlements file--such as enabling iCloud w/ CloudKit on Xcode, and being sure the app id and provisioning profile confirm that CloudKit is enabled. For a bit more detail, see my steps 1 through 7 in my question. This should work to get things "enabled".
But that is just the first piece, you then need to get things "configured". To do this, you likely have to do some configuration of the container that CloudKit generates for your app, and add code specific to the backend process in your app. There is some good info in the tutorial linked in the prior answer.
Thanks to those who gave info on this, including Apple Developer Technical Support.
I am building an app that contains its own iCloud container. The capabilities configuration is attached below.
Below is the key entry in Info.plist.
<key>iCloud.com.dearle.Ascert</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
<key>NSUbiquitousContainerName</key>
<string>Ascert</string>
</dict>
It is my understanding that anything I put into my app container should show up in the iCloud drive under a folder named Ascert and that I should be able to open the document in my app from the iCloud drive.
I have tried everything I can think of, unfortunately I am not very experienced with xCode so am unsure of where I am going wrong.
Any help would be appreciated.
I faced the same issue as well, the setup you have done is correct. Try increasing the build number and version number before you run your code for the iCloud functionality. This worked for me. Apparently as long as these values are not increased the iCloud setup is not picked up
No, it won't be visible in iCloud drive. It will be in iCloud / Manage Storage/ Container name.
To sync files between different devices with same Apple id, you use method startDownloadingUbiquitousItemAtURL. Source
I'm developing an iCloud-enabled app where users will be able to import and export files via iCloud Drive. When browsing iCloud Drive, either using the UIDocumentPickerViewController (iOS 8) or the Finder (OS X Yosemite), I can see directories created/owned by other iCloud-Drive-enabled apps, such as, Automator, Keynote, or TextEdit.
I want our app to expose its ubiquitous documents directory in iCloud Drive, too, but haven't been able to figure it out yet. Within some of the aforementioned apps' Info.plist files, I've discovered this key:
<key>NSUbiquitousContainers</key>
<dict>
<key>com.apple.TextEdit</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
These keys are also documented here, but I haven't found any other documentation on the broader subject. Edit/Note: Although it does not contain the answer to my questions, the Document Picker Programming Guide is a helpful resource.
I've tried adding the above-mentioned keys/values to our app but didn't see any effect. Things I've noticed/tried:
For 3rd party apps, iCloud containers are constructed this way: iCloud.$(CFBundleIdentifier). I'm not sure why TextEdit only uses the pure bundle identifier, but for our identifier, I've tried both approaches, i.e., with and without the iCloud. prefix. I've also recognised that you need to hard-code the bundle identifier (i.e., don't use iCloud.$(CFBundleIdentifier)) as only the PLIST's values seem to be resolved at build time, but not the keys.
I've added a sub-directory programmatically (to <containerPath>/Documents) so the container is not empty. However, this shouldn't matter as all the other apps' directories were initially empty, too.
Some Apple apps that appear in iCloud Drive do not have these entries in their Info.plist, e.g., Numbers and Pages.
iCloud is set up correctly and I can programmatically look into the ubiquity container using the URL returned by [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];.
I am logged into an iCloud account where iCloud Drive is enabled. I can see my iCloud Drive content in the UIDocumentPickerViewController.
I use the iOS 8 beta 5 simulator (and Yosemite beta 5 to view the iCloud Drive directory on the Mac) (Edit/Note: This equally applies to beta 6)
This is how my Entitlements file looks like (relevant parts only)
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.$(CFBundleIdentifier)</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array/>
I've set this up using Xcode's UI in the Capabilities section. I don't get why the last key doesn't have an entry, but adding <string>iCloud.$(CFBundleIdentifier)</string> doesn't help. Instead, it makes Xcode complain in the Capabilities UI, so I've removed it. Edit/Note: In Xcode beta 6, this has been fixed, i.e., the ubiquity container identifier needs to be set and Xcode can fix that for you.
Original Questions: So... is it a bug? Does it not work yet? Am I doing it wrong? I couldn't find a known issue in the release notes.
Edit:
Two more things that I've tried:
Adding the (optional) NSUbiquitousContainerName key (+ value) to the container-specific dictionary, as suggested by Erikmitk.
Adding only the NSUbiquitousContainerIsDocumentScopePublic key/value to the PLIST root dictionary rather than the container-specific dictionary, as it's done in one of the WWDC sample apps (look for NewBox).
I was experiencing a similar problem with my application. I was able to make this work by doing the following:
Add the NSUbiquitousContainers setting to my Info.plist file according to documentation here https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/FileProvider.html. Here is the relevant code:
<dict>
<!-- ... other top-level Info.plist settings ... -->
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.example.MyApp</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
<key>NSUbiquitousContainerName</key>
<string>MyApp</string>
</dict>
</dict>
</dict>
Important! I then changed the above NSUbiquitousContainerSupportedFolderLevels string value from Any to One
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>One</string>
Next, and last, I had to change CFBundleVersion to a higher version. I also bumped the CFBundleShortVersionString to a new version as well.
Built and ran and after that, the folder with my applications icon appeared properly in iCloud Drive! Hope this helps!
When you edited the Info.plist, maybe you forgot to bump up the bundle version number? This is a requirement as per WWDC session #234.
The catch is to call [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; (or with another container identifier if it's not the default one) at least once (not per launch, but presumably per version, or when changing one of the respective PLIST entries) in order to initialize the directory. I reckon this step needs to be combined with an increase of the bundle version number, as suggested in roop's answer.
I notice my question may have been confusing in that respect, as I mentioned being able to look into the documents directory* programmatically using the API in question. However, I removed that code from the app later, maybe before getting the rest of the setup right. I'm not going to write into the documents directory directly, only through the Document Picker. Therefore, there hasn't been any need to get the URL.
If you just need a Document Picker to read/store files from/in iCloud Drive or other apps' document directories, there's no need to call URLForUbiquityContainerIdentifier:. Only if you want your app to have its own ubiquity container (and potentially expose it in iCloud Drive and the Document Picker), the steps mentioned in the original post and the call to URLForUbiquityContainerIdentifier: are necessary.
*When mentioning the documents directory, I'm always referring to the one in the ubiquity container, not the local one.
It seems, changing the CFBundleVersion will let it work.
I think you can try it. I got this from Apple Developer Forums.
Hope this work for you.
After dorking around with this all morning, reading all the posts, making all the changes, the key thing that finally worked for me was, as Yet Another Code Maker stated, changing the bundle ID. I think once it has created a container for a bundle, you can't go back and change the visibility of it to have it appear in Finder. I had tried all the different info.plist values but nothing worked until I changed to a new bundle name and forced the system to create a new one. By the way, I didn't see this noted anywhere but the bundle name, the NSUbiquitousContainer name and the NSUbiquitousContainerName can all be different - which is what I did in my case. After spending so much time on this, I figured I would go ahead and put a simple sample app on GitHub in case anyone is still having problems debugging their iCloud folder appearing in Finder - you can find it here. All the required steps are outlined in the README.
In my case (Xcode 7 and iOS 9), the only thing which made it works, after multiple tries, was just use a new bundle identifier (you don't have to change the cloud container identifier, just be sure to select the container you want to use in the Apple Developer Member Centre and to specify in Xcode a custom container instead of the default).
In fact, that means the first time you run your application, the NSUbiquitousContainers section of the info.plist has to be set up. If you set it afterwards as a second step, it won't work...
Well, it's not documented anywhere but try to add Documents folder in the container and store your files there.
Found this hint in replies in this Apple Developer Forum thread.
The .plist entry on this documentation page has an additional entry:
<key>NSUbiquitousContainerName</key>
<string>MyApp</string>
Maybe the missing name is prohibiting it from showing up.
Couldn't find any documentation, but trial and error, I found that:
[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:#"com.apple.CloudDocs"];
Gives you the base URL for the drive as seen in the picker. Using this base URL I was able to save files in my app and see it on the iCloud drive within Yosemite.
Edit 14.8.14
I tried your plist settings:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.net.redacted.docTest</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
In my little throwaway test app "docTest" it does indeed expose the empty Documents directory in Yosemite and in the document picker.
Screenshot http://spring-appstudio.com/picker-view.png
Just wanted to emphasize one of the OP's discoveries that fixed it for me:
I've also recognised that you need to hard-code the bundle identifier (i.e., don't use iCloud.$(CFBundleIdentifier)) as only the PLIST's values seem to be resolved at build time, but not the keys.
You need to hard code the bundle id. Also update the version.
(I didn't notice this in the question until I went through all the answers).
Same problem occurred to my OSX app.
It is seemed that NSUbiquitousContainers setting works only in the creation time of the iCloud containers. So I tried with new Apple ID(for preparing clean iCloud environment), it becomes to work.
I know this is an old thread but just in case someone runs into the same issue: Only way for me to get my Container folder to be visible in iCloud Drive (after trying all the above suggestions) was to have my app create a temporary file in the Documents folder. As soon as I did that the Container Folder (and the file I created) showed up on my Mac. If this is really the case that I have to create a file to make this folder visible then this would be a bit annoying because my app is a read only app (only reads files added by the user to the Container Folder). The Container Folder needs to be visible as soon as the app is launched for the first time. I guess I will have to detect the first launch.