在解释响应之前,如何确保此功能完成?
Posted
技术标签:
【中文标题】在解释响应之前,如何确保此功能完成?【英文标题】:How Can I Make Sure This Function Completes Before I Interpret The Response? 【发布时间】:2017-02-03 07:25:18 【问题描述】:我对 Swift 有点陌生,并且在这个网站的帮助下,我已经大致了解了如何使用完成处理程序。在尝试了几天后,我希望能得到更直接的帮助。
我有一个:
@IBAction func submitRegistrationButton(_ sender: Any)
if((firstNameField.text?.trimmingCharacters(in: .whitespacesAndNewlines).characters.count) == 0)
showXAlert(title: "Oops!", message: "Please enter your first name.", viewController: self)
else if((lastNameField.text?.trimmingCharacters(in: .whitespacesAndNewlines).characters.count) == 0)
showXAlert(title: "Oops!", message: "Please enter your last name.", viewController: self)
else if((emailAddressField.text?.trimmingCharacters(in: .whitespacesAndNewlines).characters.count) == 0)
showXAlert(title: "Oops!", message: "Please enter your email address.", viewController: self)
else if !isValidEmail(testStr: emailAddressField.text!)
showXAlert(title: "Oops!", message: "Please enter a valid email address.", viewController: self)
else if((passwordField.text?.trimmingCharacters(in: .whitespacesAndNewlines).characters.count) == 0)
showXAlert(title: "Oops!", message: "Please enter a password.", viewController: self)
else if passwordField.text != passwordConfirmationField.text
showXAlert(title: "Oops!", message: "Your password and password confirmation do not match. Please correct.", viewController: self)
else
registrant.firstName = firstNameField.text!
registrant.lastName = lastNameField.text!
registrant.zipCode = zipCodeField.text!
registrant.emailAddress = emailAddressField.text!
registrant.password = passwordField.text!
storeRegistrationInfo(registrant: registrant) (object: XUserAPIResult) in
print("submissionStatus = \(object.success)")
if object.success == 1
showXAlert(title: "Welcome \(self.registrant.firstName)!", message: "Your registration was submitted successfully. Log in by clicking the Login button below", viewController: self)
else
showXAlert(title: "Un Oh", message: "There was a problem with your registration. \(object.errorMessage)", viewController: self)
...调用:
func storeRegistrationInfo(registrant: XRegistrantInfo, finished: @escaping (XUserAPIResult)->())
var apiResult = XUserAPIResult()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newRegistrant = NSEntityDescription.insertNewObject(forEntityName: "User", into: context)
let requestURL = NSURL(string: USER_API_URL)
let request = NSMutableURLRequest(url: requestURL! as URL)
request.httpMethod = "POST"
newRegistrant.setValue(registrant.firstName, forKey: "firstName")
newRegistrant.setValue(registrant.lastName, forKey: "lastName")
newRegistrant.setValue(registrant.zipCode, forKey: "zipCode")
newRegistrant.setValue(registrant.emailAddress, forKey: "emailAddress")
newRegistrant.setValue(registrant.password, forKey: "password")
newRegistrant.setValue(registrant.personna, forKey: "personna")
do
try context.save()
let postParameters = "tag=" + REGISTRATION_API_TAG + "&firstName=" + registrant.firstName + "&lastName=" + registrant.lastName + "&password=" + registrant.password + "&username=" + registrant.emailAddress + "&personna=" + registrant.personna + "&zipCode=" + registrant.zipCode
request.httpBody = postParameters.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest)
data, response, error in
if error != nil
print("error is \(error)")
return
print("response = \(response)")
//parsing the response
do
//converting resonse to NSDictionary
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
//parse json 2
if let dictionary = myJSON as? [String: Any]
if let apiSuccess = dictionary["success"] as? Int
apiResult.success = apiSuccess
if let apiError = dictionary["error"] as? Int
apiResult.error = apiError
if apiError != 0
if let apiErrorMessage = dictionary["error_msg"] as? String
apiResult.errorMessage = apiErrorMessage
else
if let apiUID = dictionary["uid"] as? String
apiResult.uniqueID = apiUID
if let nestedDictionary = dictionary["user"] as? [String: Any]
if let apiFirstName = nestedDictionary["firstName"] as? String
apiResult.user.firstName = apiFirstName
if let apiLastName = nestedDictionary["lastName"] as? String
apiResult.user.lastName = apiLastName
if let apiEmail = nestedDictionary["e-mail"] as? String
apiResult.user.emailAddress = apiEmail
if let apiCreatedAt = nestedDictionary["created_at"] as? String
apiResult.user.createdAt = apiCreatedAt
if let apiPersonna = nestedDictionary["personna"] as? String
apiResult.user.personna = apiPersonna
finished(apiResult)
catch
print(error)
task.resume()
finished(apiResult)
catch
print("There was an error saving to Core Data")
finished(apiResult)
submitRegistrationButton() 代码应该等到 storeRegistrationInfo() 返回一个 XUserAPIResult 结构,然后根据 XUserAPIResult struct 显示适当的警报strong>XUserAPIResult 的成功属性。
问题是成功检查代码在 storeRegistrationInfo() 完成解析 JSON 之前执行;显示错误的警报,然后在解析 JSON 后正确执行。代码的其他方面(Web API 调用、解析 JSON、将数据保存到 Web 数据库)有效。
我很确定我使用完成处理程序或调用 storeRegistrationInfo() 的方式有问题,但我不确定如何修复它。
如何确保@IBAction func submitRegistrationButton(_ sender: Any)中的警报代码:
storeRegistrationInfo(registrant: registrant) (object: XUserAPIResult) in
print("submissionStatus = \(object.success)")
if object.success == 1
showXAlert(title: "Welcome \(self.registrant.firstName)!", message: "Your registration was submitted successfully. Log in by clicking the Login button below", viewController: self)
else
showXAlert(title: "Un Oh", message: "There was a problem with your registration. \(object.errorMessage)", viewController: self)
...仅在解析 JSON 并填充 UserAPIResult 结构并传回后才调用?
谢谢。
【问题讨论】:
【参考方案1】:您的代码中的问题是这里的行:
task.resume()
finished(apiResult)
您应该删除对 finished
完成处理程序的调用,因为在收到响应后,应该在已经放置它的位置调用该处理程序。
对您的另一个改进建议是简化您的字段验证代码以使用guard
语句。
【讨论】:
谢谢!这似乎已经解决了它!我会听从你关于守卫声明的建议...... ...我在上面说“似乎”是因为在显示“欢迎...”警报后应用程序现在崩溃了。我不知道这是否相关。 为了结束这个问题以及其他遇到同样问题的人,感谢this thread 我意识到showXAlert()
试图从主线程以外的线程显示警报。这就是导致崩溃的原因。【参考方案2】:
试试下面的
typealias CompletionHandler = (data:XRegistrantInfo,success:Bool) -> Void;
func storeRegistrationInfo(registrant: XRegistrantInfo,completionHandler: CompletionHandler)
// your code.
//
//
completionHandler(data: dataToReturn,success : true/false)
调用会是这样的
storeRegistrationInfo(registrant, (data,success) -> Void in
//onCompletion the code will go here
if success
// success
else
// fail
)
【讨论】:
谢谢莫恩。我刚刚对typealias
做了一些快速研究。您是否建议这样做以提高代码的可维护性/可读性?您是否有任何理由检查 success: Bool
与我目前拥有的 XUserAPIResult 实例的 success: Int
属性?
如果返回类型只是要在 2 个值之间切换,那么我通常更喜欢使用 bool,如果它大于但在一个序列中然后枚举以上是关于在解释响应之前,如何确保此功能完成?的主要内容,如果未能解决你的问题,请参考以下文章