diff --git a/CHANGELOG.md b/CHANGELOG.md index 59a8a0d8a..67660383c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,14 @@ ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.7.0...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.8.0...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ __New features__ - Add ParseSpotify authentication ([#375](https://github.com/parse-community/Parse-Swift/pull/375)), thanks to [Ulaş Sancak](https://github.com/rocxteady). __Fixes__ +- Encode withinPolygon Queryconstraint correctly ([#381](https://github.com/parse-community/Parse-Swift/pull/381)), thanks to [Corey Baker](https://github.com/cbaker6). - Use select for ParseLiveQuery when fields are not present ([#376](https://github.com/parse-community/Parse-Swift/pull/376)), thanks to [Corey Baker](https://github.com/cbaker6). ### 4.7.0 diff --git a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift index b8dab79cf..9c04c2332 100644 --- a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift @@ -251,6 +251,58 @@ query8.findAll { result in } } +do { + let points: [ParseGeoPoint] = [ + try .init(latitude: 35.0, longitude: -28.0), + try .init(latitude: 45.0, longitude: -28.0), + try .init(latitude: 39.0, longitude: -35.0) + ] + let query9 = GameScore.query(withinPolygon(key: "location", points: points)) + query9.find { results in + switch results { + case .success(let scores): + + scores.forEach { (score) in + print(""" + Someone has a points value of \"\(String(describing: score.points))\" + with a geolocation \(String(describing: score.location)) within the + polygon using points: \(points) + """) + } + case .failure(let error): + assertionFailure("Error querying: \(error)") + } + } +} catch { + print("Could not create geopoints: \(error)") +} + +do { + let points: [ParseGeoPoint] = [ + try .init(latitude: 35.0, longitude: -28.0), + try .init(latitude: 45.0, longitude: -28.0), + try .init(latitude: 39.0, longitude: -35.0) + ] + let polygon = try ParsePolygon(points) + let query10 = GameScore.query(withinPolygon(key: "location", polygon: polygon)) + query10.find { results in + switch results { + case .success(let scores): + scores.forEach { (score) in + print(""" + Someone has a points value of \"\(String(describing: score.points))\" + with a geolocation \(String(describing: score.location)) within the + polygon: \(polygon) + """) + } + case .failure(let error): + assertionFailure("Error querying: \(error)") + } + } +} catch { + print("Could not create geopoints: \(error)") +} + //: Hint of the previous query (asynchronous) query2 = query2.hint("_id_") query2.find { result in diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 722f0e774..67caa34e0 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -10,7 +10,7 @@ import Foundation enum ParseConstants { static let sdk = "swift" - static let version = "4.7.0" + static let version = "4.8.0" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Types/ParsePolygon.swift b/Sources/ParseSwift/Types/ParsePolygon.swift index 256a8a275..f1195963f 100644 --- a/Sources/ParseSwift/Types/ParsePolygon.swift +++ b/Sources/ParseSwift/Types/ParsePolygon.swift @@ -105,8 +105,8 @@ extension ParsePolygon { let points = try values.decode([[Double]].self, forKey: .coordinates) try points.forEach { if $0.count == 2 { - guard let latitude = $0.first, - let longitude = $0.last else { + guard let latitude = $0.last, + let longitude = $0.first else { throw ParseError(code: .unknownError, message: "Could not decode ParsePolygon: \(points)") } decodedCoordinates.append(try ParseGeoPoint(latitude: latitude, @@ -125,7 +125,7 @@ extension ParsePolygon { try container.encode(__type, forKey: .__type) var nestedUnkeyedContainer = container.nestedUnkeyedContainer(forKey: .coordinates) try coordinates.forEach { - try nestedUnkeyedContainer.encode([$0.latitude, $0.longitude]) + try nestedUnkeyedContainer.encode([$0.longitude, $0.latitude]) } } } diff --git a/Sources/ParseSwift/Types/QueryConstraint.swift b/Sources/ParseSwift/Types/QueryConstraint.swift index 91f9c659d..23dfa275b 100644 --- a/Sources/ParseSwift/Types/QueryConstraint.swift +++ b/Sources/ParseSwift/Types/QueryConstraint.swift @@ -588,8 +588,7 @@ public func withinGeoBox(key: String, fromSouthWest southwest: ParseGeoPoint, - returns: The same instance of `QueryConstraint` as the receiver. */ public func withinPolygon(key: String, points: [ParseGeoPoint]) -> QueryConstraint { - let polygon = points.flatMap { [[$0.latitude, $0.longitude]]} - let dictionary = [QueryConstraint.Comparator.polygon.rawValue: polygon] + let dictionary = [QueryConstraint.Comparator.polygon.rawValue: points] return .init(key: key, value: dictionary, comparator: .geoWithin) } @@ -604,7 +603,6 @@ public func withinPolygon(key: String, points: [ParseGeoPoint]) -> QueryConstrai - returns: The same instance of `QueryConstraint` as the receiver. */ public func withinPolygon(key: String, polygon: ParsePolygon) -> QueryConstraint { - let polygon = polygon.coordinates.flatMap { [[$0.latitude, $0.longitude]]} let dictionary = [QueryConstraint.Comparator.polygon.rawValue: polygon] return .init(key: key, value: dictionary, comparator: .geoWithin) } diff --git a/Tests/ParseSwiftTests/ParsePolygonTests.swift b/Tests/ParseSwiftTests/ParsePolygonTests.swift index 6eca412c1..3c04ebe20 100644 --- a/Tests/ParseSwiftTests/ParsePolygonTests.swift +++ b/Tests/ParseSwiftTests/ParsePolygonTests.swift @@ -117,13 +117,13 @@ class ParsePolygonTests: XCTestCase { func testDebugString() throws { let polygon = try ParsePolygon(points) - let expected = "{\"__type\":\"Polygon\",\"coordinates\":[[0,0],[0,1],[1,1],[1,0],[0,0]]}" + let expected = "{\"__type\":\"Polygon\",\"coordinates\":[[0,0],[1,0],[1,1],[0,1],[0,0]]}" XCTAssertEqual(polygon.debugDescription, expected) } func testDescription() throws { let polygon = try ParsePolygon(points) - let expected = "{\"__type\":\"Polygon\",\"coordinates\":[[0,0],[0,1],[1,1],[1,0],[0,0]]}" + let expected = "{\"__type\":\"Polygon\",\"coordinates\":[[0,0],[1,0],[1,1],[0,1],[0,0]]}" XCTAssertEqual(polygon.description, expected) } } diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index 31e28601a..29b2d5691 100644 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -2924,89 +2924,33 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } } + #if !os(Linux) && !os(Android) && !os(Windows) // swiftlint:disable:next function_body_length func testWhereKeyWithinPolygonPoints() throws { - let expected: [String: AnyCodable] = [ - "yolo": ["$geoWithin": ["$polygon": [ - [10.1, 20.1], - [20.1, 30.1], - [30.1, 40.1]] - ] - ] - ] + // swiftlint:disable:next line_length + let expected = "{\"yolo\":{\"$geoWithin\":{\"$polygon\":[{\"__type\":\"GeoPoint\",\"latitude\":10.1,\"longitude\":20.100000000000001},{\"__type\":\"GeoPoint\",\"latitude\":20.100000000000001,\"longitude\":30.100000000000001},{\"__type\":\"GeoPoint\",\"latitude\":30.100000000000001,\"longitude\":40.100000000000001}]}}}" let geoPoint1 = try ParseGeoPoint(latitude: 10.1, longitude: 20.1) let geoPoint2 = try ParseGeoPoint(latitude: 20.1, longitude: 30.1) let geoPoint3 = try ParseGeoPoint(latitude: 30.1, longitude: 40.1) let polygon = [geoPoint1, geoPoint2, geoPoint3] let constraint = withinPolygon(key: "yolo", points: polygon) let query = GameScore.query(constraint) - let queryWhere = query.`where` - - do { - let encoded = try ParseCoding.jsonEncoder().encode(queryWhere) - let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded) - XCTAssertEqual(expected.keys, decodedDictionary.keys) - - guard let expectedValues = expected.values.first?.value as? [String: [String: [[Double]]]], - let expectedBox = expectedValues["$geoWithin"]?["$polygon"] else { - XCTFail("Should have casted") - return - } - - guard let decodedValues = decodedDictionary.values.first?.value as? [String: [String: [[Double]]]], - let decodedBox = decodedValues["$geoWithin"]?["$polygon"] else { - XCTFail("Should have casted") - return - } - XCTAssertEqual(expectedBox, decodedBox) - - } catch { - XCTFail(error.localizedDescription) - return - } + XCTAssertEqual(query.where.description, expected) } // swiftlint:disable:next function_body_length func testWhereKeyWithinPolygon() throws { - let expected: [String: AnyCodable] = [ - "yolo": ["$geoWithin": ["$polygon": [ - [10.1, 20.1], - [20.1, 30.1], - [30.1, 40.1]] - ] - ] - ] + // swiftlint:disable:next line_length + let expected = "{\"yolo\":{\"$geoWithin\":{\"$polygon\":{\"__type\":\"Polygon\",\"coordinates\":[[20.100000000000001,10.1],[30.100000000000001,20.100000000000001],[40.100000000000001,30.100000000000001]]}}}}" let geoPoint1 = try ParseGeoPoint(latitude: 10.1, longitude: 20.1) let geoPoint2 = try ParseGeoPoint(latitude: 20.1, longitude: 30.1) let geoPoint3 = try ParseGeoPoint(latitude: 30.1, longitude: 40.1) let polygon = try ParsePolygon(geoPoint1, geoPoint2, geoPoint3) let constraint = withinPolygon(key: "yolo", polygon: polygon) let query = GameScore.query(constraint) - let queryWhere = query.`where` - - do { - let encoded = try ParseCoding.jsonEncoder().encode(queryWhere) - let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded) - XCTAssertEqual(expected.keys, decodedDictionary.keys) - - guard let expectedValues = expected.values.first?.value as? [String: [String: [[Double]]]], - let expectedBox = expectedValues["$geoWithin"]?["$polygon"] else { - XCTFail("Should have casted") - return - } - - guard let decodedValues = decodedDictionary.values.first?.value as? [String: [String: [[Double]]]], - let decodedBox = decodedValues["$geoWithin"]?["$polygon"] else { - XCTFail("Should have casted") - return - } - XCTAssertEqual(expectedBox, decodedBox) - - } catch { - XCTFail(error.localizedDescription) - return - } + XCTAssertEqual(query.where.description, expected) } + #endif func testWhereKeyPolygonContains() throws { let expected: [String: AnyCodable] = [