经过多天的尝试但没有成功后,我在这里发布这个问题,希望有人可以帮助我解决这个看起来相当简单的问题。我正在为Swift项目而不是SwiftUI执行此操作。
我的要求非常简单,我很困惑为什么苹果的文档或其他任何地方都没有明确的解决方案。我希望在我的 iOS 应用程序上有一个从 CloudKit 同步到应用程序核心数据的公共和私有数据库,反之亦然。
我已经看过 WWDC 视频和示例代码好几次了。如果他们在工作,我就不会在这里发布我的问题。
最新 WWDC2022 视频中的人正在展示 WWDC2019 视频中的代码。而且您下载的代码与他在视频中显示的代码不同。对于这样一个简单的任务来说,这个下载的代码不仅过于复杂和混乱,而且它也不处理公共数据库同步和/或订阅。苹果的教程工作太糟糕了。
经过一番努力,我也设法找到了 WWDC2019 代码,但它无法在较新的 Xcode 上编译。我正在使用 Xcode 13。我将其修复为可在 Xcode 上运行。但最终,它并没有按预期与 CloudKit 同步。
我查阅了无数的例子,但它们都已经过时了。我还没有看到一个显示最新 CloudKit 屏幕的示例。大多数示例仅讨论私有数据库同步,这是一个相当简单且直接的过程。几乎没有任何示例谈论设置订阅,更不用说公共数据库了。通过订阅设置公共数据库的唯一示例位于 Hacking With Swift 网站,但它没有讨论将 Core Data 与 CloudKit 同步,而是直接从 CloudKit 中保存和读取。
Apple 自己的示例也仅适用于私有数据库同步。
经过许多天的挣扎和挫折,我来到这里。我从各种示例中精心挑选了工作代码片段。但最终我没能让事情顺利进行。
我在下面发布我的代码。当在设备上运行时,它会在 CloudKit 上创建记录。它也应该创建订阅,但并不是每次都创建它们。即使创建了订阅,它们也不会被可靠地触发。有时他们会被解雇,有时则不会。这是CloudKit的开发环境的问题吗?我尝试过设置多个 CloudKit 容器,但这个问题仍然存在。
此外,模拟器和真实设备上的行为并不相同。在 Simulator 上,我知道您不会收到推送通知,但即使启动应用程序也不会每次都从 CloudKit 下载记录。因此,有时它会按预期工作,而有时则根本不起作用。尽管日志不断显示它们与 CloudKit 后端正在进行某种通信。
无论如何,在 CloudKit 上删除记录并不会在设备上删除它。
运行 WWDC2022 视频提供的示例也非常不稳定。它应该创建帖子项目。它确实如此,但是无论我设置私有数据库订阅还是公共数据库订阅,同步工作都非常不可靠。删除了还是不行。通知也不起作用,尽管我在我的应用程序中设置得很好appdelegate
那么,有没有可行的解决方案可供参考?任何帮助将不胜感激。
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "PublicDB")
let store = …Run Code Online (Sandbox Code Playgroud) 我正在使用 PhotosPicker 让用户选择照片。如何检索所选照片的 URL?
我尝试打印 imageSelection.itemIdentifier 并得到可选(“03966B05-1F51-4A20-801C-B617A2BC14DB/L0/001”),我不知道这是否与网址路径有关。
这是我使用 PhotosPicker 的类,使用WWDC2022的示例代码。
class CreateViewModel: ObservableObject {
@Published var post: Post = Post() // Record being added to CloudKit
enum ImageState {
case empty, loading(Progress), success(Image), failure(Error)
}
@Published private(set) var imageState: ImageState = .empty
@Published var imageSelection: PhotosPickerItem? {
didSet {
if let imageSelection {
let progress = loadTransferable(from: imageSelection)
imageState = .loading(progress)
} else {
imageState = .empty
}
}
}
// Load asset data using transferable
private func …Run Code Online (Sandbox Code Playgroud) 不幸的是,使用 CloudKit 时,该.unique属性不可用于 SwiftData 模型属性。是否有最佳实践为我自己的模型设置这样的约束?
对于本地数据库,在创建和插入新模型之前获取(和过滤)模型是相当简单的。
但是如何防止来自其他设备的模型重复 - 即,在从 CloudKit 导入时将重复的模型与本地现有的模型合并,然后将它们插入到本地 SwiftData DB 中?
changesOperation.fetchRecordChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSData *clientChangeTokenData, NSError *operationError){
//encode and save token
NSData *encodedServerChangeToken = [NSKeyedArchiver archivedDataWithRootObject:serverChangeToken];
[[NSUserDefaults standardUserDefaults] setObject:encodedServerChangeToken forKey:fetchToken];
[[NSUserDefaults standardUserDefaults] synchronize];
//handle more - **this causes a retain cycle**
if(changesOperation.moreComing){
}
};
Run Code Online (Sandbox Code Playgroud)
嗨,您只是想知道在fetchRecordChangesCompletionBlock中,文档说:
如果服务器无法使用此操作对象传递所有更改的结果,则在执行fetchRecordChangesCompletionBlock属性中的块之前,将此属性设置为YES。要获取其余更改,请使用服务器返回的更改令牌创建一个新的CKFetchRecordChangesOperation对象。
在上面的代码中,这将导致一个保留周期,因此应如何处理?在重新创建操作时,是否可以使用已创建的相同完成块?
我设法在Apples CloudKit中保存,更改和删除记录.我甚至收到订阅的通知,我不知道的是,我如何列出当前用户的所有订阅.
到目前为止,这是我的代码:
let operation = CKFetchSubscriptionsOperation()
operation.fetchSubscriptionCompletionBlock = { (d, e) -> Void in
println("got subscription")
if e != nil {
println("Error")
dump(e)
}
dump(d)
}
publicDatabase.addOperation(operation)
Run Code Online (Sandbox Code Playgroud)
我得到的是:
got subscription
Error
- <CKError 0x14db0ed0: "Invalid Arguments" (12)> #0
- 0 key/value pairs
Run Code Online (Sandbox Code Playgroud)
什么是无效参数?我如何获得所有已保存订阅的列表?
当我从tableView中删除一行时,我遇到了崩溃.不知道发生了什么事.这是我的代码:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let items = days[indexPath.row]
self.removeItems(items, indexPath: indexPath)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return days.count
}
Run Code Online (Sandbox Code Playgroud)
为removeItems
func removeItems(items: [CKRecord], indexPath: NSIndexPath) {
for item in items {
db.deleteRecordWithID(item.recordID) { (record, error) -> Void in
if error != nil {
println(error.localizedDescription)
}
dispatch_async(dispatch_get_main_queue()) { () -> Void in
if let index = find(exercises, item) {
exercises.removeAtIndex(index)
}
}
}
}
days.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
Run Code Online (Sandbox Code Playgroud) <CKError 0x14d8cb70: "Partial Failure" (2/1011); "Failed to modify some records"; partial errors: {
B5DEF0B5-F064-4B27-9C89-BE75C9134297:(_defaultZone:__defaultOwner__) = <CKError 0x14d83b70: "Server Record Changed" (14/2037); "Error saving record <CKRecordID: 0x15748cd0; B5DEF0B5-F064-4B27-9C89-BE75C9134297:(_defaultZone:__defaultOwner__)> to server: Protection data didn't match">
}>
Run Code Online (Sandbox Code Playgroud)
当我尝试保存CKRecords 时,我收到此错误CloudKit.任何的想法?
我是否必须像在便捷API中那样获取第一条记录?
我正在使用CKModifyRecordsOperation方法来更新更多记录.
比方说,我有一个CKRecord的recordType帖子.帖子保留一些值,如标题和描述.当帖子显示在应用程序中时,它会附有编写它的用户的姓名和个人资料图片(让我们称之为Writer).我的问题是 - 最好将一个存储CKReference到Writer的配置文件中(配置文件是另一种包含Writer详细信息的记录),或者在写入时直接将Writer的详细信息添加到Post中会不会更好?
从数据库模式的角度来看,第一个选项非常有意义,但从性能角度来看,它似乎非常糟糕.在这个系统上有成千上万的用户,提取的数量和加载它们的时间似乎都是不合理的.
第一部分涉及加载所有帖子.
func loadPosts() {
// ...Setup the query
publicData.performQuery(query, inZoneWithID: nil) { (results: [CKRecord]?, error: NSError?) in
if let posts = results {
self.loadProfiles(posts)
}
}
}
Run Code Online (Sandbox Code Playgroud)
完成了一个查询,现在我们调用了 loadProfiles
func loadProfiles(posts: [CKRecord]) {
// Get the reference IDs out of the Posts
var referenceIDs = [CKRecordID]()
for post in posts {
// Get the reference from the post
// Append the recordID to the referenceIDs array
}
// Perform the …Run Code Online (Sandbox Code Playgroud) 我正在尝试实施与CloudKit Web服务的一些基本集成,遵循Apple的身份验证请求指南.我已经按照一些帮助,这个和这个问题,就是如何正确授权的请求,并且似乎是正确的以下所有步骤,但我仍然得到401 AUTHENTICATION_FAILED苹果错误.
我定位的端点是一个POST端点,用于在给定记录名称的情况下检索记录.
我在我的代码中添加了注释,以显示我在不同阶段获得的输出,并且我使用了替代证书,因此我没有提供我的正版私钥:
def self.signature(parameters, date, image_id)
#date: 2016-08-14T14:32:20Z
#parameters: {"records":[{"recordName":"7DBC4FAD-D18C-476A-89FB-14A515098F34"}]}
encoded_parameters = Digest::SHA256.base64digest(parameters)
#encoded_parameters: 6gmJ4AvmJgkNY4SJm6ImOxZaZ07J7cih/tRXI0zkRjQ=
url_subpath = CloudKit.url_subpath
#url_subpath: /database/1/iCloud.ProjectDent.TwIM/development/public/records/lookup
message = date + ':' + encoded_parameters + ':' + url_subpath
#message: 2016-08-14T14:23:35Z:6gmJ4AvmJgkNY4SJm6ImOxZaZ07J7cih/tRXI0zkRjQ=:/database/1/iCloud.ProjectDent.TwIM/development/public/records/lookup
private_key = OpenSSL::PKey.read(File.read('altkey.pem'))
signature = private_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(message))
#signature: -? WX?xfc???????,????v?3+Xt!?$R?_Y?×*?,?3??Z-\#????h
encoded_signature = Base64.strict_encode64(signature)
#encoded_signature: MEUCIFdYlHhmrxoIY8KW1tT6yZT17bYsP8ia09WTdpEzK1h0AiEA0yRSh39fWYHDlyqJLNgzhr9aLVwj2cWtkse3aA0tGZI=
return encoded_signature
end
def self.headers(parameters, image_id)
date = Time.now.utc.iso8601
signature = self.signature(parameters, date, image_id)
headers = {
'X-Apple-CloudKit-Request-KeyID' => CloudKit.key_id, …Run Code Online (Sandbox Code Playgroud) 我已经构建了基本的CloudKit同步引擎并且正常工作,现在我正在充实我的错误处理.我想要一个全面的列表,列出了收到.partialFailure响应代码时每条记录可能出现的错误.
文档中有一个包含所有错误代码的列表,但在我的搜索中,对于我可能会在partialErrorsByItemID字典中显示哪些内容并且仅显示为错误代码(或者是他们可以同时出现,也许只发送一条记录?).
在Apple CloudKit Share代码示例中,有一个CloudKitError类来处理错误,并处理以下部分错误:
.serverRecordChanged
.zoneNotFound
.unknownItem
.batchRequestFailed
Run Code Online (Sandbox Code Playgroud)
但我不认为这是详尽无遗的,因为班级的其他人在处理不是.partialFailure的错误时并非详尽无遗.肯定.invalidArguments可能是部分失败错误?
这是我想我需要涵盖的内容:
.alreadyShared (if sharing)
.assetFileModified (if using Assets)
.assetFileNotFound (if using Assets)
.batchRequestFailed
.constraintViolation
.invalidArguments
.referenceViolation (if sharing)
.serverRecordChanged
.unknownItem
.zoneBusy?
.zoneNotFound
Run Code Online (Sandbox Code Playgroud)
最后,因为这些是作为部分错误处理的,我是否还需要处理它们作为CloudKit的错误代码响应,就像处理非部分错误代码(如.serviceUnavailable)一样?我没有使用CKDatabase便捷方法,我正在使用像CKModifyRecordsOperation这样的完整操作,如果这很重要?
提前致谢!