Я создаю приложение, которое должно вызывать некоторые веб-службы в начале приложения.
Я написал WebServiceManager
, который содержит методы для вызова веб-сервисов и возврата результата в completionHandler
, вот один из методов WebServiceManager
:
extension WebServiceManager {
func checkVersion(completionHandler: @escaping (CheckVersionResponse?, Error?) -> ()) {
sessionManager?.adapter = BaseClientInterceptor()
let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"] as! String
sessionManager?.request(Config.VERSION_URL + "/\(appVersion)/\(buildNumber)" + Config.STATUS, method: .get).responseData { response in
print(response.response?.statusCode)
switch response.result {
case .success(let value):
print("----WS: checkVersion Success-----")
let value = JSON(value)
print(value)
let response = CheckVersionResponse(responseCode: response.response?.statusCode ?? -1)
if (response.getResponseCode() == 200) {
response.setUrl(value["url"].string ?? "")
response.setNow(value["now"].intValue)
response.setStatus(value["status"].stringValue)
response.setMessage(value["message"].stringValue)
}
completionHandler(response, nil)
case .failure(let error):
print(error)
print("----WS: checkVersion Failed-----")
completionHandler(nil, error)
}
}
}
}
и я использую некоторые методы этого менеджера в SplashScreenViewController
:
class SplashViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
WebServiceManager.shared.checkVersion() { response, error in
//...
}
WebServiceManager.shared.getCurrentDate() { response, error in
//...
}
WebServiceManager.shared.getUpdatedKeyValues() { response, error in
//...
}
if !UserDefaults.standard.bool(forKey: "hasRegistered") {
self.performSegue(withIdentifier: "ToRegistration", sender: self)
} else {
self.performSegue(withIdentifier: "ToMain", sender: self)
}
}
}
Я ожидаю, что этот метод работает построчно, но сначала он проходит через if
.
Как я могу вызвать эти веб-службы, чтобы затем принять решение на основе значений UserDefaults
?
Методы sessionManager.request являются асинхронными, поэтому они не будут выполняться синхронно (построчно) в viewDidAppear (кстати, вам не хватает super.viewDidAppear())
Если вы хотите, чтобы какой-то код выполнялся после этих трех сервисов, вы можете сделать что-то вроде этого, но не уверен, что это лучшее решение:
class SplashViewController: UIViewController {
private var allDone = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
WebServiceManager.shared.checkVersion() { response, error in
//...
checkUserDefault()
}
WebServiceManager.shared.getCurrentDate() { response, error in
//...
checkUserDefault()
}
WebServiceManager.shared.getUpdatedKeyValues() { response, error in
//...
checkUserDefault()
}
}
private func checkUserDefault() {
allDone += 1
/// When all 3 services are finish
if allDone == 3 {
if !UserDefaults.standard.bool(forKey: "hasRegistered") {
self.performSegue(withIdentifier: "ToRegistration", sender: self)
} else {
self.performSegue(withIdentifier: "ToMain", sender: self)
}
}
}
}
Надеюсь, это поможет вам!
Обновлено: это самое простое решение, основанное на вашем существующем коде, если вы хотите сделать это «правильно», вы можете проверить обещания: http://khanlou.com/2016/08/promises-in-swift/https://www.raywenderlich.com/9208-getting-started-with-promisekit
Вам нужно использовать PromiseKit и сделать что-то вроде этого:
Замените свои методы API, чтобы они использовали Promise в качестве ответа:
func checkVersion() -> Promise<CheckVersionResponse> {
sessionManager?.adapter = BaseClientInterceptor()
let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"] as! String
// Return a Promise for the caller of this function to use.
return Promise { fulfill, reject in
// Inside the Promise, make an HTTP request
sessionManager?.request(Config.VERSION_URL + "/\(appVersion)/\(buildNumber)" + Config.STATUS, method: .get).responseData { response in
print(response.response?.statusCode)
switch response.result {
case .success(let value):
print("----WS: checkVersion Success-----")
let value = JSON(value)
print(value)
let response = CheckVersionResponse(responseCode: response.response?.statusCode ?? -1)
if (response.getResponseCode() == 200) {
response.setUrl(value["url"].string ?? "")
response.setNow(value["now"].intValue)
response.setStatus(value["status"].stringValue)
response.setMessage(value["message"].stringValue)
}
fulfill(response)
case .failure(let error):
print(error)
print("----WS: checkVersion Failed-----")
reject(error)
}
}
}
}
Затем в UIViewController вызовите методы WebServiceManager следующим образом:
WebServiceManager.shared.checkVersion().then { response in
// ....
WebServiceManager.shared.getCurrentDate()
}.then { date in
// ...
WebServiceManager.shared.getUpdatedKeyValues()
}.catch { error in
print(error)
}
Я отредактировал свой ответ на случай, если он лучше соответствует вашим потребностям. Ваше здоровье!
спасибо за ваше хитрое решение, но я думал, что как-то я могу заставить его синхронизироваться по
completionHandler
, а также я хочу лучшее решение и не уверен, что ваше лучшее или нет