Как использовать JSON вне Alamofire.request ()? - PullRequest
0 голосов
/ 01 января 2019

Я пытаюсь вернуть JSON Словарь из Alamofire.Проблема в том, что при присвоении JSON внешней переменной назначенное значение сохраняется и читается только внутри блока Alamofire.request, а вне его значения не сохраняется (волшебство для меня).Что я сделал не так?

Я делаю это впервые.Я пытался использовать URLSession.shared.dataTask, но я также не мог вернуть значение из задачи.Я заметил, что сначала точка останова сработала на линии return res, затем на линии return json и затем на линии print (JSON).Я не знаю почему.

struct resultMsg {
    var message: [String:Any] = [:]
    init() { }
    init(message: [String:Any]) {
        self.message = message
    }
}

func sendGET(method: String, data: [String:String]) -> [String:Any] {
    let resultURL: String = rootURI + method
    let url = URL(string: resultURL)!
    var res: [String:Any] = [:]

    Alamofire.request(url, method: .post, parameters: data, encoding: JSONEncoding.default)
        .responseJSON { response in
            print(response)
            if let status = response.response?.statusCode {
                switch(status){
                case 201:
                    print("example success")
                default:
                    print("error with response status: \(status)")
                }
            }
            //to get JSON return value
            if let result = response.result.value {
                let JSON = result as! NSDictionary
                res = JSON as! [String : Any]
                print(JSON) // Here res is contain actualy json
            }
    }
    return res // Here res equal [:]
}

public func Authorize() -> resultMsg {
    let data: [String:String] = ["login":login,
                                 "pass":pass]        
    var json = resultMsg.init()
    json.message = sendGET(method: "auth.php", data: data)
    return json
}

Ответы [ 4 ]

0 голосов
/ 02 января 2019

Я предложу вам метод.С помощью Swift 4.2 был запущен Codable , так что вы можете легко анализировать JSON и избавляться от кода.

Примеры Класс

public struct Faults: Decodable {

    public enum CodingKeys:String,CodingKey{
        case guid
        case adi
    }

    public let guid:Int
    public let adi:String
}

И jsonparse (получить веб-сервисы)

 public func fetchFaults(complation: @escaping (Result<[Faults]>) -> Void) {
        let urlString = "http://examples"
       request(urlString, method: .get, encoding: URLEncoding.default, headers: ...)
        .responseData { (response) in
            switch response.result {
            case .success(let data):
                do {
                    let faults = try Decoders.plainDateDecoder.decode([Faults].self, from: data)
                    complation(.success(faults))
                } catch{
                    complation(.failure(Error.serializationError(internal: error)))
                }
            case .failure(let error):
                complation(.failure(Error.networkError(internal: error)))
            }
        }
    }  
0 голосов
/ 01 января 2019

Том Харрингтон прав.Лучшим подходом было бы добавить блок завершения к

func sendGET

как

func sendGET(method: String, data: [String:String], completion: ([String:Any]) -> Void)

и вызвать завершение, когда вы получите json,Вот и все.

Счастливое кодирование

0 голосов
/ 01 января 2019

В общем, я решил, что мне еще рано писать Swift программы.Я хотел портировать свой проект для iOS, написанный на C#, я решил, что нативное приложение будет лучше, и есть много ловушек с этим закрытием.Но в C# все проще: просто напишите await перед функцией, но там это не всегда нужно.

Спасибо всем, кто помог ...

UPD1

Я знаю, что это, вероятно, глупо, но только этот метод работает без лишних проблем с замыканиями.Но повторяющегося кода станет много.

UPD 2

Также, к сожалению, мне пришлось использовать GET вместо POST, поскольку в POSTСервер не получил никаких данных.Я проверил это с echo json_encode($_POST);.Но я мог бы также не использовать Alamofire.

UPD 3

Наконец, по совету Тома Харрингтона, мне удалось найти лучшее решение.До того, как я нашел решение , например, , но оно не работает в DispatchQueue.main.async, например, Alamofire.request (я предположил, что Alamofire использует main.async).Поэтому я сделал как здесь .

UPD 4

И я снова ошибаюсь.Раствор в UPD 3 иногда зависает в теле цикла.И я решил, наконец, научиться использовать собственное завершение, и я сделал это:)

func sendGET(_ method: String,_ data: [String:String],_ instance: CommonClass, completion : @escaping (Any?)->()) {
    let url = getURL(method: method, data: data)
    Alamofire.request(url, method: .get).responseJSON { response in
        if let status = response.response?.statusCode {
            instance.manageStatus(status)
        }
        if let result = response.result.value {
            completion(result)
        }
    }
}

func someFunc(_ instance: CommonClass) {      
    let data: [String:String] = ["id":String(id),
                                 "route":route]
    sendGET("getTaskData.php", data, instance, completion: { response in
        let JSON = response as! [[String:String]]
        // Do something with JSON
    })
}
0 голосов
/ 01 января 2019

Это потому, что сетевой запрос асинхронный, как обычно.Когда вы звоните Alamofire.request(...), начинается сетевой запрос, и request возвращается немедленно.В то же время, работа в сети происходит в фоновом режиме.После возврата request ваша функция sendGET сразу переходит к следующей строке.

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

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

Существуют различные способы решения этой проблемы.

  • В одном подходе вы бы сделали res свойствомкласс, где этот код.Тогда ваш блок завершения будет по-прежнему присваивать значение res, а затем вызывать новый код, чтобы сообщить вашему классу, что результат доступен.
  • В другом подходе вы изменили бы закрытие завершения, чтобы передать JSON.данные для какой-то другой функции, которая использует данные напрямую.Добавьте новую функцию с аргументом типа [String : Any] и вызовите эту функцию из закрытия завершения.

Какая из них лучше, зависит от того, что еще происходит в вашем приложении и как вам нужноиспользовать эти данные, когда вы их получите.

...