Swift flatMap и дженерики

Я пытаюсь использовать flatMap для создания Resource<T> в Swift, но продолжаю получать странную ошибку и работает только тогда, когда я принудительно привожу.

Resource<T>:

public struct Resource<T> {
    let record: CKRecord
    let parser: [String: AnyObject] -> T?
}

Рабочий код:

public func buildResource<T>(resource: Resource<T>) -> T? {
    var dataJson: [String: AnyObject] = [:]
    dataJson["recordID"] = resource.record.recordID
    for name in resource.record.attributeKeys {
        dataJson[name] = resource.record[name]
    }
    return (dataJson as? [String: AnyObject]).flatMap(resource.parser)
}

Приведенный выше код выдает предупреждение о том, что приведение типов всегда выполняется успешно, и это правда. Но когда я пытаюсь удалить приведение вот так:

public func buildResource<T>(resource: Resource<T>) -> T? {
    var dataJson: [String: AnyObject] = [:]
    dataJson["recordID"] = resource.record.recordID
    for name in resource.record.attributeKeys {
        dataJson[name] = resource.record[name]
    }
    return dataJson.flatMap(resource.parser)
}

Выдает следующую ошибку: 'flatMap' produces '[S.Generator.Element]', not the expected contextual result type 'T'?.

Парсер представляет собой struct init вот так:

struct Example {

    let name: String
    let id: Int
}

extension Example {

    init?(dataJson: [String: AnyObject]) {
        guard let name = dataJson["name"] as? String else {
            return nil
        }
        guard let id = dataJson["id"] as? Int else {
            return nil
        }
        self.name = name
        self.id = id
        return
    }

}

Любые идеи, как исправить этот или другой подход? Идея состоит в том, чтобы легко преобразовать любой CKRecord в структуру без необходимости написания большого количества шаблонного кода.


person Victor    schedule 08.06.2016    source источник


Ответы (2)


Ответ Дэниела Холла правильный, но при этом вы будете вынуждены изменить свою подпись инициализации parser, чтобы получить (String, AnyObject).

Лучшим вариантом было бы создать еще одну init с этой подписью и разобрать ее на init вашей json подписи, по-прежнему имея возможность создать эту структуру из необработанной json.

extension Example {

    init?(json: [String: AnyObject]) {
        guard let name = json["name"] as? String else {
            return nil
        }
        guard let id = json["id"] as? Int else {
            return nil
        }
        self.name = name
        self.id = id
      return
    }

    init (tuple : (String, AnyObject)) {
        var json : [String : AnyObject] = [:]
        json["name"] = tuple.0
        json["id"] = tuple.1
        self.init(json: json)!
    }
}

ИЗМЕНИТЬ

Поскольку вы создаете свой dataJson как [String : AnyObject], вам не нужно делать для него flatMap, вы можете просто вернуть resource.parser(json: dataJson)

person haroldolivieri    schedule 09.06.2016

Похоже, у вас неправильная подпись для вашей функции парсера. Весь словарь json имеет тип [String : AnyObject], но отдельные элементы в этом словаре при перечислении с помощью flatMap() имеют тип (String, AnyObject), а не [String : AnyObject].

Попробуйте изменить это:

public struct Resource<T> {
    let record: CKRecord
    let parser: [String: AnyObject] -> T?
}

к этому:

public struct Resource<T> {
    let record: CKRecord
    let parser: (String, AnyObject) -> T?
}
person Daniel Hall    schedule 08.06.2016
comment
Но не приведет ли это также к изменению инициализатора struct? - person Victor; 09.06.2016
comment
@Victor Да :) Я не имел в виду, что никаких других изменений не требуется, просто ошибка в коде была в этом месте. - person Daniel Hall; 09.06.2016