From 35554734fbe059660509ee9403ffa4fbfbc1051c Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Thu, 23 May 2024 13:20:25 +0200 Subject: [PATCH] #44: SwiftData compatibility (#47) --- README.md | 20 +++++- Sources/GISTools/GeoJson/GeoJsonCodable.swift | 38 ++++++++++++ .../GeoJson/FeatureCollectionTests.swift | 16 ++--- .../GISToolsTests/GeoJson/FeatureTests.swift | 12 ++-- .../GeoJson/GeometryCollectionTests.swift | 8 +-- .../GeoJson/LineStringTests.swift | 8 +-- .../GeoJson/MultiLineStringTests.swift | 8 +-- .../GeoJson/MultiPointTests.swift | 8 +-- .../GeoJson/MultiPolygonTests.swift | 8 +-- Tests/GISToolsTests/GeoJson/PointTests.swift | 8 +-- .../GISToolsTests/GeoJson/PolygonTests.swift | 16 ++--- .../GeoJson/SwiftDataTests.swift | 61 +++++++++++++++++++ 12 files changed, 164 insertions(+), 47 deletions(-) create mode 100644 Tests/GISToolsTests/GeoJson/SwiftDataTests.swift diff --git a/README.md b/README.md index c010ffc..0178fa8 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ targets: [ - Supports the full [GeoJSON standard][6], with some exceptions (see [TODO.md][7]) - Load and write GeoJSON objects from and to `[String:Any]`, `URL`, `Data` and `String` -- Supports `Codable` +- Supports `Codable` and `SwiftData` (see below) - Supports EPSG:3857 (web mercator) and EPSG:4326 (geodetic) conversions - Supports WKT/WKB, also with different projections - Spatial search with a R-tree @@ -655,6 +655,24 @@ func projected(to newProjection: Projection) -> FeatureCollection This type is somewhat special since its initializers will accept any valid GeoJSON object and return a `FeatureCollection` with the input wrapped in `Feature` objects if the input are geometries, or by collecting the input if it’s a `Feature`. +# SwiftData + +You need to use a transformer for using GeoJson with SwiftData (also have a look at the [SwiftData test cases](https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/GeoJson/SwiftDataTests.swift)). + +First, register the transformer like this: +```swift +GeoJsonTransformer.register() +``` + +Then create your models like this: +```swift +@Attribute(.transformable(by: GeoJsonTransformer.name.rawValue)) var geoJson: GeoJson? +@Attribute(.transformable(by: GeoJsonTransformer.name.rawValue)) var point: Point? +... +``` + +This is necessary because SwiftData doesn't work well with the default Codable implementation, so you need to do the serialization for yourself... + # WKB/WKT The following geometry types are supported: `point`, `linestring`, `linearring`, `polygon`, `multipoint`, `multilinestring`, `multipolygon`, `geometrycollection` and `triangle`. Please open an issue if you need more. diff --git a/Sources/GISTools/GeoJson/GeoJsonCodable.swift b/Sources/GISTools/GeoJson/GeoJsonCodable.swift index a4c979e..e22f1e8 100644 --- a/Sources/GISTools/GeoJson/GeoJsonCodable.swift +++ b/Sources/GISTools/GeoJson/GeoJsonCodable.swift @@ -89,6 +89,44 @@ extension Coordinate3D: Codable { } +// MARK: - SwiftData compatibility (see the README) + +#if canImport(SwiftData) +@objc(GeoJsonTransformer) +public final class GeoJsonTransformer: ValueTransformer { + + public static let name = NSValueTransformerName(rawValue: "GeoJsonTransformer") + + public static func register() { + ValueTransformer.setValueTransformer(GeoJsonTransformer(), forName: name) + } + + public override class func transformedValueClass() -> AnyClass { + // returns __SwiftValue + type(of: Point(Coordinate3D.zero) as AnyObject) + } + + public override class func allowsReverseTransformation() -> Bool { + true + } + + // Encode GeoJSON to Data + public override func transformedValue(_ value: Any?) -> Any? { + guard let geoJson = value as? GeoJson else { return nil } + + return geoJson.asJsonData() + } + + // Decode Data to GeoJSON + public override func reverseTransformedValue(_ value: Any?) -> Any? { + guard let data = value as? Data else { return nil } + + return GeoJsonReader.geoJsonFrom(jsonData: data) + } + +} +#endif + // MARK: - Private private struct GeoJsonCodingKey: CodingKey { diff --git a/Tests/GISToolsTests/GeoJson/FeatureCollectionTests.swift b/Tests/GISToolsTests/GeoJson/FeatureCollectionTests.swift index b7f3721..be7fd96 100644 --- a/Tests/GISToolsTests/GeoJson/FeatureCollectionTests.swift +++ b/Tests/GISToolsTests/GeoJson/FeatureCollectionTests.swift @@ -3,7 +3,7 @@ import XCTest final class FeatureCollectionTests: XCTestCase { - private let featureCollectionJson = """ + static let featureCollectionJson = """ { "type": "FeatureCollection", "features": [{ @@ -56,7 +56,7 @@ final class FeatureCollectionTests: XCTestCase { """ func testLoadJson() throws { - let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)) + let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)) XCTAssertEqual(featureCollection.type, GeoJsonType.featureCollection) XCTAssertEqual(featureCollection.projection, .epsg4326) @@ -94,7 +94,7 @@ final class FeatureCollectionTests: XCTestCase { } func testMap() throws { - var featureCollection = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)) + var featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)) let prop0: String? = featureCollection.features.first?.property(for: "prop0") XCTAssertEqual(prop0, "value0") @@ -110,7 +110,7 @@ final class FeatureCollectionTests: XCTestCase { } func testCompactMap() throws { - var featureCollection = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)) + var featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)) XCTAssertEqual(featureCollection.features.count, 3) @@ -123,7 +123,7 @@ final class FeatureCollectionTests: XCTestCase { } func testFilter() throws { - var featureCollection = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)) + var featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)) XCTAssertEqual(featureCollection.features.count, 3) @@ -135,7 +135,7 @@ final class FeatureCollectionTests: XCTestCase { } func testEnumerate() throws { - let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)) + let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)) let expected: [(Int, Int, Coordinate3D)] = [ (0, 0, Coordinate3D(latitude: 0.5, longitude: 102.0)), @@ -165,7 +165,7 @@ final class FeatureCollectionTests: XCTestCase { } func testEncodable() throws { - let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)) + let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -174,7 +174,7 @@ final class FeatureCollectionTests: XCTestCase { } func testDecodable() throws { - let featureCollectionData = try XCTUnwrap(FeatureCollection(jsonString: featureCollectionJson)?.asJsonData(prettyPrinted: true)) + let featureCollectionData = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)?.asJsonData(prettyPrinted: true)) let featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: featureCollectionData) XCTAssertEqual(featureCollection.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/FeatureTests.swift b/Tests/GISToolsTests/GeoJson/FeatureTests.swift index 7f51af6..05c39c6 100644 --- a/Tests/GISToolsTests/GeoJson/FeatureTests.swift +++ b/Tests/GISToolsTests/GeoJson/FeatureTests.swift @@ -3,7 +3,7 @@ import XCTest final class FeatureTests: XCTestCase { - private let featureJson = """ + static let featureJson = """ { "type": "Feature", "geometry": { @@ -30,7 +30,7 @@ final class FeatureTests: XCTestCase { """ func testLoadJson() throws { - let feature = try XCTUnwrap(Feature(jsonString: featureJson)) + let feature = try XCTUnwrap(Feature(jsonString: FeatureTests.featureJson)) XCTAssertEqual(feature.type, GeoJsonType.feature) XCTAssertEqual(feature.projection, .epsg4326) @@ -44,7 +44,7 @@ final class FeatureTests: XCTestCase { XCTAssertEqual(feature.id, .string("abcd.1234")) } - private let featureJsonWithIntId = """ + static let featureJsonWithIntId = """ { "type": "Feature", "geometry": { @@ -71,7 +71,7 @@ final class FeatureTests: XCTestCase { """ func testLoadJsonWithIntId() throws { - let feature = try XCTUnwrap(Feature(jsonString: featureJsonWithIntId)) + let feature = try XCTUnwrap(Feature(jsonString: FeatureTests.featureJsonWithIntId)) XCTAssertEqual(feature.id, .int(1234)) XCTAssertEqual(feature.projection, .epsg4326) @@ -89,7 +89,7 @@ final class FeatureTests: XCTestCase { } func testEncodable() throws { - let feature = try XCTUnwrap(Feature(jsonString: featureJson)) + let feature = try XCTUnwrap(Feature(jsonString: FeatureTests.featureJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -98,7 +98,7 @@ final class FeatureTests: XCTestCase { } func testDecodable() throws { - let featureData = try XCTUnwrap(Feature(jsonString: featureJson)?.asJsonData(prettyPrinted: true)) + let featureData = try XCTUnwrap(Feature(jsonString: FeatureTests.featureJson)?.asJsonData(prettyPrinted: true)) let feature = try JSONDecoder().decode(Feature.self, from: featureData) XCTAssertEqual(feature.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/GeometryCollectionTests.swift b/Tests/GISToolsTests/GeoJson/GeometryCollectionTests.swift index a16547c..e738c13 100644 --- a/Tests/GISToolsTests/GeoJson/GeometryCollectionTests.swift +++ b/Tests/GISToolsTests/GeoJson/GeometryCollectionTests.swift @@ -3,7 +3,7 @@ import XCTest final class GeometryCollectionTests: XCTestCase { - private let geometryCollectionJson = """ + static let geometryCollectionJson = """ { "type": "GeometryCollection", "geometries": [{ @@ -21,7 +21,7 @@ final class GeometryCollectionTests: XCTestCase { """ func testLoadJson() throws { - let geometryCollection = try XCTUnwrap(GeometryCollection(jsonString: geometryCollectionJson)) + let geometryCollection = try XCTUnwrap(GeometryCollection(jsonString: GeometryCollectionTests.geometryCollectionJson)) XCTAssertNotNil(geometryCollection) XCTAssertEqual(geometryCollection.type, GeoJsonType.geometryCollection) @@ -44,7 +44,7 @@ final class GeometryCollectionTests: XCTestCase { } func testEncodable() throws { - let geometryCollection = try XCTUnwrap(GeometryCollection(jsonString: geometryCollectionJson)) + let geometryCollection = try XCTUnwrap(GeometryCollection(jsonString: GeometryCollectionTests.geometryCollectionJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -53,7 +53,7 @@ final class GeometryCollectionTests: XCTestCase { } func testDecodable() throws { - let geometryCollectionData = try XCTUnwrap(GeometryCollection(jsonString: geometryCollectionJson)?.asJsonData(prettyPrinted: true)) + let geometryCollectionData = try XCTUnwrap(GeometryCollection(jsonString: GeometryCollectionTests.geometryCollectionJson)?.asJsonData(prettyPrinted: true)) let geometryCollection = try JSONDecoder().decode(GeometryCollection.self, from: geometryCollectionData) XCTAssertEqual(geometryCollection.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/LineStringTests.swift b/Tests/GISToolsTests/GeoJson/LineStringTests.swift index 4d5b17f..260259a 100644 --- a/Tests/GISToolsTests/GeoJson/LineStringTests.swift +++ b/Tests/GISToolsTests/GeoJson/LineStringTests.swift @@ -3,7 +3,7 @@ import XCTest final class LineStringTests: XCTestCase { - private let lineStringJson = """ + static let lineStringJson = """ { "type": "LineString", "coordinates": [ @@ -15,7 +15,7 @@ final class LineStringTests: XCTestCase { """ func testLoadJson() throws { - let lineString = try XCTUnwrap(LineString(jsonString: lineStringJson)) + let lineString = try XCTUnwrap(LineString(jsonString: LineStringTests.lineStringJson)) XCTAssertEqual(lineString.type, GeoJsonType.lineString) XCTAssertEqual(lineString.projection, .epsg4326) @@ -52,7 +52,7 @@ final class LineStringTests: XCTestCase { } func testEncodable() throws { - let lineString = try XCTUnwrap(LineString(jsonString: lineStringJson)) + let lineString = try XCTUnwrap(LineString(jsonString: LineStringTests.lineStringJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -61,7 +61,7 @@ final class LineStringTests: XCTestCase { } func testDecodable() throws { - let lineStringData = try XCTUnwrap(LineString(jsonString: lineStringJson)?.asJsonData(prettyPrinted: true)) + let lineStringData = try XCTUnwrap(LineString(jsonString: LineStringTests.lineStringJson)?.asJsonData(prettyPrinted: true)) let lineString = try JSONDecoder().decode(LineString.self, from: lineStringData) XCTAssertEqual(lineString.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/MultiLineStringTests.swift b/Tests/GISToolsTests/GeoJson/MultiLineStringTests.swift index b2efb2d..2189b76 100644 --- a/Tests/GISToolsTests/GeoJson/MultiLineStringTests.swift +++ b/Tests/GISToolsTests/GeoJson/MultiLineStringTests.swift @@ -3,7 +3,7 @@ import XCTest final class MultiLineStringTests: XCTestCase { - private let multiLineStringJson = """ + static let multiLineStringJson = """ { "type": "MultiLineString", "coordinates": [ @@ -21,7 +21,7 @@ final class MultiLineStringTests: XCTestCase { """ func testLoadJson() throws { - let multiLineString = try XCTUnwrap(MultiLineString(jsonString: multiLineStringJson)) + let multiLineString = try XCTUnwrap(MultiLineString(jsonString: MultiLineStringTests.multiLineStringJson)) XCTAssertNotNil(multiLineString) XCTAssertEqual(multiLineString.type, GeoJsonType.multiLineString) @@ -50,7 +50,7 @@ final class MultiLineStringTests: XCTestCase { } func testEncodable() throws { - let multiLineString = try XCTUnwrap(MultiLineString(jsonString: multiLineStringJson)) + let multiLineString = try XCTUnwrap(MultiLineString(jsonString: MultiLineStringTests.multiLineStringJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -59,7 +59,7 @@ final class MultiLineStringTests: XCTestCase { } func testDecodable() throws { - let multiLineStringData = try XCTUnwrap(MultiLineString(jsonString: multiLineStringJson)?.asJsonData(prettyPrinted: true)) + let multiLineStringData = try XCTUnwrap(MultiLineString(jsonString: MultiLineStringTests.multiLineStringJson)?.asJsonData(prettyPrinted: true)) let multiLineString = try JSONDecoder().decode(MultiLineString.self, from: multiLineStringData) XCTAssertEqual(multiLineString.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/MultiPointTests.swift b/Tests/GISToolsTests/GeoJson/MultiPointTests.swift index 4dc0ef1..7e83220 100644 --- a/Tests/GISToolsTests/GeoJson/MultiPointTests.swift +++ b/Tests/GISToolsTests/GeoJson/MultiPointTests.swift @@ -3,7 +3,7 @@ import XCTest final class MultiPointTests: XCTestCase { - private let multiPointJson = """ + static let multiPointJson = """ { "type": "MultiPoint", "coordinates": [ @@ -15,7 +15,7 @@ final class MultiPointTests: XCTestCase { """ func testLoadJson() throws { - let multiPoint = try XCTUnwrap(MultiPoint(jsonString: multiPointJson)) + let multiPoint = try XCTUnwrap(MultiPoint(jsonString: MultiPointTests.multiPointJson)) XCTAssertNotNil(multiPoint) XCTAssertEqual(multiPoint.type, GeoJsonType.multiPoint) @@ -35,7 +35,7 @@ final class MultiPointTests: XCTestCase { } func testEncodable() throws { - let multiPoint = try XCTUnwrap(MultiPoint(jsonString: multiPointJson)) + let multiPoint = try XCTUnwrap(MultiPoint(jsonString: MultiPointTests.multiPointJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -44,7 +44,7 @@ final class MultiPointTests: XCTestCase { } func testDecodable() throws { - let multiPointData = try XCTUnwrap(MultiPoint(jsonString: multiPointJson)?.asJsonData(prettyPrinted: true)) + let multiPointData = try XCTUnwrap(MultiPoint(jsonString: MultiPointTests.multiPointJson)?.asJsonData(prettyPrinted: true)) let multiPoint = try JSONDecoder().decode(MultiPoint.self, from: multiPointData) XCTAssertEqual(multiPoint.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/MultiPolygonTests.swift b/Tests/GISToolsTests/GeoJson/MultiPolygonTests.swift index 59b7693..ab8366c 100644 --- a/Tests/GISToolsTests/GeoJson/MultiPolygonTests.swift +++ b/Tests/GISToolsTests/GeoJson/MultiPolygonTests.swift @@ -3,7 +3,7 @@ import XCTest final class MultiPolygonTests: XCTestCase { - private let multiPolygonJson = """ + static let multiPolygonJson = """ { "type": "MultiPolygon", "coordinates": [ @@ -38,7 +38,7 @@ final class MultiPolygonTests: XCTestCase { """ func testLoadJson() throws { - let multiPolygon = try XCTUnwrap(MultiPolygon(jsonString: multiPolygonJson)) + let multiPolygon = try XCTUnwrap(MultiPolygon(jsonString: MultiPolygonTests.multiPolygonJson)) XCTAssertEqual(multiPolygon.type, GeoJsonType.multiPolygon) XCTAssertEqual(multiPolygon.projection, .epsg4326) @@ -57,7 +57,7 @@ final class MultiPolygonTests: XCTestCase { } func testEncodable() throws { - let multiPolygon = try XCTUnwrap(MultiPolygon(jsonString: multiPolygonJson)) + let multiPolygon = try XCTUnwrap(MultiPolygon(jsonString: MultiPolygonTests.multiPolygonJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -66,7 +66,7 @@ final class MultiPolygonTests: XCTestCase { } func testDecodable() throws { - let multiPolygonData = try XCTUnwrap(MultiPolygon(jsonString: multiPolygonJson)?.asJsonData(prettyPrinted: true)) + let multiPolygonData = try XCTUnwrap(MultiPolygon(jsonString: MultiPolygonTests.multiPolygonJson)?.asJsonData(prettyPrinted: true)) let multiPolygon = try JSONDecoder().decode(MultiPolygon.self, from: multiPolygonData) XCTAssertEqual(multiPolygon.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/PointTests.swift b/Tests/GISToolsTests/GeoJson/PointTests.swift index c82052b..7885f59 100644 --- a/Tests/GISToolsTests/GeoJson/PointTests.swift +++ b/Tests/GISToolsTests/GeoJson/PointTests.swift @@ -3,7 +3,7 @@ import XCTest final class PointTests: XCTestCase { - private let pointJson = """ + static let pointJson = """ { "type": "Point", "coordinates": [100.0, 0.0], @@ -12,7 +12,7 @@ final class PointTests: XCTestCase { """ func testLoadJson() throws { - let point = try XCTUnwrap(Point(jsonString: pointJson)) + let point = try XCTUnwrap(Point(jsonString: PointTests.pointJson)) XCTAssertEqual(point.type, GeoJsonType.point) XCTAssertEqual(point.projection, .epsg4326) @@ -31,7 +31,7 @@ final class PointTests: XCTestCase { } func testEncodable() throws { - let point = try XCTUnwrap(Point(jsonString: pointJson)) + let point = try XCTUnwrap(Point(jsonString: PointTests.pointJson)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -40,7 +40,7 @@ final class PointTests: XCTestCase { } func testDecodable() throws { - let pointData = try XCTUnwrap(Point(jsonString: pointJson)?.asJsonData(prettyPrinted: true)) + let pointData = try XCTUnwrap(Point(jsonString: PointTests.pointJson)?.asJsonData(prettyPrinted: true)) let point = try JSONDecoder().decode(Point.self, from: pointData) XCTAssertEqual(point.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/PolygonTests.swift b/Tests/GISToolsTests/GeoJson/PolygonTests.swift index b0c1384..58be6b9 100644 --- a/Tests/GISToolsTests/GeoJson/PolygonTests.swift +++ b/Tests/GISToolsTests/GeoJson/PolygonTests.swift @@ -3,7 +3,7 @@ import XCTest final class PolygonTests: XCTestCase { - private let polygonJsonNoHole = """ + static let polygonJsonNoHole = """ { "type": "Polygon", "coordinates": [ @@ -19,7 +19,7 @@ final class PolygonTests: XCTestCase { } """ - let polygonJsonWithHoles = """ + static let polygonJsonWithHoles = """ { "type": "Polygon", "coordinates": [ @@ -43,7 +43,7 @@ final class PolygonTests: XCTestCase { """ func testLoadJson() throws { - let polygonNoHole = try XCTUnwrap(Polygon(jsonString: polygonJsonNoHole)) + let polygonNoHole = try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonNoHole)) XCTAssertEqual(polygonNoHole.type, GeoJsonType.polygon) XCTAssertEqual(polygonNoHole.projection, .epsg4326) @@ -51,7 +51,7 @@ final class PolygonTests: XCTestCase { XCTAssertEqual(polygonNoHole.foreignMember(for: "other"), "something else") XCTAssertEqual(polygonNoHole[foreignMember: "other"], "something else") - let polygonWithHoles = try XCTUnwrap(Polygon(jsonString: polygonJsonWithHoles)) + let polygonWithHoles = try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonWithHoles)) XCTAssertEqual(polygonWithHoles.type, GeoJsonType.polygon) XCTAssertEqual(polygonWithHoles.projection, .epsg4326) @@ -79,8 +79,8 @@ final class PolygonTests: XCTestCase { } func testEncodable() throws { - let polygonNoHole = try XCTUnwrap(Polygon(jsonString: polygonJsonNoHole)) - let polygonWithHoles = try XCTUnwrap(Polygon(jsonString: polygonJsonWithHoles)) + let polygonNoHole = try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonNoHole)) + let polygonWithHoles = try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonWithHoles)) let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] @@ -90,10 +90,10 @@ final class PolygonTests: XCTestCase { } func testDecodable() throws { - let polygonNoHoleData = try XCTUnwrap(Polygon(jsonString: polygonJsonNoHole)?.asJsonData(prettyPrinted: true)) + let polygonNoHoleData = try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonNoHole)?.asJsonData(prettyPrinted: true)) let polygonNoHole = try JSONDecoder().decode(Polygon.self, from: polygonNoHoleData) - let polygonWithHolesData = try XCTUnwrap(Polygon(jsonString: polygonJsonWithHoles)?.asJsonData(prettyPrinted: true)) + let polygonWithHolesData = try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonWithHoles)?.asJsonData(prettyPrinted: true)) let polygonWithHoles = try JSONDecoder().decode(Polygon.self, from: polygonWithHolesData) XCTAssertEqual(polygonNoHole.projection, .epsg4326) diff --git a/Tests/GISToolsTests/GeoJson/SwiftDataTests.swift b/Tests/GISToolsTests/GeoJson/SwiftDataTests.swift new file mode 100644 index 0000000..62a9684 --- /dev/null +++ b/Tests/GISToolsTests/GeoJson/SwiftDataTests.swift @@ -0,0 +1,61 @@ +@testable import GISTools +import XCTest + +#if canImport(SwiftData) +import SwiftData + +@available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) +final class SwiftDataTests: XCTestCase { + + @Model + class GeoJsonModel { + @Attribute(.unique) var id: Int + @Attribute(.transformable(by: GeoJsonTransformer.name.rawValue)) var geoJson: GeoJson? + + init(id: Int, geoJson: GeoJson) { + self.id = id + self.geoJson = geoJson + } + } + + let container = { + GeoJsonTransformer.register() + + let config = ModelConfiguration(isStoredInMemoryOnly: true) + return try! ModelContainer(for: GeoJsonModel.self, configurations: config) + }() + + @MainActor + func testInsert() throws { + let geoJsonTests: [Int: GeoJson] = [ + 0: try XCTUnwrap(Point(jsonString: PointTests.pointJson)), + 1: try XCTUnwrap(MultiPoint(jsonString: MultiPointTests.multiPointJson)), + 2: try XCTUnwrap(Feature(jsonString: FeatureTests.featureJson)), + 3: try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson)), + 4: try XCTUnwrap(GeometryCollection(jsonString: GeometryCollectionTests.geometryCollectionJson)), + 5: try XCTUnwrap(LineString(jsonString: LineStringTests.lineStringJson)), + 6: try XCTUnwrap(MultiLineString(jsonString: MultiLineStringTests.multiLineStringJson)), + 7: try XCTUnwrap(Polygon(jsonString: PolygonTests.polygonJsonNoHole)), + 8: try XCTUnwrap(MultiPolygon(jsonString: MultiPolygonTests.multiPolygonJson)), + ] + + // Insert + for (id, geoJson) in geoJsonTests { + let model = GeoJsonModel(id: id, geoJson: geoJson) + container.mainContext.insert(model) + } + + // Check + for (id, geoJson) in geoJsonTests { + var descriptor = FetchDescriptor( + predicate: #Predicate { $0.id == id }, + sortBy: [SortDescriptor(\.id)]) + descriptor.fetchLimit = 1 + + let result = try XCTUnwrap(container.mainContext.fetch(descriptor).first?.geoJson) + XCTAssertTrue(result.isEqualTo(geoJson)) + } + } + +} +#endif