Initial commit to answer that dude on Reddit
This commit is contained in:
commit
8e5fe962c1
11 changed files with 910 additions and 0 deletions
127
src/CloudKitHandler.swift
Normal file
127
src/CloudKitHandler.swift
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// CloudKitHandler.swift
|
||||
// Lychee
|
||||
//
|
||||
// Created by Ryan McGrath on 5/3/18.
|
||||
// Copyright © 2018 Ryan McGrath. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
* CloudKitHandler
|
||||
*
|
||||
* Handles communicating with CloudKit and the associated APIs. The CloudKit APIs can be a bit
|
||||
* unwieldy and verbose, so this library aims to work around those limitations and streamline
|
||||
* everything.
|
||||
*/
|
||||
class CloudKitHandler {
|
||||
static let shared = CloudKitHandler()
|
||||
|
||||
var isConfiguring: Bool = true
|
||||
var isSyncing: Bool = false
|
||||
let errorHandler: CloudKitErrorHandler = CloudKitErrorHandler()
|
||||
|
||||
var accountStatus: CKAccountStatus = .noAccount
|
||||
var container: CKContainer = CKContainer(identifier: RYMC_CLOUDKIT_CONTAINER_ID)
|
||||
var userRecordID: String?
|
||||
|
||||
var pendingInvites: [URL] = []
|
||||
var pendingMetadatas: [CKShareMetadata] = []
|
||||
private var operationQueue: OperationQueue = OperationQueue()
|
||||
private var queuedOperations: [CKOperation] = []
|
||||
|
||||
// Internal
|
||||
var backingPreviousChangeToken: CKServerChangeToken?
|
||||
var backingRootRecord: CKRecord?
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(onAccountChanged), name: NSNotification.Name.CKAccountChanged, object: nil)
|
||||
}
|
||||
|
||||
@objc func onAccountChanged() {
|
||||
isConfiguring = true
|
||||
isSyncing = false
|
||||
determineAccountStatus()
|
||||
}
|
||||
|
||||
public func determineAccountStatus() {
|
||||
container.accountStatus { [unowned self] (status: CKAccountStatus, error: Error?) in
|
||||
if(error != nil) {
|
||||
print("Error retrieving account status \(String(describing: error))")
|
||||
self.finishConfiguration(successful: false)
|
||||
return
|
||||
}
|
||||
|
||||
if(status != CKAccountStatus.available) {
|
||||
// error
|
||||
self.finishConfiguration(successful: false)
|
||||
return
|
||||
}
|
||||
|
||||
self.container.fetchUserRecordID(completionHandler: { (recordID: CKRecordID?, err: Error?) in
|
||||
guard let recordID = recordID else {
|
||||
self.finishConfiguration(successful: false)
|
||||
return
|
||||
}
|
||||
|
||||
print("User record ID: \(String(describing: recordID.recordName))")
|
||||
self.userRecordID = recordID.recordName
|
||||
self.accountStatus = status
|
||||
self.configure()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func configure() {
|
||||
let createZone: CKModifyRecordZonesOperation = createPrivateZoneOperation()
|
||||
let fetchZones: CKFetchRecordZonesOperation = fetchPrivateZonesOperation(cancelIfExistsAlready: [createZone])
|
||||
createZone.addDependency(fetchZones)
|
||||
|
||||
let createPrivateSub: CKModifySubscriptionsOperation = createSubscriptionOperation(.private)
|
||||
let fetchPrivateSub: CKFetchSubscriptionsOperation = fetchSubscriptionsOperation(.private, cancelIfExistsAlready: [createPrivateSub])
|
||||
fetchPrivateSub.addDependency(createZone)
|
||||
createPrivateSub.addDependency(fetchPrivateSub)
|
||||
|
||||
let createSharedSub: CKModifySubscriptionsOperation = createSubscriptionOperation(.shared)
|
||||
let fetchSharedSub: CKFetchSubscriptionsOperation = fetchSubscriptionsOperation(.shared, cancelIfExistsAlready: [createSharedSub])
|
||||
fetchSharedSub.addDependency(createZone)
|
||||
createSharedSub.addDependency(fetchSharedSub)
|
||||
|
||||
let createRootRecord: CKModifyRecordsOperation = createRootRecordOperation()
|
||||
let fetchRootRecord: CKFetchRecordsOperation = fetchRootRecordOperation(cancelIfExistsAlready: [createRootRecord])
|
||||
fetchRootRecord.addDependency(createZone)
|
||||
createRootRecord.addDependency(fetchRootRecord)
|
||||
|
||||
let complete: BlockOperation = BlockOperation(block: { [unowned self] in
|
||||
self.finishConfiguration()
|
||||
})
|
||||
complete.addDependency(createRootRecord)
|
||||
|
||||
operationQueue.addOperations([
|
||||
fetchZones, createZone,
|
||||
fetchPrivateSub, createPrivateSub,
|
||||
fetchSharedSub, createSharedSub,
|
||||
fetchRootRecord, createRootRecord,
|
||||
complete
|
||||
], waitUntilFinished: false)
|
||||
}
|
||||
|
||||
func finishConfiguration(successful: Bool = true) {
|
||||
isConfiguring = false
|
||||
operationQueue.addOperations(queuedOperations, waitUntilFinished: false)
|
||||
queuedOperations.removeAll()
|
||||
}
|
||||
|
||||
func addOperations(_ operations: [CKOperation]) {
|
||||
if(isConfiguring) {
|
||||
queuedOperations.append(contentsOf: operations)
|
||||
} else {
|
||||
operationQueue.addOperations(operations, waitUntilFinished: false)
|
||||
}
|
||||
}
|
||||
|
||||
func stopAllOperations() {
|
||||
for operation: Operation in operationQueue.operations.reversed() {
|
||||
operation.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue