I'm trying to use flatMap to build a Resource<T>
in Swift, but keep getting a strange error, and only works when I force the cast.
Resource<T>
:
public struct Resource<T> {
let record: CKRecord
let parser: [String: AnyObject] -> T?
}
Working code:
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)
}
The code above gives a warning that the casts always succeeds, which is true. But when I try to remove the cast like so:
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)
}
It gives the following error: 'flatMap' produces '[S.Generator.Element]', not the expected contextual result type 'T'?
.
The parser is a struct init
like so:
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
}
}
Any ideas how to fix this or a different approach? The idea here is to transform any CKRecord into a struct easily without needing to write a lot of boilerplate code.
Daniel Hall's answer is right but, by doing this, you'll be forced to change your parser
init signature to receive (String, AnyObject)
.
The best option would be to create another init
with this signature and parsing it to your json
signature's init
, still being able to create this struct from a raw 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
As you're creating your dataJson
as a [String : AnyObject]
, you don't need to do a flatMap
on it, you can just return resource.parser(json: dataJson)
Looks like you have the wrong signature for your parser function. The entire json dictionary is of type [String : AnyObject]
, but the individual elements in that dictionary when enumerated with flatMap()
are of type (String, AnyObject)
, not [String : AnyObject]
.
Try changing this:
public struct Resource<T> {
let record: CKRecord
let parser: [String: AnyObject] -> T?
}
to this:
public struct Resource<T> {
let record: CKRecord
let parser: (String, AnyObject) -> T?
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With