Skip to content

Instantly share code, notes, and snippets.

@hishma
Created January 16, 2019 23:57
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hishma/7be2361888859e94cd0a898bb33c1383 to your computer and use it in GitHub Desktop.
Save hishma/7be2361888859e94cd0a898bb33c1383 to your computer and use it in GitHub Desktop.
CoreLocation and Codable
extension CLLocation: Encodable {
public 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)
}
}
public struct LocationWrapper: Decodable {
var location: CLLocation
init(location: CLLocation) {
self.location = location
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CLLocation.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)
let location = CLLocation(coordinate: CLLocationCoordinate2DMake(latitude, longitude), altitude: altitude, horizontalAccuracy: horizontalAccuracy, verticalAccuracy: verticalAccuracy, course: course, speed: speed, timestamp: timestamp)
self.init(location: location)
}
}
@yoni-placer
Copy link

For iOS 15:

import CoreLocation

extension CLLocation: Encodable {
    public enum CodingKeys: String, CodingKey {
        case latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course, timestamp, speedAccuracy, courseAccuracy, sourceInformation
    }
    
    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)
        try container.encode(speedAccuracy, forKey: .speedAccuracy)
        if #available(iOS 13.4, *) {
            try container.encode(courseAccuracy, forKey: .courseAccuracy)
            try container.encode(courseAccuracy, forKey: .sourceInformation)
        }
        if #available(iOS 15.0, *) {
            try container.encode(sourceInformation, forKey: .sourceInformation)
        }
    }
}

public struct LocationWrapper: Decodable {
    var location: CLLocation
    
    init(location: CLLocation) {
        self.location = location
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CLLocation.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)

        var location = CLLocation(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
                                  altitude: altitude,
                                  horizontalAccuracy: horizontalAccuracy,
                                  verticalAccuracy: verticalAccuracy,
                                  course: course,
                                  speed: speed,
                                  timestamp: timestamp)

        if #available(iOS 15.0, *) {
            let courseAccuracy = try container.decode(CLLocationDirectionAccuracy.self, forKey: .courseAccuracy)
            let speedAccuracy = try container.decode(CLLocationSpeedAccuracy.self, forKey: .speedAccuracy)
            let sourceInfoWrapper = try? container.decode(CLLocationSourceInformationWrapper.self, forKey: .sourceInformation)
            
            if let sourceInfoWrapper = sourceInfoWrapper {
            location = CLLocation(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
                                  altitude: altitude,
                                  horizontalAccuracy: horizontalAccuracy,
                                  verticalAccuracy: verticalAccuracy,
                                  course: course,
                                  courseAccuracy: courseAccuracy,
                                  speed: speed,
                                  speedAccuracy: speedAccuracy,
                                  timestamp: timestamp,
                                  sourceInfo: sourceInfoWrapper.sourceInfo)
            }
        } else if #available(iOS 13.4, *) {
            let courseAccuracy = try container.decode(CLLocationDirectionAccuracy.self, forKey: .courseAccuracy)
            let speedAccuracy = try container.decode(CLLocationSpeedAccuracy.self, forKey: .speedAccuracy)

            location = CLLocation(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
                                  altitude: altitude,
                                  horizontalAccuracy: horizontalAccuracy,
                                  verticalAccuracy: verticalAccuracy,
                                  course: course,
                                  courseAccuracy: courseAccuracy,
                                  speed: speed,
                                  speedAccuracy: speedAccuracy,
                                  timestamp: timestamp)
        }
        self.init(location: location)
    }
}

@available(iOS 15.0, *)
extension CLLocationSourceInformation: Encodable {

    enum CodingKeys: String, CodingKey {
        case isSimulatedBySoftware, isProducedByAccessory
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(isSimulatedBySoftware, forKey: .isSimulatedBySoftware)
        try container.encode(isProducedByAccessory, forKey: .isProducedByAccessory)
    }
}

@available(iOS 15.0, *)
public struct CLLocationSourceInformationWrapper: Decodable {
    var sourceInfo: CLLocationSourceInformation
    
    init(sourceInfo: CLLocationSourceInformation) {
        self.sourceInfo = sourceInfo
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CLLocationSourceInformation.CodingKeys.self)
        
        let isSimulatedBySoftware = try container.decode(Bool.self, forKey: .isSimulatedBySoftware)
        let isProducedByAccessory = try container.decode(Bool.self, forKey: .isProducedByAccessory)
        
        let sourceInfo = CLLocationSourceInformation(softwareSimulationState: isSimulatedBySoftware, andExternalAccessoryState: isProducedByAccessory)

        self.init(sourceInfo: sourceInfo)
    }
}

@hishma
Copy link
Author

hishma commented May 3, 2022

👍

@andriy-appuchino
Copy link

andriy-appuchino commented Aug 10, 2022

if let sourceInfoWrapper = sourceInfoWrapper {
location = CLLocation(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
altitude: altitude,
horizontalAccuracy: horizontalAccuracy,
verticalAccuracy: verticalAccuracy,
course: course,
courseAccuracy: courseAccuracy,
speed: speed,
speedAccuracy: speedAccuracy,
timestamp: timestamp,
sourceInfo: sourceInfoWrapper.sourceInfo)
}

@yoni-placer There is a potential error here. If the sourceInfoWrapper is nil, the courseAccuracy and the speedAccuracy will not be initialized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment