Ios CloudKit:获取具有特定记录类型的所有记录?
我目前已在我的应用程序中设置了CloudKit,以便我使用以下代码添加新记录Ios CloudKit:获取具有特定记录类型的所有记录?,ios,icloud,cloudkit,ckrecord,Ios,Icloud,Cloudkit,Ckrecord,我目前已在我的应用程序中设置了CloudKit,以便我使用以下代码添加新记录 CKRecordID *recordID = [[CKRecordID alloc] initWithRecordName:@"stringArray"]; CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Strings" recordID:recordID]; [record setObject:[NSArray arrayWithObjects:@
CKRecordID *recordID = [[CKRecordID alloc] initWithRecordName:@"stringArray"];
CKRecord *record = [[CKRecord alloc] initWithRecordType:@"Strings" recordID:recordID];
[record setObject:[NSArray arrayWithObjects:@"one", @"two", @"three", @"four", nil] forKey:@"stringArray"];
[_privateDatabase saveRecord:record completionHandler:nil];
但是,现在我希望能够获取相同记录类型“Strings”的所有记录,并返回编译到NSArray中的记录。我该怎么做呢?目前,我所知道的只是如何使用recordID单独获取每条记录,这很麻烦,必须有一种更简单的方法
[_privateDatabase fetchRecordWithID:recordID completionHandler:^(CKRecord *record, NSError *error) {
if (error) {
// Error handling for failed fetch from private database
}
else {
NSLog(@"ICLOUD TEST: %@", [record objectForKey:@"stringArray"]);
}
}];
啊,我明白了。使用下面的代码,我能够创建一个在数据库上运行的查询,然后在完成块中返回一个NSArray,我循环了一遍,并在NSLog中返回了保存的密钥的值
NSPredicate *predicate = [NSPredicate predicateWithValue:YES];
CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Strings" predicate:predicate];
[_privateDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
for (CKRecord *record in results) {
NSLog(@"Contents: %@", [record objectForKey:@"stringArray"]);
}
}];
以下函数将返回所请求记录类型的所有记录:
let database = CKContainer(identifier: "container_here").privateCloudDatabase
typealias RecordsErrorHandler = ([CKRecord], Swift.Error?) -> Void
func fetchRecords(forType type: String, completion: RecordsErrorHandler? = nil) {
var records = [CKRecord]()
let query = CKQuery(recordType: type, predicate: NSPredicate(value: true))
let queryOperation = CKQueryOperation(query: query)
queryOperation.zoneID = CloudAssistant.shared.zone.zoneID
queryOperation.recordFetchedBlock = { record in
records.append(record)
}
queryOperation.queryCompletionBlock = { cursor, error in
self.fetchRecords(with: cursor, error: error, records: records) { records in
completion?(records, nil)
}
}
database.add(queryOperation)
}
private func fetchRecords(with cursor: CKQueryCursor?, error: Swift.Error?, records: [CKRecord], completion: RecordsHandler?) {
var currentRecords = records
if let cursor = cursor, error == nil {
let queryOperation = CKQueryOperation(cursor: cursor)
queryOperation.recordFetchedBlock = { record in
currentRecords.append(record)
}
queryOperation.queryCompletionBlock = { cursor, error in
self.fetchRecords(with: cursor, error: error, records: currentRecords, completion: completion)
}
database.add(queryOperation)
} else {
completion?(records)
}
}
以下是Swift 3.0中的答案
func myQuery() {
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "tableName", predicate: predicate)
publicDatabase.perform(query, inZoneWith: nil) { (record, error) in
for record: CKRecord in record! {
//...
// if you want to access a certain 'field'.
let name = record.value(forKeyPath: "Name") as! String
}
}
}
Swift 4的解决方案, 显示如何获取类型为“YourTable”的所有记录,并打印
系统字段
和自定义字段
:
let query = CKQuery(recordType: "YourTable", predicate: NSPredicate(value: true))
CKContainer.default().publicCloudDatabase.perform(query, inZoneWith: nil) { (records, error) in
records?.forEach({ (record) in
// System Field from property
let recordName_fromProperty = record.recordID.recordName
print("System Field, recordName: \(recordName_fromProperty)")
// Custom Field from key path (eg: deeplink)
let deeplink = record.value(forKey: "deeplink")
print("Custom Field, deeplink: \(deeplink ?? "")")
})
}
在尝试获取所有记录并理解Cloudkit存储的结构和细节时,我发现在调试期间提供以下功能非常有用。这使用信号量来保留数据结构以便打印。也许有一种更优雅的方法可以做到这一点,但这是可行的
//
// Print a list of records in all zones in all databases
//
func printRecordsInContainers() {
let myContainer = CKContainer.default()
// Edit the following dictionary to include any known containers and possible record types
let containerRecordTypes: [CKContainer: [String]] = [ myContainer: ["MyRecordType", "OldRecordType", "MyUser", "PrivateInfo"] ]
let containers = Array(containerRecordTypes.keys)
for containerz in containers {
let databases: [CKDatabase] = [containerz.publicCloudDatabase, containerz.privateCloudDatabase, containerz.sharedCloudDatabase]
for database in databases {
var dbType = "<None>"
if database.databaseScope.rawValue == 1 { dbType = "Public" }
if database.databaseScope.rawValue == 2 { dbType = "Private" }
if database.databaseScope.rawValue == 3 { dbType = "Shared" }
//print("\(database.debugDescription)")
print("\n\n\nSwift 5
After looking through a bunch of posts and solutions on SO I have managed to come with a solution that suits my needs and should be simple enough for anyone that just wants to fetch all of their records of given type from iCloud.
Solution
The solution that uses an extension to the CKDatabase to introduce a method that handles the cursor: CKQueryOperation.Cursor
of CKQueryOperation
to continue asking iCloud for more records. In this approach I dispatch to the background queue so I can block it and wait for the operation to be finished completely, either on receiving an error or with the last batch of records. Once the semaphore unlocks the queue it proceeds with calling the main completion block with the result. Also I am taking advantage of Swift's Result
type in the completion handler.
extension CKDatabase {
func fetchAll(
recordType: String, resultsLimit: Int = 100, timeout: TimeInterval = 60,
completion: @escaping (Result<[CKRecord], Error>) -> Void
) {
DispatchQueue.global().async { [unowned self] in
let query = CKQuery(
recordType: recordType, predicate: NSPredicate(value: true)
)
let semaphore = DispatchSemaphore(value: 0)
var records = [CKRecord]()
var error: Error?
var operation = CKQueryOperation(query: query)
operation.resultsLimit = resultsLimit
operation.recordFetchedBlock = { records.append($0) }
operation.queryCompletionBlock = { (cursor, err) in
guard err == nil, let cursor = cursor else {
error = err
semaphore.signal()
return
}
let newOperation = CKQueryOperation(cursor: cursor)
newOperation.resultsLimit = operation.resultsLimit
newOperation.recordFetchedBlock = operation.recordFetchedBlock
newOperation.queryCompletionBlock = operation.queryCompletionBlock
operation = newOperation
self?.add(newOperation)
}
self?.add(operation)
_ = semaphore.wait(timeout: .now() + 60)
if let error = error {
completion(.failure(error))
} else {
completion(.success(records))
}
}
}
}
//
//打印所有数据库中所有区域的记录列表
//
func printRecordsInContainers(){
让myContainer=CKContainer.default()
//编辑以下词典以包括任何已知容器和可能的记录类型
let containerRecordTypes:[CKContainer:[String]]=[myContainer:[“MyRecordType”、“OldRecordType”、“MyUser”、“PrivateInfo”]]
let containers=Array(containerRecordTypes.keys)
用于集装箱中的集装箱Z{
let数据库:[CKDatabase]=[containerz.publicCloudDatabase,containerz.privateCloudDatabase,containerz.sharedCloudDatabase]
用于数据库中的数据库{
var dbType=“”
如果database.databaseScope.rawValue==1{dbType=“Public”}
如果database.databaseScope.rawValue==2{dbType=“Private”}
如果database.databaseScope.rawValue==3{dbType=“Shared”}
//打印(“\(database.debugDescription)”)
打印(“\n\n\nSwift 5
在浏览了一大堆文章和解决方案之后,我终于找到了一个适合我需要的解决方案,对于那些只想从iCloud获取所有给定类型记录的人来说,这个解决方案应该足够简单
解决方案
该解决方案使用CKDatabase的扩展引入一种方法来处理cursor:CKQueryOperation。cursor
ofCKQueryOperation
继续向iCloud请求更多记录。在这种方法中,我将其分派到后台队列,以便阻止它并等待操作完全完成在接收到错误或最后一批记录时。一旦信号量解锁队列,它将继续使用结果调用主完成块。此外,我还利用了Swift在完成处理程序中的result
类型
扩展数据库{
func fetchAll(
记录类型:字符串,结果限制:Int=100,超时:TimeInterval=60,
完成:@escaping(Result)->Void
) {
DispatchQueue.global().async{[unowned self]位于
let query=CKQuery(
记录类型:记录类型,谓词:NSPredicate(值:true)
)
让信号量=分派信号量(值:0)
var记录=[CKRecord]()
var错误:错误?
var operation=CKQueryOperation(查询:query)
operation.resultsLimit=resultsLimit
operation.recordFetchedBlock={records.append($0)}
operation.queryCompletionBlock={(游标,错误)位于
guard err==nil,让cursor=cursor else{
错误=错误
信号量
返回
}
让newOperation=CKQueryOperation(游标:游标)
newOperation.resultsLimit=operation.resultsLimit
newOperation.recordFetchedBlock=operation.recordFetchedBlock
newOperation.queryCompletionBlock=operation.queryCompletionBlock
操作=新操作
self?.add(新操作)
}
self?.add(操作)
_=信号量。等待(超时:.now()+60)
如果let error=error{
完成(.failure(error))
}否则{
完成(.success(记录))
}
}
}
}
用法
对于熟悉Swift闭包语法和Result
type的人来说,使用该方法相当简单
let数据库:CKDatabase=。。。
database.fetchAll(记录类型:“User”){结果为
切换结果{
成功案例(让用户参与):
//处理获取的用户,例如,将其保存到数据库
案例。失败(let错误):
//处理错误
}
}
}
但这不会返回所有记录,只返回一个子集(默认情况下通常限制为100)@MaxDesiatov您对limited的意思是什么?如何获取所有记录?不要紧,这里回答了它(100个限制):并且不起作用。获取错误:根据CKQuery API参考:谓词基于格式字符串。不能使用值或基于块的谓词。“我不认为总是这样,但目前两个答案都不正确,因为它们使用NSPredicate(值:)
我过去曾使用过它,但根据苹果公司自己的文档,任何这样做的人都应该知道CloudKit不支持它。苹果公司很可能会做出改变,破坏代码,并且可以使用基于值或块的谓词来完成。我认为这类似于通过