From 30fea27e36f649b3d44b9ea0eea58881579d48a3 Mon Sep 17 00:00:00 2001 From: Dan Leedham Date: Sun, 21 Jun 2026 21:26:33 +0100 Subject: [PATCH 1/9] Update to Swift 6.2 Concurrency update (Sendable) --- Package.swift | 2 +- Sources/ForeFlightKML/Styles/Colors.swift | 2 +- Sources/ForeFlightKML/Styles/IconStyle.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index 03bc596..555083c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:6.2 import PackageDescription let package = Package( diff --git a/Sources/ForeFlightKML/Styles/Colors.swift b/Sources/ForeFlightKML/Styles/Colors.swift index 5ef3882..8fd8750 100644 --- a/Sources/ForeFlightKML/Styles/Colors.swift +++ b/Sources/ForeFlightKML/Styles/Colors.swift @@ -1,6 +1,6 @@ import Foundation -public struct KMLColor: Equatable, CustomStringConvertible { +public struct KMLColor: Equatable, CustomStringConvertible, Sendable { public var description: String { kmlHexString } /// Single internal storage: bits [31..24]=A, [23..16]=R, [15..8]=G, [7..0]=B diff --git a/Sources/ForeFlightKML/Styles/IconStyle.swift b/Sources/ForeFlightKML/Styles/IconStyle.swift index dc80caf..ef1b435 100644 --- a/Sources/ForeFlightKML/Styles/IconStyle.swift +++ b/Sources/ForeFlightKML/Styles/IconStyle.swift @@ -102,7 +102,7 @@ public enum PredefinedIconType: String, Codable { /// Icon shapes that support custom colors. /// These are the "shapes" style icons that can be any color via KML's color tag. -public enum CustomIconType: Codable { +public enum CustomIconType: Codable, Sendable { case opendiamond case triangle case forbidden From 495d785122fdf952b993cc05e47cc0059b9fbb2e Mon Sep 17 00:00:00 2001 From: Dan Leedham Date: Wed, 21 Oct 2026 09:42:25 +0100 Subject: [PATCH 2/9] Move to Vincenty from Spherical --- .../xcshareddata/swiftpm/Package.resolved | 8 +++--- .../ForeFlightKMLDemo/KML/KMLGenerator.swift | 1 - .../Map/MapViewRepresentable.swift | 1 - Package.resolved | 11 ++++---- Package.swift | 4 +-- README.md | 3 +-- .../CoreElements/LineString.swift | 2 +- .../CoreElements/LinearRing.swift | 2 +- .../ForeFlightKML/CoreElements/Point.swift | 2 +- .../ForeFlightKML+Convenience.swift | 2 +- Sources/ForeFlightKML/ForeFlightKML.swift | 2 +- .../Geometry/Circle/LineCircle.swift | 2 +- .../Geometry/Circle/PolygonCircle.swift | 2 +- .../Geometry/Segment/LineSector.swift | 2 +- .../Segment/PolygonAnnularSector.swift | 2 +- .../Geometry/Segment/PolygonSector.swift | 2 +- .../Geometry/Shared/CircleGeometry.swift | 2 +- .../ForeFlightKML/Geometry/Shared/Line.swift | 2 +- .../Geometry/Shared/SectorGeometry.swift | 2 +- .../Models/CoordinateContainer.swift | 2 +- Sources/ForeFlightKML/Models/Placemark.swift | 2 +- ...Geodesy+KML.swift => Coordinate+KML.swift} | 25 +++++++++++++++---- .../Builder/ForeFlightKML+BuildTests.swift | 1 - .../Builder/ForeFlightKML+KMZTests.swift | 1 - .../Builder/ForeFlightKMLTests.swift | 1 - .../CoreElementTests/LineStringTests.swift | 1 - .../CoreElementTests/LinearRingTests.swift | 1 - .../CoreElementTests/PointTests.swift | 1 - .../CoreElementTests/PolygonTests.swift | 1 - .../GeometryTests/LineCircleTests.swift | 1 - .../GeometryTests/LineSectorTests.swift | 1 - .../GeometryTests/LineTests.swift | 1 - .../PolygonAnnularSectorTests.swift | 1 - .../GeometryTests/PolygonCircleTests.swift | 1 - .../GeometryTests/PolygonSegmentTests.swift | 1 - .../ModelTests/AltitudeTests.swift | 1 - .../ModelTests/CoordinateContainerTests.swift | 1 - .../Geometry/GeometryStyleTests.swift | 1 - .../StyleTests/Geometry/PointStyleTests.swift | 1 - .../StyleTests/IconStyle+HiddenTests.swift | 1 - .../StyleTests/SubStyleTests.swift | 1 - .../Utils/CoordinateFormattingTests.swift | 1 - .../Utils/TestHelpers.swift | 1 - .../UserMapShapesSampleFullTest.swift | 1 - .../UserMapShapesSampleIndividualTests.swift | 1 - 45 files changed, 48 insertions(+), 58 deletions(-) rename Sources/ForeFlightKML/Utils/{Geodesy+KML.swift => Coordinate+KML.swift} (79%) diff --git a/Example/ForeFlightKMLDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ForeFlightKMLDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 36f24aa..d417bbb 100644 --- a/Example/ForeFlightKMLDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ForeFlightKMLDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -2,12 +2,12 @@ "originHash" : "9cb73a7eb66a94e9ff3b48c0d33c79d885ba74172d84e80b923fabe67536dcb9", "pins" : [ { - "identity" : "geodesy", + "identity" : "vincenty", "kind" : "remoteSourceControl", - "location" : "https://github.com/florianreinhart/Geodesy", + "location" : "https://github.com/Haelix-Code/Vincenty", "state" : { - "revision" : "c72d7ea459c6eee4d041272c61f84df61d850091", - "version" : "0.2.2" + "revision" : "e029255ef3313dcf77cecb4faf64cf94a0812608", + "version" : "0.0.1" } }, { diff --git a/Example/ForeFlightKMLDemo/KML/KMLGenerator.swift b/Example/ForeFlightKMLDemo/KML/KMLGenerator.swift index 573307a..2f37688 100644 --- a/Example/ForeFlightKMLDemo/KML/KMLGenerator.swift +++ b/Example/ForeFlightKMLDemo/KML/KMLGenerator.swift @@ -1,5 +1,4 @@ import Foundation -import GeodesySpherical import CoreLocation import ForeFlightKML diff --git a/Example/ForeFlightKMLDemo/Map/MapViewRepresentable.swift b/Example/ForeFlightKMLDemo/Map/MapViewRepresentable.swift index d54fe99..6d56d10 100644 --- a/Example/ForeFlightKMLDemo/Map/MapViewRepresentable.swift +++ b/Example/ForeFlightKMLDemo/Map/MapViewRepresentable.swift @@ -1,7 +1,6 @@ import SwiftUI import MapKit import CoreLocation -import GeodesySpherical import ForeFlightKML struct MapViewRepresentable: UIViewRepresentable { diff --git a/Package.resolved b/Package.resolved index a95a72e..ed5a83d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,12 +1,13 @@ { + "originHash" : "17e50782305f6c29b65dd5cb050f0836dda7428e80849631e5782b62be7267e0", "pins" : [ { - "identity" : "geodesy", + "identity" : "vincenty", "kind" : "remoteSourceControl", - "location" : "https://github.com/florianreinhart/Geodesy", + "location" : "https://github.com/Haelix-Code/Vincenty", "state" : { - "revision" : "c72d7ea459c6eee4d041272c61f84df61d850091", - "version" : "0.2.2" + "revision" : "e029255ef3313dcf77cecb4faf64cf94a0812608", + "version" : "0.0.1" } }, { @@ -19,5 +20,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index 03bc596..f0968ba 100644 --- a/Package.swift +++ b/Package.swift @@ -11,14 +11,14 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/florianreinhart/Geodesy", .upToNextMajor(from: "0.2.2")), + .package(url: "https://github.com/Haelix-Code/Vincenty", .upToNextMinor(from: "0.0.1")), .package(url: "https://github.com/weichsel/ZIPFoundation.git", from: "0.9.0") ], targets: [ .target( name: "ForeFlightKML", dependencies: [ - .product(name: "Geodesy", package: "Geodesy"), + .product(name: "Vincenty", package: "Vincenty"), .product(name: "ZIPFoundation", package: "ZIPFoundation") ], resources: [ diff --git a/README.md b/README.md index ce0dc48..fe3af93 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ See `/Tests/UserMapShapeTests/UserMapShapesSampleFullTest.swift` ```swift import ForeFlightKML -import GeodesySpherical let builder = ForeFlightKMLBuilder(documentName: "Airport with ATZ") @@ -126,7 +125,7 @@ Full public API surface is visible in the package sources. ## Notes, conventions and gotchas -- **Coordinates order**: KML requires `longitude,latitude[,altitude]`. The public API accepts `Coordinate(latitude:..., longitude:...)` (from `Geodesy`) and the framework emits coordinates in the KML `lon,lat[,alt]` order. +- **Coordinates order**: KML requires `longitude,latitude[,altitude]`. The public API accepts `Coordinate(latitude:..., longitude:...)` (a typealias for [Vincenty](https://github.com/Haelix-Code/Vincenty)'s `GeodeticCoordinate`) and the framework emits coordinates in the KML `lon,lat[,alt]` order. - **Units**: Distances (e.g. `LineCircle.radius`) are in **meters**. - **Angles/bearings**: bearings (for arc & circle generation) are interpreted in degrees (0..360). The bearing convention is clockwise from north. - **Altitude**: When you provide altitudes, the `AltitudeMode` is emitted (defaults to `.absolute` in most geometries). diff --git a/Sources/ForeFlightKML/CoreElements/LineString.swift b/Sources/ForeFlightKML/CoreElements/LineString.swift index 069a689..1c215b9 100644 --- a/Sources/ForeFlightKML/CoreElements/LineString.swift +++ b/Sources/ForeFlightKML/CoreElements/LineString.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// A line string connecting multiple coordinates in order. /// Used for routes, boundaries, or any connected line geometry. diff --git a/Sources/ForeFlightKML/CoreElements/LinearRing.swift b/Sources/ForeFlightKML/CoreElements/LinearRing.swift index a799b4e..283bb23 100644 --- a/Sources/ForeFlightKML/CoreElements/LinearRing.swift +++ b/Sources/ForeFlightKML/CoreElements/LinearRing.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// Defines a closed line string, typically the outer boundary of a Polygon. /// Optionally, a LinearRing can also be used as the inner boundary of a Polygon to create holes in the Polygon. diff --git a/Sources/ForeFlightKML/CoreElements/Point.swift b/Sources/ForeFlightKML/CoreElements/Point.swift index e121bb0..e6317ee 100644 --- a/Sources/ForeFlightKML/CoreElements/Point.swift +++ b/Sources/ForeFlightKML/CoreElements/Point.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// A geographic point in 3D space that can be displayed as a marker in ForeFlight. /// diff --git a/Sources/ForeFlightKML/ForeFlightKML+Convenience.swift b/Sources/ForeFlightKML/ForeFlightKML+Convenience.swift index 23effa2..39aa9b7 100644 --- a/Sources/ForeFlightKML/ForeFlightKML+Convenience.swift +++ b/Sources/ForeFlightKML/ForeFlightKML+Convenience.swift @@ -1,5 +1,5 @@ import Foundation -import GeodesySpherical +import Vincenty extension ForeFlightKMLBuilder { diff --git a/Sources/ForeFlightKML/ForeFlightKML.swift b/Sources/ForeFlightKML/ForeFlightKML.swift index 0e6409a..25f5f61 100644 --- a/Sources/ForeFlightKML/ForeFlightKML.swift +++ b/Sources/ForeFlightKML/ForeFlightKML.swift @@ -1,5 +1,5 @@ import Foundation -import GeodesySpherical +import Vincenty /// A Builder for composing a KML Document (styles + placemarks). /// Use this to create KML documents compatible with ForeFlight's User Map Shapes feature. diff --git a/Sources/ForeFlightKML/Geometry/Circle/LineCircle.swift b/Sources/ForeFlightKML/Geometry/Circle/LineCircle.swift index a3eafba..a1736b3 100644 --- a/Sources/ForeFlightKML/Geometry/Circle/LineCircle.swift +++ b/Sources/ForeFlightKML/Geometry/Circle/LineCircle.swift @@ -1,5 +1,5 @@ import Foundation -import GeodesySpherical +import Vincenty /// A circular line geometry approximated with multiple line segments. /// diff --git a/Sources/ForeFlightKML/Geometry/Circle/PolygonCircle.swift b/Sources/ForeFlightKML/Geometry/Circle/PolygonCircle.swift index 34a4322..ee27bdd 100644 --- a/Sources/ForeFlightKML/Geometry/Circle/PolygonCircle.swift +++ b/Sources/ForeFlightKML/Geometry/Circle/PolygonCircle.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// A circular polygon geometry approximated with multiple line segments. /// diff --git a/Sources/ForeFlightKML/Geometry/Segment/LineSector.swift b/Sources/ForeFlightKML/Geometry/Segment/LineSector.swift index b1ba148..9c6bd62 100644 --- a/Sources/ForeFlightKML/Geometry/Segment/LineSector.swift +++ b/Sources/ForeFlightKML/Geometry/Segment/LineSector.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// An arc or sector geometry (like a pie slice) defined by center, radius, and angular extents. /// diff --git a/Sources/ForeFlightKML/Geometry/Segment/PolygonAnnularSector.swift b/Sources/ForeFlightKML/Geometry/Segment/PolygonAnnularSector.swift index 70dbb68..0ffd1e2 100644 --- a/Sources/ForeFlightKML/Geometry/Segment/PolygonAnnularSector.swift +++ b/Sources/ForeFlightKML/Geometry/Segment/PolygonAnnularSector.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty public struct PolygonAnnularSector: KMLElement, AltitudeSupport { let polygon: Polygon diff --git a/Sources/ForeFlightKML/Geometry/Segment/PolygonSector.swift b/Sources/ForeFlightKML/Geometry/Segment/PolygonSector.swift index 72c021e..71dadff 100644 --- a/Sources/ForeFlightKML/Geometry/Segment/PolygonSector.swift +++ b/Sources/ForeFlightKML/Geometry/Segment/PolygonSector.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty public struct PolygonSector: KMLElement, AltitudeSupport { let polygon: Polygon diff --git a/Sources/ForeFlightKML/Geometry/Shared/CircleGeometry.swift b/Sources/ForeFlightKML/Geometry/Shared/CircleGeometry.swift index 68b46cb..1af834e 100644 --- a/Sources/ForeFlightKML/Geometry/Shared/CircleGeometry.swift +++ b/Sources/ForeFlightKML/Geometry/Shared/CircleGeometry.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty internal enum CircleGeometry { diff --git a/Sources/ForeFlightKML/Geometry/Shared/Line.swift b/Sources/ForeFlightKML/Geometry/Shared/Line.swift index dc94257..01d13a4 100644 --- a/Sources/ForeFlightKML/Geometry/Shared/Line.swift +++ b/Sources/ForeFlightKML/Geometry/Shared/Line.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// A simple line connecting multiple coordinates in sequence. /// diff --git a/Sources/ForeFlightKML/Geometry/Shared/SectorGeometry.swift b/Sources/ForeFlightKML/Geometry/Shared/SectorGeometry.swift index 57c670c..bfe6e37 100644 --- a/Sources/ForeFlightKML/Geometry/Shared/SectorGeometry.swift +++ b/Sources/ForeFlightKML/Geometry/Shared/SectorGeometry.swift @@ -1,5 +1,5 @@ import Foundation -import GeodesySpherical +import Vincenty internal enum SectorGeometry { static func generateSectorPoints( diff --git a/Sources/ForeFlightKML/Models/CoordinateContainer.swift b/Sources/ForeFlightKML/Models/CoordinateContainer.swift index 06d2cd4..5306e5d 100644 --- a/Sources/ForeFlightKML/Models/CoordinateContainer.swift +++ b/Sources/ForeFlightKML/Models/CoordinateContainer.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// Protocol for KML elements that contain coordinate arrays and support altitude/tessellation. /// Used by LineString, LinearRing, and similar coordinate-based geometries. diff --git a/Sources/ForeFlightKML/Models/Placemark.swift b/Sources/ForeFlightKML/Models/Placemark.swift index ff1032c..b17a9d8 100644 --- a/Sources/ForeFlightKML/Models/Placemark.swift +++ b/Sources/ForeFlightKML/Models/Placemark.swift @@ -1,4 +1,4 @@ -import GeodesySpherical +import Vincenty /// Represents a Feature Placemark that combines geometry with styling and metadata. /// diff --git a/Sources/ForeFlightKML/Utils/Geodesy+KML.swift b/Sources/ForeFlightKML/Utils/Coordinate+KML.swift similarity index 79% rename from Sources/ForeFlightKML/Utils/Geodesy+KML.swift rename to Sources/ForeFlightKML/Utils/Coordinate+KML.swift index 352c0ea..20759fb 100644 --- a/Sources/ForeFlightKML/Utils/Geodesy+KML.swift +++ b/Sources/ForeFlightKML/Utils/Coordinate+KML.swift @@ -1,5 +1,6 @@ import Foundation -import GeodesySpherical +// Re-exported so `Coordinate` is fully usable with just `import ForeFlightKML`. +@_exported import Vincenty // KML uses 3D geographic coordinates: longitude, latitude and altitude, in that order. // The longitude and latitude components are in decimal degrees @@ -39,7 +40,21 @@ internal func formatCoordinate(_ value: Double, precision: Int) -> String { return String(formatted[formatted.startIndex.. Coordinate { + guard let solution = try? Ellipsoid.wgs84.direct(from: self, distance: distance, initialBearing: bearing) else { + return Coordinate(latitude: .nan, longitude: .nan) + } + return solution.destination + } + /// Format coordinate as KML `longitude,latitude` string with given precision. /// Trailing zeros are trimmed (e.g. `2.0` not `2.00000000`). public func kmlString(precision: Int = 8) -> String { @@ -68,7 +83,7 @@ internal struct Coordinate3D: Hashable { public let altitude: Double? public init( - latitude: GeodesySpherical.Degrees, longitude: GeodesySpherical.Degrees, + latitude: Double, longitude: Double, altitude: Double? = nil ) { self.coordinate = Coordinate(latitude: latitude, longitude: longitude) @@ -80,8 +95,8 @@ internal struct Coordinate3D: Hashable { self.altitude = altitude } - public var latitude: GeodesySpherical.Degrees { coordinate.latitude } - public var longitude: GeodesySpherical.Degrees { coordinate.longitude } + public var latitude: Double { coordinate.latitude } + public var longitude: Double { coordinate.longitude } public func kmlString(precision: Int = kDefaultCoordinatePrecision) -> String { let lon = formatCoordinate(self.longitude, precision: precision) diff --git a/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+BuildTests.swift b/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+BuildTests.swift index f95b2e6..4cc6501 100644 --- a/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+BuildTests.swift +++ b/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+BuildTests.swift @@ -1,5 +1,4 @@ import XCTest -import GeodesySpherical @testable import ForeFlightKML final class ForeFlightKMLBuildTests: XCTestCase { diff --git a/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+KMZTests.swift b/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+KMZTests.swift index 15ace04..0c3d2b9 100644 --- a/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+KMZTests.swift +++ b/Tests/ForeFlightKMLTests/Builder/ForeFlightKML+KMZTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import ForeFlightKML -import GeodesySpherical import ZIPFoundation final class KMZPackagingTests: XCTestCase { diff --git a/Tests/ForeFlightKMLTests/Builder/ForeFlightKMLTests.swift b/Tests/ForeFlightKMLTests/Builder/ForeFlightKMLTests.swift index 0f49cef..cd0ea96 100644 --- a/Tests/ForeFlightKMLTests/Builder/ForeFlightKMLTests.swift +++ b/Tests/ForeFlightKMLTests/Builder/ForeFlightKMLTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import Foundation import XCTest diff --git a/Tests/ForeFlightKMLTests/CoreElementTests/LineStringTests.swift b/Tests/ForeFlightKMLTests/CoreElementTests/LineStringTests.swift index bc688b0..2bd8a37 100644 --- a/Tests/ForeFlightKMLTests/CoreElementTests/LineStringTests.swift +++ b/Tests/ForeFlightKMLTests/CoreElementTests/LineStringTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/CoreElementTests/LinearRingTests.swift b/Tests/ForeFlightKMLTests/CoreElementTests/LinearRingTests.swift index f7cd999..9dda9ed 100644 --- a/Tests/ForeFlightKMLTests/CoreElementTests/LinearRingTests.swift +++ b/Tests/ForeFlightKMLTests/CoreElementTests/LinearRingTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/CoreElementTests/PointTests.swift b/Tests/ForeFlightKMLTests/CoreElementTests/PointTests.swift index c7fa454..3be12d3 100644 --- a/Tests/ForeFlightKMLTests/CoreElementTests/PointTests.swift +++ b/Tests/ForeFlightKMLTests/CoreElementTests/PointTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/CoreElementTests/PolygonTests.swift b/Tests/ForeFlightKMLTests/CoreElementTests/PolygonTests.swift index 1f7ceea..1ea12af 100644 --- a/Tests/ForeFlightKMLTests/CoreElementTests/PolygonTests.swift +++ b/Tests/ForeFlightKMLTests/CoreElementTests/PolygonTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/GeometryTests/LineCircleTests.swift b/Tests/ForeFlightKMLTests/GeometryTests/LineCircleTests.swift index 420a48c..b032b96 100644 --- a/Tests/ForeFlightKMLTests/GeometryTests/LineCircleTests.swift +++ b/Tests/ForeFlightKMLTests/GeometryTests/LineCircleTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/GeometryTests/LineSectorTests.swift b/Tests/ForeFlightKMLTests/GeometryTests/LineSectorTests.swift index 407b5af..db52114 100644 --- a/Tests/ForeFlightKMLTests/GeometryTests/LineSectorTests.swift +++ b/Tests/ForeFlightKMLTests/GeometryTests/LineSectorTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/GeometryTests/LineTests.swift b/Tests/ForeFlightKMLTests/GeometryTests/LineTests.swift index 0ae09e8..ab183f3 100644 --- a/Tests/ForeFlightKMLTests/GeometryTests/LineTests.swift +++ b/Tests/ForeFlightKMLTests/GeometryTests/LineTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/GeometryTests/PolygonAnnularSectorTests.swift b/Tests/ForeFlightKMLTests/GeometryTests/PolygonAnnularSectorTests.swift index 6564783..bb00191 100644 --- a/Tests/ForeFlightKMLTests/GeometryTests/PolygonAnnularSectorTests.swift +++ b/Tests/ForeFlightKMLTests/GeometryTests/PolygonAnnularSectorTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/GeometryTests/PolygonCircleTests.swift b/Tests/ForeFlightKMLTests/GeometryTests/PolygonCircleTests.swift index 488aab6..4d07436 100644 --- a/Tests/ForeFlightKMLTests/GeometryTests/PolygonCircleTests.swift +++ b/Tests/ForeFlightKMLTests/GeometryTests/PolygonCircleTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/GeometryTests/PolygonSegmentTests.swift b/Tests/ForeFlightKMLTests/GeometryTests/PolygonSegmentTests.swift index e525f88..0cb6d77 100644 --- a/Tests/ForeFlightKMLTests/GeometryTests/PolygonSegmentTests.swift +++ b/Tests/ForeFlightKMLTests/GeometryTests/PolygonSegmentTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/ModelTests/AltitudeTests.swift b/Tests/ForeFlightKMLTests/ModelTests/AltitudeTests.swift index 845a476..9e7299f 100644 --- a/Tests/ForeFlightKMLTests/ModelTests/AltitudeTests.swift +++ b/Tests/ForeFlightKMLTests/ModelTests/AltitudeTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/ModelTests/CoordinateContainerTests.swift b/Tests/ForeFlightKMLTests/ModelTests/CoordinateContainerTests.swift index 58ce72c..f0933ba 100644 --- a/Tests/ForeFlightKMLTests/ModelTests/CoordinateContainerTests.swift +++ b/Tests/ForeFlightKMLTests/ModelTests/CoordinateContainerTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/StyleTests/Geometry/GeometryStyleTests.swift b/Tests/ForeFlightKMLTests/StyleTests/Geometry/GeometryStyleTests.swift index 44dfe59..0f54bf1 100644 --- a/Tests/ForeFlightKMLTests/StyleTests/Geometry/GeometryStyleTests.swift +++ b/Tests/ForeFlightKMLTests/StyleTests/Geometry/GeometryStyleTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/StyleTests/Geometry/PointStyleTests.swift b/Tests/ForeFlightKMLTests/StyleTests/Geometry/PointStyleTests.swift index 7683582..684687c 100644 --- a/Tests/ForeFlightKMLTests/StyleTests/Geometry/PointStyleTests.swift +++ b/Tests/ForeFlightKMLTests/StyleTests/Geometry/PointStyleTests.swift @@ -1,5 +1,4 @@ import XCTest -import GeodesySpherical @testable import ForeFlightKML final class PointStylesTests: XCTestCase { diff --git a/Tests/ForeFlightKMLTests/StyleTests/IconStyle+HiddenTests.swift b/Tests/ForeFlightKMLTests/StyleTests/IconStyle+HiddenTests.swift index 0d8e27c..31a8e36 100644 --- a/Tests/ForeFlightKMLTests/StyleTests/IconStyle+HiddenTests.swift +++ b/Tests/ForeFlightKMLTests/StyleTests/IconStyle+HiddenTests.swift @@ -1,6 +1,5 @@ import XCTest @testable import ForeFlightKML -import GeodesySpherical final class LabelOnlyTests: XCTestCase { diff --git a/Tests/ForeFlightKMLTests/StyleTests/SubStyleTests.swift b/Tests/ForeFlightKMLTests/StyleTests/SubStyleTests.swift index 0e47911..3faf3a5 100644 --- a/Tests/ForeFlightKMLTests/StyleTests/SubStyleTests.swift +++ b/Tests/ForeFlightKMLTests/StyleTests/SubStyleTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/ForeFlightKMLTests/Utils/CoordinateFormattingTests.swift b/Tests/ForeFlightKMLTests/Utils/CoordinateFormattingTests.swift index aaa7a55..c55af46 100644 --- a/Tests/ForeFlightKMLTests/Utils/CoordinateFormattingTests.swift +++ b/Tests/ForeFlightKMLTests/Utils/CoordinateFormattingTests.swift @@ -1,5 +1,4 @@ import XCTest -import GeodesySpherical @testable import ForeFlightKML final class CoordinateFormattingTests: XCTestCase { diff --git a/Tests/ForeFlightKMLTests/Utils/TestHelpers.swift b/Tests/ForeFlightKMLTests/Utils/TestHelpers.swift index 550670b..bcc40a5 100644 --- a/Tests/ForeFlightKMLTests/Utils/TestHelpers.swift +++ b/Tests/ForeFlightKMLTests/Utils/TestHelpers.swift @@ -1,5 +1,4 @@ import XCTest -import GeodesySpherical @testable import ForeFlightKML class XMLTestHelper { diff --git a/Tests/UserMapShapeTests/UserMapShapesSampleFullTest.swift b/Tests/UserMapShapeTests/UserMapShapesSampleFullTest.swift index 7d9f4ed..e9783b3 100644 --- a/Tests/UserMapShapeTests/UserMapShapesSampleFullTest.swift +++ b/Tests/UserMapShapeTests/UserMapShapesSampleFullTest.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML diff --git a/Tests/UserMapShapeTests/UserMapShapesSampleIndividualTests.swift b/Tests/UserMapShapeTests/UserMapShapesSampleIndividualTests.swift index 20196d1..0131d3b 100644 --- a/Tests/UserMapShapeTests/UserMapShapesSampleIndividualTests.swift +++ b/Tests/UserMapShapeTests/UserMapShapesSampleIndividualTests.swift @@ -1,4 +1,3 @@ -import GeodesySpherical import XCTest @testable import ForeFlightKML From 8a314ccab4545f041d45d02b380f23156cffe976 Mon Sep 17 00:00:00 2001 From: Dan Leedham Date: Thu, 22 Oct 2026 17:35:19 +0100 Subject: [PATCH 3/9] Inherit Sendable --- Sources/ForeFlightKML/Enums/OutputFormat.swift | 2 +- Sources/ForeFlightKML/ForeFlightKML.swift | 9 +++++---- Sources/ForeFlightKML/Models/AltitudeSupport.swift | 2 +- Sources/ForeFlightKML/Models/Placemark.swift | 2 +- Sources/ForeFlightKML/Styles/IconStyle.swift | 4 ++-- Sources/ForeFlightKML/Utils/DataFormat.swift | 2 +- Sources/ForeFlightKML/Utils/Protocols.swift | 6 +++--- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Sources/ForeFlightKML/Enums/OutputFormat.swift b/Sources/ForeFlightKML/Enums/OutputFormat.swift index 5ead170..638f01e 100644 --- a/Sources/ForeFlightKML/Enums/OutputFormat.swift +++ b/Sources/ForeFlightKML/Enums/OutputFormat.swift @@ -1,4 +1,4 @@ -public enum OutputFormat { +public enum OutputFormat: Sendable { case kml case kmz } diff --git a/Sources/ForeFlightKML/ForeFlightKML.swift b/Sources/ForeFlightKML/ForeFlightKML.swift index 25f5f61..024c9aa 100644 --- a/Sources/ForeFlightKML/ForeFlightKML.swift +++ b/Sources/ForeFlightKML/ForeFlightKML.swift @@ -4,10 +4,11 @@ import Vincenty /// A Builder for composing a KML Document (styles + placemarks). /// Use this to create KML documents compatible with ForeFlight's User Map Shapes feature. /// -/// - Note: Marked `@unchecked Sendable` because instances are intended -/// to be created, populated, and built within a single scope (not shared across threads). -/// This allows callers to use the builder from any actor/thread context. -public final class ForeFlightKMLBuilder: Building, @unchecked Sendable { +/// - Note: Not `Sendable`: the builder holds unsynchronised mutable state, so create, populate +/// and build it within a single scope. It can still be handed off to another task or actor +/// as long as the sender stops using it. The `Placemark`s and styles it takes, and the +/// `BuildResult` it produces, are all `Sendable`. +public final class ForeFlightKMLBuilder: Building { /// Optional name for the `` element. private var documentName: String? /// Maximum decimal places for coordinate values. Trailing zeros are trimmed, diff --git a/Sources/ForeFlightKML/Models/AltitudeSupport.swift b/Sources/ForeFlightKML/Models/AltitudeSupport.swift index 2a6da4f..68c153b 100644 --- a/Sources/ForeFlightKML/Models/AltitudeSupport.swift +++ b/Sources/ForeFlightKML/Models/AltitudeSupport.swift @@ -18,7 +18,7 @@ extension AltitudeSupport { /// Supported Altitude Modes per KML 2.2 specification /// See: https://developers.google.com/kml/documentation/kmlreference#altitudemode -public enum AltitudeMode: String, Equatable { +public enum AltitudeMode: String, Equatable, Sendable { case absolute case clampToGround case relativeToGround diff --git a/Sources/ForeFlightKML/Models/Placemark.swift b/Sources/ForeFlightKML/Models/Placemark.swift index b17a9d8..6cf5990 100644 --- a/Sources/ForeFlightKML/Models/Placemark.swift +++ b/Sources/ForeFlightKML/Models/Placemark.swift @@ -7,7 +7,7 @@ import Vincenty /// - Optional name that appears in ForeFlight /// - Optional style that controls visual appearance /// -public struct Placemark { +public struct Placemark: Sendable { public private(set) var name: String? public private(set) var geometry: KMLElement public private(set) var styleUrl: String? diff --git a/Sources/ForeFlightKML/Styles/IconStyle.swift b/Sources/ForeFlightKML/Styles/IconStyle.swift index ef1b435..9391281 100644 --- a/Sources/ForeFlightKML/Styles/IconStyle.swift +++ b/Sources/ForeFlightKML/Styles/IconStyle.swift @@ -93,7 +93,7 @@ public struct IconStyle: KMLSubStyle { /// Icon shapes that work with Google's predefined color system. /// These are the "paddle" style icons that only support fixed colors. -public enum PredefinedIconType: String, Codable { +public enum PredefinedIconType: String, Codable, Sendable { case pushpin case circle case square @@ -155,7 +155,7 @@ public enum CustomIconType: Codable, Sendable { /// Predefined icon colors available for paddle-style icons. /// These are the standard colors provided by Google's KML icon set. -public enum DefinedIconColor: String, Codable { +public enum DefinedIconColor: String, Codable, Sendable { case purple = "purple" case white = "wht" case green = "grn" diff --git a/Sources/ForeFlightKML/Utils/DataFormat.swift b/Sources/ForeFlightKML/Utils/DataFormat.swift index bf80777..0df55b5 100644 --- a/Sources/ForeFlightKML/Utils/DataFormat.swift +++ b/Sources/ForeFlightKML/Utils/DataFormat.swift @@ -1,6 +1,6 @@ import Foundation -public struct BuildResult { +public struct BuildResult: Sendable { public let data: Data public let fileExtension: String public let mimetype: String diff --git a/Sources/ForeFlightKML/Utils/Protocols.swift b/Sources/ForeFlightKML/Utils/Protocols.swift index 379ca57..68e1b98 100644 --- a/Sources/ForeFlightKML/Utils/Protocols.swift +++ b/Sources/ForeFlightKML/Utils/Protocols.swift @@ -2,7 +2,7 @@ /// to a KML fragment string. /// /// Implementers must produce valid KML (as a `String`) for that element. -public protocol KMLElement { +public protocol KMLElement: Sendable { func kmlString() -> String /// Write KML directly into a mutable string buffer for better performance. /// Default implementation falls back to `kmlString()`. @@ -25,7 +25,7 @@ public extension KMLElement { /// Represents a top-level KML `Style` that has an id and a KML body. /// Use this to create reusable styles that placemarks can reference. -public protocol KMLStyle { +public protocol KMLStyle: Sendable { // Top-level style that must provide an id and full KML (usually a ) func id() -> String func kmlString() -> String @@ -44,7 +44,7 @@ public extension KMLStyle { /// Represents a style *sub-element* like `` or ``. /// These are embedded inside a top-level `