嵌套 Cloudkit 查询未按正确顺序打印

Nested Cloudkit queries not printing in the right order

我的代码打印顺序不正确。我认为这是我使用 dispatch to main 的方式的问题。我想传入一个老板ID。找到具有该 ID 的用户,然后获取他们的 SubscribedBosses 数组。对于该数组中的每个 ID,查询该用户和 return 该用户的 screenName。将这些 screenName 附加到 userArray。之后完成 GetBossSubs 函数和 return userArray(屏幕名称数组)。

现在 .onAppear 的结果是 运行 在 .onAppear 中的函数实际完成之前。 User AppendedFound TestUserName

之前打印
static func getBossSubs(bossID: String, completion: @escaping (Result<UserNames, Error>) ->
    ()) {
    let pred = NSPredicate(format: "uniqueID = %@", bossID)
    let sort = NSSortDescriptor(key: "creationDate", ascending: false)
    let query = CKQuery(recordType: RecordType.Users, predicate: pred)
    query.sortDescriptors = [sort]
    
    let operation = CKQueryOperation(query: query)
    operation.desiredKeys = ["subscribedBosses"]
    operation.resultsLimit = 50
    
    operation.recordFetchedBlock = { record in
        DispatchQueue.main.async {
            guard let subs = record["subscribedBosses"] as? [String] else {
                print("Error at screenName")
                
                completion(.failure(CloudKitHelperError.castFailure))
                return
            }
            let userArray = UserNames() //Error that it should be a LET is here. 
            for boss in subs{
                CloudKitHelper.getBossScreenName(bossID: boss) { (result) in
                    
                    switch result{
                        
                    case .success(let name):
                        userArray.names.append(name) //works fine
                        print("Found \(userArray.names)") //Prints a name
                    case .failure(let er):
                        completion(.failure(er))
                    }
                    
                }
                
                
            }
            print("does this run?") // Only runs if No Name
            completion(.success(userArray)) // contains no name or doesn't run?
        }
    }
    operation.queryCompletionBlock = { (_, err) in
        DispatchQueue.main.async {
            if let err = err {
                completion(.failure(err))
                return
            }
        }
        
    }
    CKContainer.default().publicCloudDatabase.add(operation)
}

我这样调用代码:

.onAppear {
            // MARK: - fetch from CloudKit
            self.userList.names = []
            let myUserID = UserDefaults.standard.string(forKey: self.signInWithAppleManager.userIdentifierKey)!
            // get my subs projects
            CloudKitHelper.getBossSubs(bossID: myUserID) { (results) in
                switch results{
                    
                case .success(let user):
                    print("Users appended")
                    self.userList.names = user.names
                case .failure(let er):
                    print(er.localizedDescription)
                }
            }
        }

这是因为您 运行 一个异步函数在 另一个 异步函数中。

// this just *starts* a series of asynchronous functions, `result` is not yet available
for boss in subs {
    CloudKitHelper.getBossScreenName(bossID: boss) { result in
        switch result {
        case .success(let name):
            userArray.names.append(name) // works fine
            print("Found \(userArray.names)") // Prints a name
        case .failure(let er):
            completion(.failure(er))
        }
    }
}

// this will run before any `CloudKitHelper.getBossScreenName` finishes
completion(.success(userArray))

一个可能的解决方案可能是检查所有 CloudKitHelper.getBossScreenName 函数是否完成(例如,通过检查 userArray 的大小)然后才 return 完成:

for boss in subs {
    CloudKitHelper.getBossScreenName(bossID: boss) { result in
        switch result {
        case .success(let name):
            userArray.names.append(name)
            if userArray.names.count == subs.count {
                completion(.success(userArray)) // complete only when all functions finish
            }
        case .failure(let er):
            completion(.failure(er))
        }
    }
}

// do not call completion here, wait for all functions to finish
// completion(.success(userArray))