Как я могу запускать асинхронные запросы вне контроллера представления

Я создаю приложение для iOS, и я только что закончил свою часть входа/регистрации (запрашивая API для отдыха Sais.js). На данный момент у меня есть 2 контроллера представления с дублирующимся кодом, потому что я выдаю остальные вызовы на прослушивателе событий кнопки регистрации/входа каждого class и есть много подобного кода, который я могу реорганизовать.

Что я хочу сделать, так это создать синглтон с именем ApiManager, который будет содержать все вызовы, которые мне нужны. (И будущие)

Проблема в том, что с асинхронными вызовами я не могу создать функцию входа в систему (имя пользователя, пароль), которая будет возвращать данные, чтобы я мог их сохранить и подготовиться.

Каков простой/правильный способ добиться этого правильно? Это означает вызов ApiManager.myFunction и использование результата везде, где это необходимо (заполнение табличного представления для данных, инициирование перехода для входа в систему или регистрация с успехом), а также возможность повторного использования этой функции в другом контроллере представления, даже если она предназначена для другого использования. Я использую свифт.

EDIT: Вот как я это сделал, надеюсь, это поможет вам

Функция, выполняющая оставшийся вызов:

func login(#username: String, password: String, resultCallback: (finalresult: UserModel!,finalerror:String!) -> Void) {
        Alamofire.request(.POST, AppConfiguration.ApiConfiguration.apiDomain+"/login", parameters: ["username": username,"password": password], encoding: .JSON)
            .responseJSON { request, response, data, error in

                if let anError = error
                {
                    resultCallback(finalresult: nil,finalerror:anError.localizedDescription)
                }else if(response!.statusCode == 200){
                    var user:UserModel = self.unserializeAuth(data!)//just processing the json using SwiftyJSON to get a easy to use object. 


                    resultCallback(finalresult: user,finalerror:nil)
                }else{
                    resultCallback(finalresult: nil,finalerror:"Username/Password incorrect!")
                }




            }.responseString{ (request, response, stringResponse, error) in

                // print response as string for debugging, testing, etc.



                println(stringResponse)

        }
    }

И вот как я вызываю эту функцию из моего ViewController:

@IBAction func onLoginTapped(sender: AnyObject) {//When my user tap the login button

        let username = loginInput.text;//taking the content of inputs
        let password = passwordInput.text;
        ApiManager.sharedInstance.login(username:username,password:password){
             [unowned self] finalresult,finalerror in
            if(finalresult !== nil){//if result is not null login is successful and we can now store the user in the singleton
                ApiManager.sharedInstance.current_user=finalresult
                self.performSegueWithIdentifier("showAfterLogin", sender: nil)//enter the actual app and leave the login process
            }else{
                self.displayAlert("Error!", message: finalerror)//it is basically launching a popup to the user telling him why it didnt work

            }
        }

    }

person Adel 'Sean' Helal    schedule 07.09.2015    source источник


Ответы (2)


Почти все мои приложения заканчиваются классом Server, который единственный знает, как взаимодействовать с сервером. Он делает вызов, анализирует результат в структуру Swift и возвращает его. Большинство моих серверов возвращают json, поэтому я использую SwiftyJSON, но вы можете делать все, что хотите.

Дело в том, что, поскольку это единственный класс, который знает о связи с сервером, если мне нужно изменить библиотеку, используемую для связи (AFNetworking 1, 2, Parse и т. д.), это единственный класс, который мне нужно коснуться. .

class Server {
    static let instance = Server()

    func loginWithUsername(username: String, password: String, resultCallback: (result: Either<User, NSError>) -> Void) {
        // if login is successful call
        resultCallback(result: .Left(self.user!))
        // otherwise call
        resultCallback(result: .Right(error))
    }
}

Пример использования:

let server = Server.instance
SVProgressHUD.showWithStatus("Loggin In...")
server.loginWithUsername(username, password: password) { [unowned self] result in
    SVProgressHUD.dismiss()
    switch result {
    case .Left(let user):
        self.presentUserType(user.userType)
    case .Right(let error):
        self.warnUserWithMessage("An error occured. \(error.localizedDescription)")
    }
}

Если имя пользователя/пароль необходимы для всех последующих вызовов, то серверный объект сохранит их копию. Если логин возвращает токен, сервер сохраняет его копию. КЭД.

person Daniel T.    schedule 07.09.2015
comment
Превосходно! Я не знал о .Left .Right, это действительно полезно. Не могли бы вы сказать мне, в чем польза [unowned self] во второй части кода, пожалуйста? - person Adel 'Sean' Helal; 08.09.2015
comment
Я также получаю сообщение об ошибке в любой части, вы использовали конкретную структуру? - person Adel 'Sean' Helal; 08.09.2015
comment
Хорошо, я узнал, что это от robrix/Ether. Как бы вы сделали то же самое, не используя Both ? - person Adel 'Sean' Helal; 08.09.2015
comment
Я написал свой Либо. Но все, что работает для вас. Google Железнодорожное программирование, чтобы узнать больше. Без Либо вам пришлось бы передать два обратных вызова или иметь один обратный вызов с двумя необязательными параметрами с инвариантом, что только один из них будет содержать значение. - person Daniel T.; 08.09.2015
comment
Понял! Спасибо за ваши драгоценные советы. - person Adel 'Sean' Helal; 08.09.2015

Обычно у меня есть служебные функции в базовом классе, совместно используемом моими контроллерами представлений, и я использую NSNotificationCenter для реагирования на результаты запросов. Это также может быть легко достигнуто с помощью делегирования (протокол и делегат.

В основном речь идет о восприятии, но мне легче представить, что вы можете, например, запустить действие на одном контроллере и отреагировать на другом, потому что вызов занимает так много времени, и вы не блокируете навигацию в своем приложении.

person user3802077    schedule 07.09.2015