Swift-Alamofire调用函数时Completion Handler未执行问题求助
问题:API请求完成后Completion Handler未触发,Realm读取数组为空
问题详情
定义了如下Swift函数用于API请求并存储数据到Realm:
typealias Handler = (Swift.Result<Any?, APIErrors>) -> Void func viewApi (ownerId: String, accessToken: String, completionHandler: @escaping Handler ){ var viewArray: [Information] = [Information]() let headers: HTTPHeaders = [ .authorization("Bearer \(accessToken)") ] let viewUrl = "\(viewTaskUrl)?userId=\(ownerId)" let url = URL(string: viewUrl) var request = URLRequest(url: url!) request.method = .get request.headers = headers var taskList:[Task] = [] AF.request(request).responseJSON(completionHandler: { (response) in var holder = Information(id: 0, title: "", description: "", status: "") switch response.result { case .success(let data): do{ if response.response?.statusCode == 200 { // completionHandler(.success("JSON HERE: \ (String(describing: json))")) let jsonData = try JSONSerialization.data(withJSONObject: data) let jsonString = String(data: jsonData, encoding: .utf8) let trySplit = jsonString?.components(separatedBy: ",") for i in 0...trySplit!.count-1{ if trySplit![i].contains("id"){ let id = trySplit![i].components(separatedBy: ":")[1] holder.id = Int(id)! } if trySplit![i].contains("title"){ let title = trySplit![i].components(separatedBy: ":")[1] holder.title = title // print("TITLE: \(title)") } if trySplit![i].contains("description"){ let desc = trySplit![i].components(separatedBy: ":")[1] holder.description = desc } if trySplit![i].contains("status"){ let stat = trySplit![i].components(separatedBy: ":")[1] let edited = stat.replacingOccurrences(of: "}", with: "") holder.status = edited viewArray.append(holder) } } }else{ // completionHandler(.failure(.custom(message: "Please check your network connectivity"))) print("Error") } }catch{ print(error.localizedDescription) } case .failure(let err): print (err) } print("ARRAY: \(viewArray)") for i in 0 ..< viewArray.count { let task = Task() let edited = viewArray[i].status.replacingOccurrences(of: "]", with: "") let new = viewArray[i].title.replacingOccurrences(of: "\"", with: "") task.taskName = new task.id = viewArray[i].id task.taskStatus = edited task.taskDescription = viewArray[i].description task.email = ownerId taskList.append(task) } try! self.realm.write{ self.realm.add(taskList) } }).resume() }
该函数可正常获取JSON并写入Realm,但在ViewController调用时,Completion Handler未执行,且立刻读取Realm时返回空数组:
self.APIManager.viewApi(ownerId: self.userId!, accessToken: self.token!) { (result) in switch result{ case .success(_): //does not run print("SUCCESS COMPLETION") case .failure(let err): print(err.localizedDescription) } }
需求:让函数执行完成后触发后续代码,确保读取Realm数组不为空且Completion Handler正常触发。
注:暂时忽略JSON处理的粗糙实现,后续会优化
解决方案
1. 补全Completion Handler的调用
问题核心是你在viewApi函数中从未调用传入的completionHandler闭包,这是导致ViewController中闭包不执行的直接原因。需要在所有请求分支中添加调用:
修改后的viewApi关键部分:
AF.request(request).responseJSON(completionHandler: { (response) in // ... 原有JSON解析、数组组装逻辑保持不变 ... switch response.result { case .success(let data): do{ if response.response?.statusCode == 200 { // ... JSON解析逻辑 ... // 请求成功且解析完成后调用success分支 completionHandler(.success(taskList)) // 可传入需要的数据,比如组装好的taskList }else{ let error = APIErrors.custom(message: "请求失败,状态码:\(response.response?.statusCode ?? 0)") completionHandler(.failure(error)) print("Error") } }catch{ let error = APIErrors.custom(message: error.localizedDescription) completionHandler(.failure(error)) print(error.localizedDescription) } case .failure(let err): let error = APIErrors.custom(message: err.localizedDescription) completionHandler(.failure(error)) print(err) } // ... Realm写入逻辑 ... try! self.realm.write{ self.realm.add(taskList) } // 可选:如果需要确保Realm写入完成后再通知回调,可将completionHandler移到这里 }).resume()
2. 在Completion Handler中读取Realm数据
网络请求和Realm写入都是耗时/异步操作,不能在调用viewApi后立刻读取Realm数据,必须等待completionHandler触发后再执行读取逻辑:
修改后的ViewController调用代码:
self.APIManager.viewApi(ownerId: self.userId!, accessToken: self.token!) { [weak self] (result) in guard let self = self else { return } switch result{ case .success(_): print("SUCCESS COMPLETION") // 此时Realm已写入完成,在这里读取数据 do { let realm = try Realm() let tasks = realm.objects(Task.self).filter("email == %@", self.userId!) let taskArray = Array(tasks) print("读取到的Realm数据:\(taskArray)") // 后续UI更新或业务逻辑放在这里 DispatchQueue.main.async { // 若需更新UI,切换到主线程执行 } } catch { print("Realm读取失败:\(error.localizedDescription)") } case .failure(let err): print(err.localizedDescription) } }
额外注意事项
- 避免使用
try!,建议用do-catch处理Realm操作异常,防止崩溃 - 网络请求回调默认在后台线程,若需更新UI,必须切换到主线程执行
内容的提问来源于stack exchange,提问作者Tristan
相关产品推荐
相关产品推荐

