swift:守卫的捷径"让自己= ......"?

Jer*_*kar 3 optional ios swift

我正在处理来自JSON的数据,我必须将它们解析成一个快速的对象.我用这个代码:

struct MyGPSCoords {

    var latitude:Double
    var longitude:Double
    var accuracy:Int
    var datetime:NSDate

    init?(infobrutFromJson_:[String:String]?)
    {
        guard let infobrut = infobrutFromJson_ else {
            // first time the user sign up to the app, php server returns "null" in Json 
            return nil
        }

        guard
        let lat:Double = Double(infobrut["latitude"] ?? "nil"),
        let lng = Double(infobrut["longitude"] ?? "nil"),
        let acc = Int(infobrut["accuracy"] ?? "nil"),
        let dtm = NSDate(timeIntervalSince1970: Double(infobrut["time"] ?? "nil"))
        else {
            print("warning : unable to parse data from server. Returning nil");
            return nil ; // position not NIL but format not expected => = nil
        }
        self.latitude = lat
        self.longitude = lng
        self.accuracy = acc
        self.datetime = dtm
    }


}
Run Code Online (Sandbox Code Playgroud)

我想让"守卫"声明尽可能短.例如,我补充说?"nil"所以如果其中一个键不存在,则Double("nil")= nil并且guard语句可以处理.对于NSDate,我使用方便init进行了扩展?如果它的参数是零,则返回nil,所以我可以这样.

现在我的问题是,我可以通过直接在guard.latitude中分配保护声明中的值吗?当我尝试这个:

guard self.latitude = Double(infobrut["latitude"] ?? "nil"), ... 
Run Code Online (Sandbox Code Playgroud)

它说它不能从Double投出?加倍..所以,有没有办法让这个后卫更短,并避免我分配lat,lng,acc和dtm缓冲变量?

Rob*_*ier 5

首先,您当然应该尝试修复JSON,因为这个JSON格式不正确.字符串不是JSON中的数字.假设你无法纠正这个破坏的JSON,你想要的工具是flatMap,它转换为T ?? 到T?(这是后卫所期望的).

guard
    let lat = infobrut["latitude"].flatMap(Double.init),
    let lng = infobrut["longitude"].flatMap(Double.init),
    let acc = infobrut["accuracy"].flatMap(Int.init),
    let dtm = infobrut["time"].flatMap(TimeInterval.init).flatMap(Date.init(timeIntervalSince1970:))
    else {
        print("warning : unable to parse data from server. Returning nil")
        return nil // position not NIL but format not expected => = nil
}
Run Code Online (Sandbox Code Playgroud)

我看到很多评论说Codable在这里不会起作用,但绝对会这样,而且它应该是你应该使用的.这是一种方式(这是关于它的错误消息有点草率,但它很简单):

struct MyGPSCoords: Decodable {

    var latitude:Double
    var longitude:Double
    var accuracy:Int
    var datetime:Date

    enum CodingKeys: String, CodingKey {
        case latitude, longitude, accuracy, datetime
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        guard
            let lat = Double(try container.decode(String.self, forKey: .latitude)),
            let lng = Double(try container.decode(String.self, forKey: .longitude)),
            let acc = Int(try container.decode(String.self, forKey: .accuracy)),
            let dtm = TimeInterval(try container.decode(String.self,
                                                        forKey: .datetime)).flatMap(Date.init(timeIntervalSince1970:))
        else {
            throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Could not decode"))
        }

        self.latitude = lat
        self.longitude = lng
        self.accuracy = acc
        self.datetime = dtm
    }

}
Run Code Online (Sandbox Code Playgroud)

或者您可以通过内部有用的功能获得真正的幻想,并通过强大的功能摆脱所有临时变量和选项throws.

struct MyGPSCoords: Decodable {

    var latitude:Double
    var longitude:Double
    var accuracy:Int
    var datetime:Date

    enum CodingKeys: String, CodingKey {
        case latitude, longitude, accuracy, datetime
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        func decodeBrokenJSON<T>(_ type: T.Type,
                                 forKey key: CodingKeys) throws -> T
            where T: Decodable & LosslessStringConvertible {
                return try T.init(container.decode(String.self, forKey: key)) ?? {
                    throw DecodingError.dataCorruptedError(forKey: key,
                                                           in: container,
                                                           debugDescription: "Could not decode \(key)")
                    }()
        }

        self.latitude = try decodeBrokenJSON(Double.self, forKey: .latitude)
        self.longitude = try decodeBrokenJSON(Double.self, forKey: .longitude)
        self.accuracy = try decodeBrokenJSON(Int.self, forKey: .accuracy)
        self.datetime = Date(timeIntervalSince1970: try decodeBrokenJSON(TimeInterval.self, forKey: .datetime))
    }

}
Run Code Online (Sandbox Code Playgroud)

(国际海事组织,这是一个很好的例子,说明它是如何throws真正发光的,并且应该比通常使用得多.)