我正在开发一个使用cloudKit存储用户数据的存储管理系统。我在私有数据库中设置了一些预置数据的自定义区域。
我有一个func loadCustomerArray()
,它应该检索"Customers“区域中的所有记录,然后从每个返回的CKRecord中创建一个Customer对象。我使用的是fetch(withQuery: , inZoneWith: )
函数,但是由于没有关于这个函数的文档,而且对于这个问题的大多数在线回答都使用了现在不再推荐的方法,所以我很难为这个函数设置完成处理程序。
这是我的代码:
func loadCustomerArray() async throws -> [Customer] {
//set the cloud database to the users private database
let cloudDB = CKContainer.default().privateCloudDatabase
let custZone = CKRecordZone(zoneName: "Customers")
let pred = NSPredicate(value: true) //true -> return all records
let query = CKQuery(recordType: "Customer", predicate: pred)
var customerRecords: [Customer] = []
//Get the records matching these criteria
cloudDB.fetch(withQuery: query, inZoneWith: custZone.zoneID, resultsLimit: 100) { result, error in
}
return customerRecords
}
目前,我得到了一个错误的Contextual closure type '(Result<(matchResults: [(CKRecord.ID, Result<CKRecord, Error>)], queryCursor: CKQueryOperation.Cursor?), Error>) -> Void' expects 1 argument, but 2 were used in closure body
,但是我不知道用什么代替result, error in
来迭代结果。
编辑
根据杰西的指示我放弃了那个想法。
以下是我对如何实现他的解决方案的理解:
我添加了func records
和queryRecords
func表单,相同的帖子如下所示:
public func queryRecords(recordType: CKRecord.RecordType, predicate: NSPredicate, database: CKDatabase, Zone: CKRecordZone) async throws -> [CKRecord] {
return try await database.records(type: recordType, predicate: predicate, zoneID: Zone.zoneID)
}
public extension CKDatabase {
/// Request `CKRecord`s that correspond to a Swift type.
///
/// - Parameters:
/// - recordType: Its name has to be the same in your code, and in CloudKit.
/// - predicate: for the `CKQuery`
func records(type: CKRecord.RecordType,predicate: NSPredicate = .init(value: true),zoneID: CKRecordZone.ID) async throws -> [CKRecord] {
try await withThrowingTaskGroup(of: [CKRecord].self) { group in
func process(
_ records: (
matchResults: [(CKRecord.ID, Result<CKRecord, Error>)],
queryCursor: CKQueryOperation.Cursor?
)
) async throws {
group.addTask {
try records.matchResults.map { try $1.get() }
}
if let cursor = records.queryCursor {
try await process(self.records(continuingMatchFrom: cursor))
}
}
try await process(
records(
matching: .init(
recordType: type,
predicate: predicate
),
inZoneWith: zoneID
)
)
return try await group.reduce(into: [], +=)
}
}
}
并向我的Customer类添加了一个初始化器,如下所示:
//Initializer with CKRecord
init (record: CKRecord) {
self.ID = record["customerID"] as! Int
self.CustomerName = record["customerName"] as! String
self.ContactName = record["contactName"] as! String
self.Address = record["Address"] as! String
self.City = record["City"] as! String
self.PostalCode = record["postCode"] as! String
self.Country = record["Country"] as! String
}
所以现在我的loadCustomerArray()
功能是这样的:
func loadCustomerArray() async throws -> [Customer] {
//array to be returned
var customers: [Customer] = []
//set the cloud database to the users private database
let cloudDB = CKContainer.default().privateCloudDatabase
let custZone = CKRecordZone(zoneName: "Customers")
let pred = NSPredicate(value: true) //true -> return all records
//Get the records matching these criteria
let customerRecords = try await queryRecords(recordType: "Customer", predicate: pred, database: cloudDB, Zone: custZone)
for record in customerRecords {
//create customer object from the records
let customer = Customer(record: record)
//add customer obj to the array to be returned
customers.append(customer)
}
return customers
}
上述loadCustomerArray()`` func is called like so inside of my customers page
viewDidLoad()` func:
Task {
do {
customerArray = try await loadCustomerArray()
tableView.reloadData()
}
catch {
print(error)
}
}
但是它仍然不能正常工作,所以对如何正确地实现它的任何解释都是非常有帮助的。
更新
我添加了以下代码,让用户知道他们的iCloud帐户是否可以在应用程序中使用:
//check iCloud acc. status
CKContainer.default().accountStatus { (accountStatus, error) in
//creates an alert popup depending on the iCloud account status
switch accountStatus {
case .available:
let cloudAvailable = UIAlertController(title: "iCloud Account Available",
message: "your iCloud account will be used to store your stores data",
preferredStyle: .alert)
cloudAvailable.addAction(UIAlertAction(title: "Okay", style: .default, handler: { (_) in
cloudAvailable.dismiss(animated: true)
}))
DispatchQueue.main.async {
self.present(cloudAvailable, animated: true)
}
case .noAccount:
let noCloud = UIAlertController(title: "No iCloud Account Available",
message: "this app requires an iCloud account, please set up an account and then try to sign up again",
preferredStyle: .alert)
noCloud.addAction(UIAlertAction(title: "Okay", style: .default, handler: { (_) in
noCloud.dismiss(animated: true)
}))
DispatchQueue.main.async {
self.present(noCloud, animated: true)
}
case .restricted:
let restrictedCloud = UIAlertController(title: "iCloud Account Is Restricted",
message: "please unrestrict your iCloud account and try to sign up again",
preferredStyle: .alert)
restrictedCloud.addAction(UIAlertAction(title: "Okay", style: .default, handler: { (_) in
restrictedCloud.dismiss(animated: true)
}))
DispatchQueue.main.async {
self.present(restrictedCloud, animated: true)
}
//unable to determine iCloud Account status as the defualt case
default:
let unableToDetermine = UIAlertController(title: "Unable To Determine iCloud Account Status",
message: "please make sure you have set up an iCloud account and that it allows this app access",
preferredStyle: .alert)
unableToDetermine.addAction(UIAlertAction(title: "Okay", style: .default, handler: { (_) in
unableToDetermine.dismiss(animated: true)
}))
DispatchQueue.main.async {
self.present(unableToDetermine, animated: true)
}
}
在我注册页面的viewDidLoad()
功能里面。当我在模拟器上测试它时,它返回了noCloud
UIAlertController,所以问题是我没有在模拟器中登录我的Apple。
发布于 2022-03-29 22:17:54
不要将旧API与Swift并发混用。相反,请将Customer
与类似于InitializableWithCloudKitRecord
的协议相结合。
var customers: [Customer] {
get async throws {
try await .init(
database: CKContainer.default().privateCloudDatabase,
zoneID: CKRecordZone(zoneName: "Customers").zoneID
)
}
}
public protocol InitializableWithCloudKitRecord {
init(record: CKRecord) throws
}
public extension Array where Element: InitializableWithCloudKitRecord {
init(
database: CKDatabase,
zoneID: CKRecordZone.ID? = nil,
predicate: NSPredicate = .init(value: true)
) async throws {
self = try await database.records(
type: Element.self,
zoneID: zoneID,
predicate: predicate
).map(Element.init)
}
}
https://stackoverflow.com/questions/71668593
复制相似问题