Swift中使用Location包装器解码CLLocation变量报错求助
解决CLLocation Codable扩展中的类型不匹配问题
问题场景
为CLLocation实现Codable扩展时,解码FriendBase结构体的lastKnownLocation属性时出现错误:Cannot assign value of type 'Location' to type 'CLLocation?',相关代码如下:
原FriendBase结构体及解码扩展
struct FriendBase: Identifiable, Encodable { var id: UUID = UUID() var firstName: String var lastKnownLocation: CLLocation? = nil enum CodingKeys: String, CodingKey { case id case firstName case lastKnownLocation } } extension FriendBase: Decodable { init(from decoder: Decoder) throws { let value = try decoder.container(keyedBy: CodingKeys.self) id = try value.decode(UUID.self, forKey: .id) firstName = try value.decode(String.self, forKey: .firstName) lastKnownLocation = try value.decode(Location.self, forKey: .lastKnownLocation) } }
原CLLocation扩展及中间模型
extension CLLocation: Encodable { enum CodingKeys: String, CodingKey { case latitude case longitude case altitude case horizontalAccuracy case verticalAccuracy case speed case course case timestamp } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(coordinate.latitude, forKey: .latitude) try container.encode(coordinate.longitude, forKey: .longitude) try container.encode(altitude, forKey: .altitude) try container.encode(horizontalAccuracy, forKey: .horizontalAccuracy) try container.encode(verticalAccuracy, forKey: .verticalAccuracy) try container.encode(speed, forKey: .speed) try container.encode(course, forKey: .course) try container.encode(timestamp, forKey: .timestamp) } } struct Location: Codable { let latitude: CLLocationDegrees let longitude: CLLocationDegrees let altitude: CLLocationDistance let horizontalAccuracy: CLLocationAccuracy let verticalAccuracy: CLLocationAccuracy let speed: CLLocationSpeed let course: CLLocationDirection let timestamp: Date } extension CLLocation { convenience init(model: Location) { self.init(coordinate: CLLocationCoordinate2DMake(model.latitude, model.longitude), altitude: model.altitude, horizontalAccuracy: model.horizontalAccuracy, verticalAccuracy: model.verticalAccuracy, course: model.course, speed: model.speed, timestamp: model.timestamp) } }
解决方案
方案1:基于现有Location模型修复类型转换
错误核心是Location实例无法直接赋值给CLLocation?类型属性,需先解码Location,再通过你定义的便利初始化器转换为CLLocation,同时用decodeIfPresent处理可选值(避免lastKnownLocation为nil时解码失败):
修改FriendBase的Decodable扩展:
extension FriendBase: Decodable { init(from decoder: Decoder) throws { let value = try decoder.container(keyedBy: CodingKeys.self) id = try value.decode(UUID.self, forKey: .id) firstName = try value.decode(String.self, forKey: .firstName) // 先解码Location(若存在),再转换为CLLocation if let locationModel = try value.decodeIfPresent(Location.self, forKey: .lastKnownLocation) { lastKnownLocation = CLLocation(model: locationModel) } } }
方案2:直接让CLLocation遵循Decodable(更简洁)
可以省去中间的Location结构体,直接为CLLocation实现Decodable协议:
extension CLLocation: Decodable { enum CodingKeys: String, CodingKey { case latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course, timestamp } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let latitude = try container.decode(CLLocationDegrees.self, forKey: .latitude) let longitude = try container.decode(CLLocationDegrees.self, forKey: .longitude) let altitude = try container.decode(CLLocationDistance.self, forKey: .altitude) let horizontalAccuracy = try container.decode(CLLocationAccuracy.self, forKey: .horizontalAccuracy) let verticalAccuracy = try container.decode(CLLocationAccuracy.self, forKey: .verticalAccuracy) let speed = try container.decode(CLLocationSpeed.self, forKey: .speed) let course = try container.decode(CLLocationDirection.self, forKey: .course) let timestamp = try container.decode(Date.self, forKey: .timestamp) self.init(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude), altitude: altitude, horizontalAccuracy: horizontalAccuracy, verticalAccuracy: verticalAccuracy, course: course, speed: speed, timestamp: timestamp) } }
此时FriendBase的解码代码可简化为:
extension FriendBase: Decodable { init(from decoder: Decoder) throws { let value = try decoder.container(keyedBy: CodingKeys.self) id = try value.decode(UUID.self, forKey: .id) firstName = try value.decode(String.self, forKey: .firstName) lastKnownLocation = try value.decodeIfPresent(CLLocation.self, forKey: .lastKnownLocation) } }
内容的提问来源于stack exchange,提问作者user21104171
相关产品推荐
相关产品推荐

