6 Replies
      Latest reply on Aug 15, 2019 10:07 AM by alienspaces
      ggruen Level 1 Level 1 (0 points)

        I'm trying to re-initialize a CloudKit container using the instructions in the docs. This worked in XCode 11 beta 3 or 4 (when I first initialized it), possibly running on Mac OS 10.14.x.  I'm now running on Mac OS 10.15 beta 4 (19A512f), running Xcode 11 beta 4, and get "Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'" for the indicated line below (line 4):

         

                // Initialize the CloudKit schema. This should be toggled off or removed when shipping the app.
                let containerId = "iCloud.com."
                let options = NSPersistentCloudKitContainerOptions(containerIdentifier: containerId)
        -->     options.shouldInitializeSchema = true // toggle to false when done, or true to initialize iCloud schema
                description.cloudKitContainerOptions = options

         

        I've tried running clean build, running xcode-select -s /Applications/Xcode-beta.app, and building to a different iOS device and consistently get this error. The most up-to-date documentation I can find for NSPersistentCloudKitContainerOptions shows that shouldInitializeSchema is a Boolean variable.

         

        Thought I'd post here before submitting a bug in Feedback.

         

        Thanks!

        • Re: Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'
          Yacectr Level 1 Level 1 (10 points)

          Same issue.

          MacOS 10.14.5

          Xcode 11.0 beta 4

           

                  let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)
                  options.shouldInitializeSchema = true // error

           

          I got an error:

          Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'

           

          NSPersistentCloudKitContainerOptions class declaration

          @available(iOS 13.0, *)
          open class NSPersistentCloudKitContainerOptions : NSObject {
          
              /**
               The container identifier of the CKContainer to use with a given instance of NSPersistentStoreDescription
               */
              open var containerIdentifier: String { get }
          
              
              public init(containerIdentifier: String)
          }
          
          • Re: Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'
            tschmitz Level 1 Level 1 (0 points)

            This doesn't seem to be documented anywhere, but it looks like in iOS 13 beta 4 there's a new `NSPersistentCloudKitContainer.initializeCloudKitSchemaWithOptions` method. Take a look at the API diffs here: http://codeworkshop.net/objc-diff/sdkdiffs/ios/13.0b4/CoreData.html

              • Re: Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'
                Yacectr Level 1 Level 1 (10 points)

                Thanks. I found documentation in Xcode Core Data framework files

                    /*
                     This method creates a set of representative CKRecord instances for all stores in the container
                     that use Core Data with CloudKit and uploads them to CloudKit. These records are "fully saturated"
                     in that they have a representative value set for every field Core Data might serialize for the given
                     managed object model.
                     
                     After records are successfully uploaded the schema will be visible in the CloudKit dashboard and
                     the representative records will be deleted.
                     
                     This method returns YES if these operations succeed, or NO and the underlying error if they fail.
                     
                     Note: This method also validates the managed object model in use for a store, so a validation error
                     may be returned if the model is not valid for use with CloudKit.
                     */
                    open func initializeCloudKitSchema(options: NSPersistentCloudKitContainerSchemaInitializationOptions = []) throws
                
                  • Re: Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'
                    ggruen Level 1 Level 1 (0 points)

                    For anyone else finding this:

                     

                    1. You need to call container.initializeCloudKitSchema() after container.loadPersistentStores.
                    2. You don't really need to call container.initializeCloudKitSchema() at all, as CloudKit will create your schema on the fly. initializeCloudKitSchema just sends some fake data for your model, so it's handy to see if anything's going to break that Xcode didn't warn you about.

                     

                    Here's some code that works in Xcode 11 beta 5 to initialize an NSPersistentCloudKitContainer using a local store and a cloud store (see the Manage Multiple Stores section of the Setting Up Core Data doc).

                     

                        // MARK: - Core Data stack
                    
                        /// Convenience method so you can do DataManager.shared.context instead of DataManager.shared.persistentContainer.viewContext.
                        lazy var context = self.persistentContainer.viewContext
                    
                        /// persistentContainer.viewContext
                        lazy var persistentContainer: NSPersistentContainer = {
                            /*
                             The persistent container for the application. This implementation
                             creates and returns a container, having loaded the store for the
                             application to it. This property is optional since there are legitimate
                             error conditions that could cause the creation of the store to fail.
                             */
                            let container = NSPersistentCloudKitContainer(name: "Model")
                    
                            // Put our stores into Application Support
                            var storePath: URL
                            do {
                                storePath = try FileManager.default.url(for: .applicationSupportDirectory,
                                                                        in: .userDomainMask,
                                                                        appropriateFor: nil,
                                                                        create: true)
                            } catch {
                                fatalError("Unable to get path to Application Support directory")
                            }
                    
                            // Create a store description for a local store
                            let localStoreLocation = storePath.appendingPathComponent("local.store")
                            let localStoreDescription =
                                NSPersistentStoreDescription(url: localStoreLocation)
                            localStoreDescription.configuration = "Local"
                    
                            // Create a store descpription for a CloudKit-backed local store
                            let cloudStoreLocation = storePath.appendingPathComponent("cloud.store")
                            let cloudStoreDescription =
                                NSPersistentStoreDescription(url: cloudStoreLocation)
                            cloudStoreDescription.configuration = "Cloud"
                    
                    
                            // Set the container options on the cloud store
                            cloudStoreDescription.cloudKitContainerOptions =
                                NSPersistentCloudKitContainerOptions(
                                    containerIdentifier: "iCloud.com.mydomain.mycontainer")
                    
                            // Update the container's list of store descriptions
                            container.persistentStoreDescriptions = [
                                cloudStoreDescription,
                                localStoreDescription
                            ]
                    
                            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                                if let error = error as NSError? {
                                    // Replace this implementation with code to handle the error appropriately.
                                    // fatalError() causes the application to generate a crash log and terminate.
                                    // You should not use this function in a shipping application, although it may be useful during
                                    // development.
                                    /*
                                     Typical reasons for an error here include:
                                     * The parent directory does not exist, cannot be created, or disallows writing.
                                     * The persistent store is not accessible, due to permissions or data protection when the device
                                     *     is locked.
                                     * The device is out of space.
                                     * The store could not be migrated to the current model version.
                                     Check the error message to determine what the actual problem was.
                                     */
                                    fatalError("Unresolved error \(error), \(error.userInfo) for \(storeDescription)")
                                }
                            })
                    
                            do {
                                  // Uncomment to do a dry run and print the CK records it'll make
                    //            try container.initializeCloudKitSchema(options: [.dryRun, .printSchema])
                                // Uncomment to initialize your schema
                                try container.initializeCloudKitSchema()
                            } catch {
                                print("Unable to initialize CloudKit schema: \(error.localizedDescription)")
                            }
                    
                            return container
                        }()
                    
                    
                      • Re: Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'
                        .Nick Apple Staff Apple Staff (40 points)

                        You don't really need to call container.initializeCloudKitSchema() at all, as CloudKit will create your schema on the fly. initializeCloudKitSchema just sends some fake data for your model, so it's handy to see if anything's going to break that Xcode didn't warn you about.

                         

                        This is wrong. Please refer to the WWDC talk this year.

                         

                        NSPersistentCloudKitContainer materializes a number of specific values in your record that may not be initialized properly simply by using your app. Specifically for variable length fields like data blobs and strings you are unlikely to trivially violate our asset threshold during normal use.

                          • Re: Value of type 'NSPersistentCloudKitContainerOptions' has no member 'shouldInitializeSchema'
                            alienspaces Level 1 Level 1 (20 points)

                            Thank you for addressing this, I was going to query it because it went against everything I'd read. Do you know if it's possible to capture the output of the schema initialization process when using the option .printSchema? I'd quite like to save it to a file and also search for the success text so my integration test can respond accordingly. Do you think it would work if I ran it in a seperate Process and just redirected stdout? Wait that won't work because Process is a macOS only class and even so, you can't set iCloud Container entitlements on a command line application. Which I wish you could...