diff --git a/CHANGELOG.md b/CHANGELOG.md index 93131527f..f8f01783f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,11 @@ [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.5.1...3.0.0) __New features__ +- Adds equalTo QueryConstraint along with ability to change the SDK default behavior of using $eq QueryConstraint parameter or not ([#310](https://github.com/parse-community/Parse-Swift/pull/310)), thanks to [Corey Baker](https://github.com/cbaker6). +- Adds isNull and isNotNull QueryConstraint along with the ability set/forceSet null using ParseOperation ([#308](https://github.com/parse-community/Parse-Swift/pull/308)), thanks to [Corey Baker](https://github.com/cbaker6). +- Adds auth support for GitHub, Google, and LinkedIn ([#307](https://github.com/parse-community/Parse-Swift/pull/307)), thanks to [Corey Baker](https://github.com/cbaker6). - (Breaking Change) Adds options to matchesText QueryConstraint along with the ability to see matching score. The compiler should recommend the new score property to all ParseObjects ([#306](https://github.com/parse-community/Parse-Swift/pull/306)), thanks to [Corey Baker](https://github.com/cbaker6). - Adds withCount query ([#306](https://github.com/parse-community/Parse-Swift/pull/306)), thanks to [Corey Baker](https://github.com/cbaker6). -- Adds auth support for GitHub, Google, and LinkedIn ([#307](https://github.com/parse-community/Parse-Swift/pull/307)), thanks to [Corey Baker](https://github.com/cbaker6). -- Adds isNull QueryConstraint along with the ability set/forceSet null using ParseOperation ([#308](https://github.com/parse-community/Parse-Swift/pull/308)), thanks to [Corey Baker](https://github.com/cbaker6). __Improvements__ - (Breaking Change) Change boolean configuration parameters to match Swift conventions. The compilor should help with name changes ([#311](https://github.com/parse-community/Parse-Swift/pull/311)), thanks to [Corey Baker](https://github.com/cbaker6). diff --git a/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift index c53882961..16689aed5 100644 --- a/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift @@ -72,8 +72,8 @@ do { print(error) } -//: Query all scores whose is null or undefined. -let query1 = GameScore.query(notNull(key: "name")) +//: Query all scores whose name is null or undefined. +let query1 = GameScore.query(isNotNull(key: "name")) let results1 = try query1.find() print("Total found: \(results1.count)") results1.forEach { score in diff --git a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift index f5a9c5646..c2ceb9753 100644 --- a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift @@ -191,8 +191,8 @@ query4.find { results in } } -//: Get the same results as the previous query whose location is not null or undefined. -var anotherQuery4 = GameScore.query("points" > 9, notNull(key: "location")) +//: If you want to query for points > 9 and whose location is not null or undefined. +var anotherQuery4 = GameScore.query("points" > 9, isNotNull(key: "location")) anotherQuery4.find { results in switch results { case .success(let scores): diff --git a/ParseSwift.playground/Sources/Common.swift b/ParseSwift.playground/Sources/Common.swift index ce8d88b82..95f2857c5 100644 --- a/ParseSwift.playground/Sources/Common.swift +++ b/ParseSwift.playground/Sources/Common.swift @@ -3,15 +3,17 @@ import ParseSwift public func initializeParse() { ParseSwift.initialize(applicationId: "applicationId", - clientKey: "clientKey", - masterKey: "masterKey", - serverURL: URL(string: "http://localhost:1337/1")!, - isUsingTransactions: false) + clientKey: "clientKey", + masterKey: "masterKey", + serverURL: URL(string: "http://localhost:1337/1")!, + isUsingTransactions: false, + isUsingEqualQueryConstraint: false) } public func initializeParseCustomObjectId() { ParseSwift.initialize(applicationId: "applicationId", - clientKey: "clientKey", - serverURL: URL(string: "http://localhost:1337/1")!, - isAllowingCustomObjectIds: true) + clientKey: "clientKey", + serverURL: URL(string: "http://localhost:1337/1")!, + isAllowingCustomObjectIds: true, + isUsingEqualQueryConstraint: false) } diff --git a/README.md b/README.md index ddb69e133..f8a934ab7 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ let package = Package( Then run `swift build`. You can also install using SPM in your Xcode project by going to -"Project->NameOfYourProject->Swift Packages" and placing "https://github.com/parse-community/Parse-Swift.git" in the +"Project->NameOfYourProject->Swift Packages" and placing `https://github.com/parse-community/Parse-Swift.git` in the search field. ### [CocoaPods](https://cocoapods.org) diff --git a/Sources/ParseSwift/Parse.swift b/Sources/ParseSwift/Parse.swift index c51404026..7945fba1d 100644 --- a/Sources/ParseSwift/Parse.swift +++ b/Sources/ParseSwift/Parse.swift @@ -30,6 +30,10 @@ public struct ParseConfiguration { /// - warning: This is experimental. public internal(set) var isUsingTransactions = false + /// Use the **$eq** query constraint when querying. + /// - warning: This is known not to work for LiveQuery on Parse Servers <= 5.0.0. + public internal(set) var isUsingEqualQueryConstraint = false + /// The default caching policy for all http requests that determines when to /// return a response from the cache. Defaults to `useProtocolCachePolicy`. /// See Apple's [documentation](https://developer.apple.com/documentation/foundation/url_loading_system/accessing_cached_data) @@ -76,6 +80,7 @@ public struct ParseConfiguration { - parameter isAllowingCustomObjectIds: Allows objectIds to be created on the client. side for each object. Must be enabled on the server to work. - parameter isUsingTransactions: Use transactions when saving/updating multiple objects. + - parameter isUsingEqualQueryConstraint: Use the **$eq** query constraint when querying. - parameter keyValueStore: A key/value store that conforms to the `ParseKeyValueStore` protocol. Defaults to `nil` in which one will be created an memory, but never persisted. For Linux, this this is the only store available since there is no Keychain. Linux users should replace this store with an @@ -106,8 +111,10 @@ public struct ParseConfiguration { masterKey: String? = nil, serverURL: URL, liveQueryServerURL: URL? = nil, + allowCustomObjectId: Bool = false, isAllowingCustomObjectIds: Bool = false, isUsingTransactions: Bool = false, + isUsingEqualQueryConstraint: Bool = false, keyValueStore: ParseKeyValueStore? = nil, requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, cacheMemoryCapacity: Int = 512_000, @@ -126,6 +133,7 @@ public struct ParseConfiguration { self.liveQuerysServerURL = liveQueryServerURL self.isAllowingCustomObjectIds = isAllowingCustomObjectIds self.isUsingTransactions = isUsingTransactions + self.isUsingEqualQueryConstraint = isUsingEqualQueryConstraint self.mountPath = "/" + serverURL.pathComponents .filter { $0 != "/" } .joined(separator: "/") @@ -237,6 +245,7 @@ public struct ParseSwift { - parameter isAllowingCustomObjectIds: Allows objectIds to be created on the client. side for each object. Must be enabled on the server to work. - parameter isUsingTransactions: Use transactions when saving/updating multiple objects. + - parameter isUsingEqualQueryConstraint: Use the **$eq** query constraint when querying. - parameter keyValueStore: A key/value store that conforms to the `ParseKeyValueStore` protocol. Defaults to `nil` in which one will be created an memory, but never persisted. For Linux, this this is the only store available since there is no Keychain. Linux users should replace this store with an @@ -268,6 +277,7 @@ public struct ParseSwift { liveQueryServerURL: URL? = nil, isAllowingCustomObjectIds: Bool = false, isUsingTransactions: Bool = false, + isUsingEqualQueryConstraint: Bool = false, keyValueStore: ParseKeyValueStore? = nil, requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, cacheMemoryCapacity: Int = 512_000, @@ -287,6 +297,7 @@ public struct ParseSwift { liveQueryServerURL: liveQueryServerURL, isAllowingCustomObjectIds: isAllowingCustomObjectIds, isUsingTransactions: isUsingTransactions, + isUsingEqualQueryConstraint: isUsingEqualQueryConstraint, keyValueStore: keyValueStore, requestCachePolicy: requestCachePolicy, cacheMemoryCapacity: cacheMemoryCapacity, @@ -305,6 +316,7 @@ public struct ParseSwift { liveQueryServerURL: URL? = nil, isAllowingCustomObjectIds: Bool = false, isUsingTransactions: Bool = false, + isUsingEqualQueryConstraint: Bool = false, keyValueStore: ParseKeyValueStore? = nil, requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, cacheMemoryCapacity: Int = 512_000, @@ -324,6 +336,7 @@ public struct ParseSwift { liveQueryServerURL: liveQueryServerURL, isAllowingCustomObjectIds: isAllowingCustomObjectIds, isUsingTransactions: isUsingTransactions, + isUsingEqualQueryConstraint: isUsingEqualQueryConstraint, keyValueStore: keyValueStore, requestCachePolicy: requestCachePolicy, cacheMemoryCapacity: cacheMemoryCapacity, diff --git a/Sources/ParseSwift/Types/QueryConstraint.swift b/Sources/ParseSwift/Types/QueryConstraint.swift index f9fe9d87d..1cbf56c2d 100644 --- a/Sources/ParseSwift/Types/QueryConstraint.swift +++ b/Sources/ParseSwift/Types/QueryConstraint.swift @@ -15,6 +15,7 @@ public struct QueryConstraint: Encodable, Hashable { case lessThanOrEqualTo = "$lte" case greaterThan = "$gt" case greaterThanOrEqualTo = "$gte" + case equalTo = "$eq" case notEqualTo = "$ne" case containedIn = "$in" case notContainedIn = "$nin" @@ -136,9 +137,36 @@ public func <= (key: String, value: T) -> QueryConstraint where T: Encodable - parameter key: The key that the value is stored in. - parameter value: The value to compare. - returns: The same instance of `QueryConstraint` as the receiver. + - warning: See `equalTo` for more information. + Behavior changes based on `ParseSwift.configuration.isUsingEqualQueryConstraint` + where isUsingEqualQueryConstraint == true is known not to work for LiveQuery on + Parse Servers <= 5.0.0. */ public func == (key: String, value: T) -> QueryConstraint where T: Encodable { - QueryConstraint(key: key, value: value) + equalTo(key: key, value: value) +} + +/** + Add a constraint that requires that a key is equal to a value. + - parameter key: The key that the value is stored in. + - parameter value: The value to compare. + - parameter isUsingEQ: Set to **true** to use **$eq** comparater, + allowing for multiple `QueryConstraint`'s to be used on a single **key**. + Setting to *false* may override any `QueryConstraint`'s on the same **key**. + Defaults to `ParseSwift.configuration.isUsingEqualQueryConstraint`. + - returns: The same instance of `QueryConstraint` as the receiver. + - warning: `isUsingEQ == true` is known not to work for LiveQueries + on Parse Servers <= 5.0.0. + */ +public func equalTo (key: String, + value: T, + //swiftlint:disable:next line_length + isUsingEQ: Bool = ParseSwift.configuration.isUsingEqualQueryConstraint) -> QueryConstraint where T: Encodable { + if !isUsingEQ { + return QueryConstraint(key: key, value: value) + } else { + return QueryConstraint(key: key, value: value, comparator: .equalTo) + } } /** @@ -147,9 +175,37 @@ public func == (key: String, value: T) -> QueryConstraint where T: Encodable - parameter value: The `ParseObject` to compare. - returns: The same instance of `QueryConstraint` as the receiver. - throws: An error of type `ParseError`. + - warning: See `equalTo` for more information. + Behavior changes based on `ParseSwift.configuration.isUsingEqualQueryConstraint` + where isUsingEqualQueryConstraint == true is known not to work for LiveQuery on + Parse Servers <= 5.0.0. */ public func == (key: String, value: T) throws -> QueryConstraint where T: ParseObject { - try QueryConstraint(key: key, value: value.toPointer()) + try equalTo(key: key, value: value) +} + +/** + Add a constraint that requires that a key is equal to a `ParseObject`. + - parameter key: The key that the value is stored in. + - parameter value: The `ParseObject` to compare. + - parameter isUsingEQ: Set to **true** to use **$eq** comparater, + allowing for multiple `QueryConstraint`'s to be used on a single **key**. + Setting to *false* may override any `QueryConstraint`'s on the same **key**. + Defaults to `ParseSwift.configuration.isUsingEqualQueryConstraint`. + - returns: The same instance of `QueryConstraint` as the receiver. + - throws: An error of type `ParseError`. + - warning: `isUsingEQ == true` is known not to work for LiveQueries + on Parse Servers <= 5.0.0. + */ +public func equalTo (key: String, + value: T, + //swiftlint:disable:next line_length + isUsingEQ: Bool = ParseSwift.configuration.isUsingEqualQueryConstraint) throws -> QueryConstraint where T: ParseObject { + if !isUsingEQ { + return try QueryConstraint(key: key, value: value.toPointer()) + } else { + return try QueryConstraint(key: key, value: value.toPointer(), comparator: .equalTo) + } } /** @@ -703,7 +759,7 @@ public func isNull (key: String) -> QueryConstraint { - parameter key: The key that the value is stored in. - returns: The same instance of `QueryConstraint` as the receiver. */ -public func notNull (key: String) -> QueryConstraint { +public func isNotNull (key: String) -> QueryConstraint { QueryConstraint(key: key, comparator: .notEqualTo, isNull: true) } diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index 3a5f78b22..94441645b 100644 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -62,6 +62,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length clientKey: "clientKey", masterKey: "masterKey", serverURL: url, + isUsingEqualQueryConstraint: false, isTesting: true) } @@ -1186,6 +1187,14 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(query.description, expected) } + func testWhereKeyEqualToBoolEQ() throws { + let query = GameScore.query(equalTo(key: "isCounts", value: true, isUsingEQ: true)) + // swiftlint:disable:next line_length + let expected = "GameScore ({\"limit\":100,\"skip\":0,\"_method\":\"GET\",\"where\":{\"isCounts\":{\"$eq\":true}}})" + XCTAssertEqual(query.debugDescription, expected) + XCTAssertEqual(query.description, expected) + } + func testWhereKeyEqualToParseObject() throws { var compareObject = GameScore(points: 11) compareObject.objectId = "hello" @@ -1195,6 +1204,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(query.debugDescription, expected) } + func testWhereKeyEqualToParseObjectEQ() throws { + var compareObject = GameScore(points: 11) + compareObject.objectId = "hello" + let query = try GameScore.query(equalTo(key: "yolo", value: compareObject, isUsingEQ: true)) + // swiftlint:disable:next line_length + let expected = "GameScore ({\"limit\":100,\"skip\":0,\"_method\":\"GET\",\"where\":{\"yolo\":{\"$eq\":{\"__type\":\"Pointer\",\"className\":\"GameScore\",\"objectId\":\"hello\"}}}})" + XCTAssertEqual(query.debugDescription, expected) + } + func testWhereKeyEqualToParseObjectDuplicateConstraint() throws { var compareObject = GameScore(points: 11) compareObject.objectId = "hello" @@ -1235,7 +1253,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length func testWhereKeyNotNull() throws { var compareObject = GameScore(points: 11) compareObject.objectId = "hello" - let query = GameScore.query(notNull(key: "yolo")) + let query = GameScore.query(isNotNull(key: "yolo")) let expected = "GameScore ({\"limit\":100,\"skip\":0,\"_method\":\"GET\",\"where\":{\"yolo\":{\"$ne\":null}}})" XCTAssertEqual(query.debugDescription, expected) }