2016-06-08 3 views
3

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

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 в структуру легко, без необходимости писать много шаблонов.

ответ

4
ответ

Daniel Hall является правильным, но, делая это, вы будете вынуждены изменить parser инициализации подписи получить (String, AnyObject) ,

Наилучшим вариантом было бы создать еще один init с этой подписью и разбор его вашей подписи jsoninit, еще будучи в состоянии создать эту структуру из сырого 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)! 
    } 
} 

EDIT

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

3

Похоже, что у вас неправильная подпись для функции вашего парсера. Весь словарь 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? 
} 
+0

Но разве не это также приводят к изменению инициализатора 'struct'? – Victor

+1

@Victor Да :) Я не подразумевал, что никаких других изменений не было, просто ошибка в коде была в этом месте. –

Смежные вопросы