0
votes

As the title suggests, I want to implement iCloud Drive support on an existing app, in which the ubiquity container is prefixed by the old style, $(TeamIdentifierPrefix). For the life of me I cannot get this to work.

Initially I tried Apple's own steps as detailed here: https://developer.apple.com/library/content/documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesigningForDocumentsIniCloud.html#//apple_ref/doc/uid/TP40012094-CH2-SW20

This did not work unfortunately. (I have remembered to increment the Bundle Version each build)

Following the steps available on this post: iCloud Drive Folder, I was able to get my app to appear in iCloud Drive, however my existing documents were inaccessible as they reside at e.g. X123F4563T8.com.example.app and not the newly created iCloud.com.example.app.

I have tried countless variations of differing identifier configurations, to try to get iCloud Drive to point to the old ubiquity container, but to no avail.

My entitlements file looks like the following, (with my app's own identifiers):

<key>com.apple.developer.icloud-container-identifiers</key>
<array>
    <string>iCloud.com.example.AppName</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
    <string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
    <string>iCloud.com.example.AppName</string>
</array>
<key>keychain-access-groups</key>
<array>
    <string>$(AppIdentifierPrefix)com.example.AppName</string>
</array>

The first key seems to be what decides which ubiquity directory to use.

This was previously set to:

<key>com.apple.developer.icloud-container-identifiers</key>
<array>
    <string>$(TeamIdentifierPrefix)com.example.AppName</string>
</array>

I have narrowed down my search to discover that this is the value for this key seems to be the deciding factor as to whether (old style) iCloud Documents are used

 $(TeamIdentifierPrefix)com.example.AppName

Or iCloud Drive,

 iCloud.com.example.AppName

For completeness sake, the following is included in my Info.plist:

<key>iCloud.com.example.AppName</key>
<dict>
    <key>NSUbiquitousContainerIsDocumentScopePublic</key>
    <true/>
    <key>NSUbiquitousContainerName</key>
    <string>App Name For Drive</string>
    <key>NSUbiquitousContainerSupportedFolderLevels</key>
    <string>Any</string>
</dict>

I also have not overlooked:

<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
    <string>iCloud.com.example.AppName</string>
</array>

However, the value of this seems to have no effect on the outcome.

1

1 Answers

0
votes

To get this working, the following entry was added to my Info.plist:

<key>NSUbiquitousContainers</key>
<dict>
    <key>iCloud.com.Example.AppName</key>
    <dict>
        <key>NSUbiquitousContainerIsDocumentScopePublic</key>
        <true/>
        <key>NSUbiquitousContainerName</key>
        <string>Example App Name</string>
        <key>NSUbiquitousContainerSupportedFolderLevels</key>
        <string>Any</string>
    </dict>
</dict>

My AppName.entitlements file contains amongst it:

<key>com.apple.developer.icloud-container-identifiers</key>
<array>
    <string>$(TeamIdentifierPrefix)com.example.AppName</string>
    <string>iCloud.com.example.AppName</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
    <string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
    <string>$(TeamIdentifierPrefix)com.example.AppName</string>
</array>

Now that we are all set up with the ability to access both containers, I added the 2 iCloud ubiquity addresses I wanted to be able to access (old style iCloud documents and newer iCloud Drive), to my Info.plist:

<key>UbiquitousContainers</key>
<array>
    <string>$(TeamIdentifierPrefix)com.example.AppName</string>
    <string>iCloud.com.example.AppName</string>
</array>

I then added the following into a constants file for easy access:

struct UbiquityContainers {
    private static let ubiquityContainers : [String] = Bundle.main.object(forInfoDictionaryKey: "UbiquitousContainers") as! [String]
    static var iCloudDocuments : String {
        get {
            return ubiquityContainers[0]
        }
    }
    static var iCloudDrive : String {
        get {
            return ubiquityContainers[1]
        }
    }
}

I elected to allow the user to switch between the 2 containers by means of a TabBar. The old iCloud Documents directory holds documents from an old version of my app which need to be converted to the new document format prior to opening so it makes sense in my case to have separate views for each file version.

var ubiquityContainer : String = UbiquityContainers.iCloudDrive
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
    if(self.documentsInCloud) {
        if(item == self.iCloudDriveTabBarItem) {
            self.ubiquityContainer = UbiquityContainers.iCloudDrive
        } else if(item == self.iCloudDocumentsTabBarItem) {
            self.ubiquityContainer = UbiquityContainers.iCloudDocuments
        }
        self.startCloudQuery()
    }
}

I hope this helps anyone out in the future who may be lacking follicles due to them being scattered on the ground. :)