Как передать nil в качестве необязательного аргумента универсальной функции - PullRequest
0 голосов
/ 01 октября 2018

У меня есть функция следующим образом:

func request<D: Decodable>(from urlString: String,
                           useToken: Bool = false,
                           requestType: RequestType = .get,
                           body: Data? = nil,
                           expecting type: D.Type? = nil,
                           completion: @escaping (Result<D?>) -> Void)

Возможно ли это сделать: request(..., expecting: nil) или func request<D: Decodable>(... expecting type: D.Type? = nil)?

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

Когда я использую функцию,например: request(from: "https:..", requestType: .post, body: body), компилятор жалуется, что Enum element 'post' cannot be referenced as an instance member

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

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Основная проблема здесь заключается в том, что типом, который вы хотите, является Void, но Void не может быть декодируемым, и вы не можете сделать его декодируемым, потому что неноминальные типы (такие как Void) не могут быть расширены.Это всего лишь текущее ограничение Swift.

Правильным решением этой проблемы является перегрузка.Создайте два метода:

// For values
func request<D: Decodable>(from urlString: String,
                           useToken: Bool = false,
                           requestType: RequestType = .get,
                           body: Data? = nil,
                           expecting type: D.Type,
                           completion: @escaping (Result<D>) -> Void) {}

// For non-values
func request(from urlString: String,
             useToken: Bool = false,
             requestType: RequestType = .get,
             body: Data? = nil,
             completion: @escaping (Error?) -> Void) {}

Создайте еще один общий метод, который превращает запрос в данные и который оба могут вызывать:

func requestData(from urlString: String,
                 useToken: Bool = false,
                 requestType: RequestType = .get,
                 body: Data? = nil,
                 completion: @escaping (Result<Data>) -> Void) {}

Ваша функция запроса декодирования теперь преобразует .success(Data) вD.Ваша функция запроса без декодирования отбрасывает данные (или, возможно, гарантирует, что они пусты, если вы педантичны по этому поводу), и вызывает обработчик завершения.

Если вы хотите, чтобы ваш код был немного большепараллельно, так что он всегда передает результат, а не ошибку ?, тогда вы все равно можете получить это с подстройкой к сигнатуре:

func request(from urlString: String,
             useToken: Bool = false,
             requestType: RequestType = .get,
             body: Data? = nil,
             completion: @escaping (Result<Void>) -> Void) {}

Но перегрузка по-прежнему здесь ответ.


(СТАРЫЕ ОТВЕТЫ) Здесь нет проблем с передачей nil, если D можно каким-то образом вывести.Но должен быть способ сделать вывод D.Например, следующее должно быть хорошо:

request(from: "") { (result: Result<Bool?>) in
    print(result)
}

Что не будет хорошо, так это:

request(from: "") { (result) in
    print(result)
}

Потому что в этом случае нет способа определить, что Dis.

Тем не менее, учитывая вашу цель, вы не хотите, чтобы Type был необязательным в любом случае.Как вы говорите, иногда результат «ничего не возвращает».Правильный тип для «ничего не возвращает» - Void, а не ноль.

func request<D: Decodable>(from urlString: String,
                           useToken: Bool = false,
                           body: Data? = nil,
                           expecting type: D.Type = Void.self, // <<----
                           completion: @escaping (Result<D>) -> Void)

(я предполагаю, что тогда вы хотите Result<D> вместо Result<D?>, но любой из них может быть правильным в зависимости от вашеготочный вариант использования.)

Void - это обычный тип в Swift.Это тип с ровно одним значением: (), пустой кортеж.

0 голосов
/ 01 октября 2018

это нормально работает на детской площадке

 let t = testStruct.init()
   let t2 : testStruct? = nil
   test(t)
   testOptional(t)
   testOptional(t2)

func test<T: testProtocol>(_ para: T){
    print(para.id())
}
func testOptional<T: testProtocol>(_ para: T?){
    if let p = para{
        print(p.id())
    }
}
protocol testProtocol {
    func id() -> String
}
struct testStruct{

}
extension testStruct : testProtocol {
    func id() -> String {
        return "hello"
    } 
}

, но вы не можете просто вызвать testOptional ().это должно быть передано что-то, даже ноль необязательно, чтобы тип мог быть выведен.

...