Быстрый запрос GET с параметрами URL

Я создаю опрос, в котором, когда пользователь отвечает на вопрос, я добавляю ответы к строке URL-адреса с параметрами и отправляю запрос на получение на мой сервер. Таким образом, для каждого ответа делается запись выбранного ответа, метки времени и уникального идентификатора опроса.

Я не уверен, что это лучший способ сделать это, но это то, что у меня есть до сих пор.

Я создаю URL-адрес и элементы запроса.

var urlComponents: URLComponents {

    let resultID = surveyQuestions.resultId
    print("\(String(describing: resultID))")

    let resultResponseID = surveyQuestions.questions[surveyResultResponseId]
    print("\(String(describing: resultResponseID))")

    let questionIndex = questionNumbers
    print("\(String(describing: questionIndex))")

    var urlComponents = URLComponents(string: "My String URL")

    urlComponents?.queryItems = [
        URLQueryItem(name: "surveyResultsId", value: "\(String(describing: resultID))"),
        URLQueryItem(name: "surveyResultsResponseId", value: "\(String(describing: resultResponseID))"),
        URLQueryItem(name: "questions", value: "\(questionIndex)"),
        URLQueryItem(name: "selectedAnswer", value: "\(storedAnswer)")

    ] 

    let url = urlComponents?.url

    print(url!.absoluteString as Any)

    return urlComponents!
}

Затем я создаю запрос на отправку.

func sendRequest(_ url: String, parameters: [String: String], completion: @escaping ([String: Any]?, Error?) -> Void) {

    var components = URLComponents(string: url)!
    components.queryItems = parameters.map { (key, value) in
        URLQueryItem(name: key, value: value)
    }

    components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
    let request = URLRequest(url: components.url!)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, // is there data
            let response = response as? HTTPURLResponse, // is there HTTP response
            (200 ..< 300) ~= response.statusCode, // is statusCode 2XX
            error == nil else { // was there no error, otherwise ...
                completion(nil, error)
                return
        }

        let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
        completion(responseObject, nil)
        print("This is the \(responseObject!)")
    }
    task.resume()
}

И, наконец, я вызываю запрос на отправку, когда нажимается ответ.

@IBAction func answerPressed(_ sender: UIButton) {

    if sender.tag == selectedAnswer {
        questionNumbers += 1
    }
    storedAnswer = [sender.tag]
//        storedAnswer.append(sender.tag)
    print(storedAnswer)

    sendRequest("\(urlComponents)", parameters: ["": ""]) { responseObject, error in
        guard let responseObject = responseObject, error == nil else {
            print(error ?? "Unknown error")
            return
        }


        // use `responseObject` here
    }

    questionNumbers += 1
    updateQuestion()
}

Теперь, когда я запускаю это, я возвращаю строку с элементами запроса, но когда я запускаю запрос на отправку, я получаю неизвестную ошибку. Я чувствую, что делаю что-то не так. Для области «используйте responseObject здесь», что я туда добавляю. Я немного запутался. Также, когда я вызываю запрос на отправку, что я должен указать в значениях параметров. Сейчас это просто параметры: ["": ""]. Я чувствую, как будто я рядом. Буду признателен за любую оказанную помощь.


person jfulton    schedule 16.09.2019    source источник
comment
Вы пропустили код для ~= перегрузки или он работает сам по себе?   -  person Vyachaslav Gerchicov    schedule 23.12.2019


Ответы (1)


Во-первых, вы делаете гораздо больше работы, чем, вероятно, необходимо. Вы кодируете параметры строки запроса в URLComponents, что правильно. Затем при отправке вы разбираете свой URL-адрес и анализируете компоненты, а затем перекодируете их. Вы также делаете много силового развертывания, что является хрупким и скрывает проблемы.

Вот ваш код, упрощенный на игровой площадке, которая работает для меня:

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let sampleURL = "https://someserver.com/somepath"

func sendRequest(_ url: URL, completion: @escaping ([String: Any]?, Error?) -> Void) {

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data, // is there data
            let response = response as? HTTPURLResponse, // is there HTTP response
            (200 ..< 300) ~= response.statusCode, // is statusCode 2XX
            error == nil else { // was there no error, otherwise ...
                completion(nil, error)
                return
        }
        let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
        completion(responseObject, nil)
    }
    task.resume()
}

var urlComponents: URLComponents? {

    let resultID = "resultID123"
    let resultResponseID = "responseID456"
    let questionIndex = "questionNumbers1"

    var urlComponents = URLComponents(string: sampleURL)
    urlComponents?.queryItems = [
        URLQueryItem(name: "surveyResultsId", value: "\(String(describing: resultID))"),
        URLQueryItem(name: "surveyResultsResponseId", value: "\(String(describing: resultResponseID))"),
        URLQueryItem(name: "questions", value: "\(questionIndex)"),
        URLQueryItem(name: "selectedAnswer", value: "\("storedAnswer1")")

    ]
    return urlComponents
}

if let urlComponents = urlComponents, let url = urlComponents.url?.absoluteURL {
    sendRequest(url) { (result, error) in
        print("Got an answer: \(String(describing: result))")
    }
}

Когда я запускаю это для URL-адреса сервера, который возвращает действительный JSON, я получаю:

Got an answer: Optional(["image": {
    href = "https://example.com";
}, "object_types": {
    card =     {
        fields =         {
        };
        pollable = 1;
    };
}])
person David S.    schedule 16.09.2019
comment
Спасибо за быстрый ответ. Для if let urlcomponents = urlcomponents я получаю эту ошибку «let» не может отображаться вложенной внутри другого шаблона «var» или «let». Не уверен, почему. - person jfulton; 16.09.2019
comment
Когда я помещаю if let urlComponents = urlComponents, let url = urlComponents.url?.absoluteURL { sendRequest(url) { (результат, ошибка) в print(получил ответ: (String(описание: результат))) } } внутри моего Ответить Нажатая кнопка работает нормально. это как я должен использовать его? - person jfulton; 16.09.2019
comment
Я также заметил, что ответ, который я получаю при отправке запроса, не включает url и urlqueryitems. Он просто возвращает необязательный ответ без компонентов URL. - person jfulton; 17.09.2019
comment
URLQueryItems — это просто конструкция для кодирования параметров в строку запроса. Когда вы вызываете absoluteURL для urlComponents, он кодирует все компоненты в сам URL-адрес (например, пример.com/). Вы всегда можете восстановить их в случае необходимости, но они там. - person David S.; 17.09.2019
comment
Есть ли способ удалить ? перед элементами запроса? Я попытался использовать replaceOccurrences(of: ?, with: ) для sampleUrl, но это не сработало. - person jfulton; 26.09.2019
comment
@jfulton, безусловно, есть, но это изменит характер URL-адреса. Это изменит параметры строки запроса на параметры пути. Скорее всего, ваш сервер не сможет справиться с результатом. - person David S.; 26.09.2019
comment
На самом деле будет. Сервер не принимает строку запроса с символом ?. Только если я его удалю. - person jfulton; 26.09.2019
comment
Вроде бы другой вопрос. Возможно, запросите новый с желаемым URL-адресом, и мы поможем вам создать его. - person David S.; 26.09.2019