Search code examples
xcodecloudkitxcode-ui-testingcode-signing-entitlements

iOS UI test target is missing entitlement icloud-services


I have an app that uses iCloud. This works fine.

I now tried to write a UI test that checks first if the iCloud account is accessible, i.e. that the user is logged in to iCloud and iCloud is enabled for the app.

To do so, I tried to call (CloudKit is imported)

let container = CKContainer(identifier: kICloudContainerID)
container.accountStatus() { (status, error) in
  // …
}  

However as soon as I try to access CKContainer (1st line), I get the error

The application is missing required entitlement com.apple.developer.icloud-services
(null)

Of corse, my app has iCloud entitlements set, but such entitlements cannot be set for a test target.

I have read this post. The suggested solutions (disabling breakpoints, toggle iCloud capabilities, clearing out the code signing entitlements) did not work.

Any idea how to solve this problem?

EDIT:

For testing, I created a new Single View App with UI Tests included.
In the app target, I enabled only CloudKit:
enter image description here

In the app delegate, I imported CloudKit, and accessed CKContainer:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let container = CKContainer(identifier: "iCloud.com.zeh4soft.EntitlementUITest")
    return true
}  

This works.

In the UI Tests, I imported also CloudKit, and added the same line:

func testExample() {
  let container = CKContainer(identifier: "iCloud.com.zeh4soft.EntitlementUITest")
}  

This does not work, but gives the same error:
enter image description here


Solution

  • I contacted Apple, and they found the solution for a device, but not for a simulator.
    I did the following steps:

    Setup in Xcode a completely new Single View App project and include UI tests. Call it „EntitlementUITest“. This will create in the project navigator a folder called „EntitlementUITestUITests“.

    Select in this folder „EntitlementUITestUITests.swift“.
    There is a function testExample(). Add in this function the line

        let _   = CKContainer(identifier: "iCloud.com.yourid.EntitlementUITest")
    

    yourid is e.g. your company name.
    This is the statement that causes the run time error if the project has not been prepared as laid out below.

    Select in the project navigator the project.
    This will display an additional pane with the project and the targets.
    There are 2 targets: The main target „EntitlementUITest“ and the UI test target „EntitlementUITestUITests“.
    Select the main target „EntitlementUITest“.
    In the top bar of the window right of the pane, select „Capabilities“. You will see that you can select iCloud usage.

    Now select the test target „EntitlementUITestUITests“. The top bar now does not have „Capabilities“. This means, the setup that allows your UI tests to use iCloud is much more complicated, and has to be done manually.

    First, one needs an entitlements file that specifies the capabilities that the app wants to use. I manually created the following file (actually, I copied the entitlements file of the main target and edited it):

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>aps-environment</key>
        <string>development</string>
        <key>com.apple.developer.icloud-container-identifiers</key>
        <array>
            <string>iCloud.com.yourid.EntitlementUITest</string>
        </array>
        <key>com.apple.developer.icloud-services</key>
        <array>
            <string>CloudKit</string>
        </array>
    </dict>
    </plist>  
    

    I added this file to the project.

    In order to allow the UI test target to use iCloud, one needs now a few action in the development center.

    In your web browser, open the development center, and open there „Certificates, Identifiers & Profiles“.

    The test target is treated as a new app. Thus, it needs a separate app ID.
    Open App IDs, and add a new ID „EntitlementUITestUITests“.
    It should be an „Explicit App ID“ that uses the bundle ID of your test target, here

    com.yourid.EntitlementUITest.  
    

    Enable iCloud and Push notifications.
    I am not sure if Push Notifications are required, but Apple told me to do so. Probably, you can omit it in the entitlements file and here. If it is present, please ensure that a valid certificate is specified for the push notification service.
    When you enable iCloud, ensure that iCloud.com.yourid.EntitlementUITest is the only one selected among the assigned iCloud containers.

    Create next a new development provisioning profile (one that uses your iOS developer certificate) and associate with com.yourid.EntitlementUITestUITests.
    Download the profile.

    Back in Xcode, select again target „EntitlementUITestUITests“ with tab General.
    In the section Signing, uncheck Automatically manage signing, since this does not work.
    In Signing (Debug), select in the drop down menu Provisioning profile to import your just downloaded profile. This will also be selected in the Signing (Release) section.

    Now the project should build for testing without errors.

    Run the testExample. It should run now without run time error.

    Conclusion:
    It is possible to access a CKContainer in an UI test, but this is incredibly complicated.
    Why is this process not as easy as with the main targets?
    Maybe too less developers UI test their iCloud apps, otherwise more enhancement requests to Xcode would have been written…

    One more thing: If everything was done right, the test runs only on a device, not on a simulator. Apple said, running this test on a simulator gives unpredictable results. For me, it was predictable - it fails always.