swiftcloudkitckoperationoperationqueue

How to queue CKOperations for different CKDatabase


I create 2 operations, let's say CKModifySubscriptionsOperation. One is for private, another for shared database. I could queue them by adding to the OperationQueue, each next would start after previous completion block.

let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
// ...
operationQueue.addOperation(operation)

// Queued great but all subscriptions are created in private database

But I need to do some action (fetching, modifying etc) from different databases, but still need to queue them. Here is how I add operation to the database. How to put them to the single queue but still let them go to needed database each?

container.privateCloudDatabase.add(operation)
container.sharedCloudDatabase.add(operation)

// Put subscriptions to correct databases but no queue

Solution

  • I've solved the goal by creating a controlled custom operation.

    Now we can queue cloud database specific operations just like that

    let privateSubscriptionOperation = SubscriptionOperation(type: .private)
    let sharedSubscriptionOperation = SubscriptionOperation(type: .shared)
    
    operationQueue.addOperation(privateSubscriptionOperation)
    operationQueue.addOperation(sharedSubscriptionOperation)
    

    First of all, parent class

    class CKDatabaseControlledOperation: Operation {
        
        let databaseType: DatabaseType
        let database: CKDatabase
        
        private var _finished = false
        private var _executing = false
        
        init(type: DatabaseType) {
            databaseType = type
            switch type {
            case .private:
                database = CKContainer.default().privateCloudDatabase
            case .shared:
                database = CKContainer.default().sharedCloudDatabase
            }
        }
        
        override var isExecuting: Bool {
            get {
                return !_executing
            }
            set {
                willChangeValue(forKey: "isExecuting") // This must match the overriden variable
                _executing = newValue
                didChangeValue(forKey: "isExecuting") // This must match the overriden variable
            }
        }
        
        override var isFinished: Bool {
            get {
                return _finished
            }
            set {
                willChangeValue(forKey: "isFinished") // This must match the overriden variable
                _finished = newValue
                didChangeValue(forKey: "isFinished") // This must match the overriden variable
            }
        }
        
        func stopOperation() {
            isFinished = true
            isExecuting = false
        }
        
        func startOperation() {
            isFinished = false
            isExecuting = true
        }
    
        enum DatabaseType: String {
            case `private` = "private-changes"
            case shared = "shared-changes"
        }
    }
    

    And then we can create any database operation (subscription in this example but will work with any)

    class SubscriptionOperation: CKDatabaseControlledOperation {
        
        override func main() {
            startOperation() //Operation starts
            
            let subscription = CKDatabaseSubscription(subscriptionID: databaseType.rawValue)
            //...set any needed stuff like NotificationInfo
            
            let operation = CKModifySubscriptionsOperation(subscriptionsToSave: [subscription], subscriptionIDsToDelete: [])
    
            operation.modifySubscriptionsCompletionBlock = { [unowned self] subscriptions, subscriptionIDs, error in
                //Handle errors
                self.stopOperation() //Operation ends
            }
            
            database.add(operation)
        }
        
    }