アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

java.time.InstantをSwift Codableでパースできるようにする

xxxxxx.yyyyyy , "xxxxxx.yyyyyy", xxxxxx どの形式でも受け取れるようにします。
Double等でパースすると精度が足りないのでDecimalを使用します。

import Foundation

@main
public struct hello_swift {
    public static func main() {
        let decoder = JSONDecoder()
        let encoder = JSONEncoder()
        let json = "{\"a\": 1675912938, \"b\": 1675912938.1675912938, \"c\": \"1675912938.1675912938\"}"
        let decoded = try! decoder.decode(Dictionary<String, Instant>.self, from: Data(json.utf8))
        print(decoded)
        print(String(decoding: try! encoder.encode(decoded), as: UTF8.self))
    }
}

final public class Instant: Codable, LosslessStringConvertible {
    public let seconds: Int64
    public let nano: Int64
    
    public init(_ seconds: Int64, _ nano: Int64) {
        self.seconds = seconds
        self.nano = nano
    }

    public convenience init?(_ description: String) {
        try? self.init(decinal: NSDecimalNumber(string: description))
    }

    private init(decinal: NSDecimalNumber) throws {
        let splitedText = decinal.stringValue.split(separator: ".")
        self.seconds = Int64(splitedText[0]) ?? 0
        if splitedText.count >= 2 {
            self.nano = Int64(splitedText[1]) ?? 0   
        } else {
            self.nano = 0
        }
    }

    public convenience init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let decimal = try? container.decode(Decimal.self) {
            try self.init(decinal: NSDecimalNumber(decimal: decimal))
        } else if let text =  try? container.decode(String.self) {
            try self.init(decinal: NSDecimalNumber(string: text))
        } else {
            throw NSError(domain: "parse failed. \(decoder)", code: -1, userInfo: nil)
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(Decimal(string: "\(seconds).\(nano)"))
    }

    public var description: String { return "\(seconds).\(nano)" }
}
["c": 1675912938.1675912938, "b": 1675912938.1675912938, "a": 1675912938.0]
{"a":1675912938,"b":1675912938.1675912938,"c":1675912938.1675912938}