简体   繁体   English

Swift flatMap和泛型

[英]Swift flatMap and generics

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. 我正在尝试使用flatMap在Swift中构建一个Resource<T> ,但是一直遇到一个奇怪的错误,只有当我强制执行强制转换时才会有效。

Resource<T> : 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'? 它给出了以下错误: 'flatMap' produces '[S.Generator.Element]', not the expected contextual result type 'T'? .

The parser is a struct init like so: 解析器是一个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
    }

}

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. 这里的想法是将任何CKRecord转换为结构,而无需编写大量的样板代码。

Daniel Hall's answer is right but, by doing this, you'll be forced to change your parser init signature to receive (String, AnyObject) . Daniel Hall的答案是对的,但是,通过这样做,你将被迫改变你的parser初始签名以接收(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 . 最好的选择是使用此签名创建另一个init并将其解析为json签名的init ,仍然能够从原始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) 当您将dataJson创建为[String : AnyObject] ,您不需要对其执行flatMap ,您只需返回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] . 整个json字典的类型为[String : AnyObject] ,但使用flatMap()枚举时该字典中的各个元素的类型为(String, AnyObject) ,而不是[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?
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM