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)


Отговорът на Daniel Hall е правилен, но като направите това, вие ще бъдете принудени да промените своя 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