Skip to content

Commit

Permalink
feat: add set operation based on KeyPath (#403)
Browse files Browse the repository at this point in the history
* wip

* switch to using chainable for reverts

* add file

* feat:  add set operation based on KeyPath

* add tests and refactor general errors

* add back tests for deprecated methods

* add get() method

* reduce codecov patch
  • Loading branch information
cbaker6 committed Sep 5, 2022
1 parent 9dac227 commit d94dcad
Show file tree
Hide file tree
Showing 23 changed files with 701 additions and 289 deletions.
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ coverage:
status:
patch:
default:
target: auto
target: 72
changes: false
project:
default:
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Parse-Swift Changelog

### main
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.9.3...main)
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.10.0...main)
* _Contributing to this repo? Add info about your change here to be included in the next release_

### 4.10.0
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.9.3...4.10.0)

__New features__
- Add a new operation method that allows developers to set a new value to a KeyPath without needing the string version of the key. Also adds the get() method to allow developers to get the unwrapped property of any ParseObject based on its KeyPath ([#403](https://github.com/parse-community/Parse-Swift/pull/403)), thanks to [Corey Baker](https://github.com/cbaker6).
- Add revertKeyPath() and revertObject() methods to ParseObject which allow developers to revert to original values of key paths or objects after mutating ParseObjects that already have an objectId ([#402](https://github.com/parse-community/Parse-Swift/pull/402)), thanks to [Corey Baker](https://github.com/cbaker6).

### 4.9.3
Expand Down
10 changes: 10 additions & 0 deletions ParseSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@
7085DDB326D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7085DDB226D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift */; };
7085DDB426D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7085DDB226D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift */; };
7085DDB526D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7085DDB226D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift */; };
7087A93C28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7087A93B28C558CA00656E93 /* ParseOperation+keyPath.swift */; };
7087A93D28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7087A93B28C558CA00656E93 /* ParseOperation+keyPath.swift */; };
7087A93E28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7087A93B28C558CA00656E93 /* ParseOperation+keyPath.swift */; };
7087A93F28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7087A93B28C558CA00656E93 /* ParseOperation+keyPath.swift */; };
708CADCF2872263D0066C279 /* ParseKeychainAccessGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708CADCE2872263D0066C279 /* ParseKeychainAccessGroupTests.swift */; };
708CADD02872263D0066C279 /* ParseKeychainAccessGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708CADCE2872263D0066C279 /* ParseKeychainAccessGroupTests.swift */; };
708CADD12872263D0066C279 /* ParseKeychainAccessGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 708CADCE2872263D0066C279 /* ParseKeychainAccessGroupTests.swift */; };
Expand Down Expand Up @@ -1269,6 +1273,7 @@
7085DD9326CBF3A70033B977 /* Documentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Documentation.docc; sourceTree = "<group>"; };
7085DDA226CC8A470033B977 /* ParseHealth+combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ParseHealth+combine.swift"; sourceTree = "<group>"; };
7085DDB226D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAuthenticationCombineTests.swift; sourceTree = "<group>"; };
7087A93B28C558CA00656E93 /* ParseOperation+keyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ParseOperation+keyPath.swift"; sourceTree = "<group>"; };
708CADCE2872263D0066C279 /* ParseKeychainAccessGroupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseKeychainAccessGroupTests.swift; sourceTree = "<group>"; };
708D035125215F9B00646C70 /* Deletable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deletable.swift; sourceTree = "<group>"; };
709A147C283949D100BF85E5 /* ParseSchema.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchema.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2135,6 +2140,7 @@
F97B464024D9C78B00F4A88B /* ParseOperation.swift */,
703B091026BD992E005A112F /* ParseOperation+async.swift */,
7044C19E25C4FA870011F6E7 /* ParseOperation+combine.swift */,
7087A93B28C558CA00656E93 /* ParseOperation+keyPath.swift */,
91285B1B26990D7F0051B544 /* ParsePolygon.swift */,
705025BC284C610C008D6624 /* ParsePush.swift */,
705025C1284C7841008D6624 /* ParsePush+async.swift */,
Expand Down Expand Up @@ -2697,6 +2703,7 @@
91285B1C26990D7F0051B544 /* ParsePolygon.swift in Sources */,
91BB8FCA2690AC99005A6BA5 /* QueryViewModel.swift in Sources */,
7085DD9426CBF3A70033B977 /* Documentation.docc in Sources */,
7087A93C28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */,
705025EB285153BC008D6624 /* ParsePushApplePayloadable.swift in Sources */,
705025A928441C96008D6624 /* ParseFieldOptions.swift in Sources */,
F97B45D624D9C6F200F4A88B /* ParseEncoder.swift in Sources */,
Expand Down Expand Up @@ -3006,6 +3013,7 @@
91285B1D26990D7F0051B544 /* ParsePolygon.swift in Sources */,
91BB8FCB2690AC99005A6BA5 /* QueryViewModel.swift in Sources */,
7085DD9526CBF3A70033B977 /* Documentation.docc in Sources */,
7087A93D28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */,
705025EC285153BC008D6624 /* ParsePushApplePayloadable.swift in Sources */,
705025AA28441C96008D6624 /* ParseFieldOptions.swift in Sources */,
F97B45D724D9C6F200F4A88B /* ParseEncoder.swift in Sources */,
Expand Down Expand Up @@ -3447,6 +3455,7 @@
91679D67268E596300F71809 /* ParseVersion.swift in Sources */,
91285B1F26990D7F0051B544 /* ParsePolygon.swift in Sources */,
91BB8FCD2690AC99005A6BA5 /* QueryViewModel.swift in Sources */,
7087A93F28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */,
705025EE285153BC008D6624 /* ParsePushApplePayloadable.swift in Sources */,
705025AC28441C96008D6624 /* ParseFieldOptions.swift in Sources */,
7085DD9726CBF3A70033B977 /* Documentation.docc in Sources */,
Expand Down Expand Up @@ -3633,6 +3642,7 @@
91679D66268E596300F71809 /* ParseVersion.swift in Sources */,
91285B1E26990D7F0051B544 /* ParsePolygon.swift in Sources */,
91BB8FCC2690AC99005A6BA5 /* QueryViewModel.swift in Sources */,
7087A93E28C558CA00656E93 /* ParseOperation+keyPath.swift in Sources */,
705025ED285153BC008D6624 /* ParsePushApplePayloadable.swift in Sources */,
705025AB28441C96008D6624 /* ParseFieldOptions.swift in Sources */,
7085DD9626CBF3A70033B977 /* Documentation.docc in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,11 @@ public extension ParseUser {
completion(result)
}
} catch {
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
if let parseError = error as? ParseError {
completion(.failure(parseError))
} else {
let parseError = ParseError(code: .unknownError, message: error.localizedDescription)
completion(.failure(parseError))
}
completion(.failure(parseError))
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/ParseSwift/Documentation.docc/ParseSwift.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ To learn how to use or experiment with ParseSwift, you can run and edit the [Par

- ``ParseSwift/initialize(configuration:)``
- ``ParseSwift/initialize(applicationId:clientKey:masterKey:serverURL:liveQueryServerURL:allowingCustomObjectIds:usingTransactions:usingEqualQueryConstraint:usingPostForQuery:keyValueStore:requestCachePolicy:cacheMemoryCapacity:cacheDiskCapacity:usingDataProtectionKeychain:deletingKeychainIfNeeded:httpAdditionalHeaders:maxConnectionAttempts:authentication:)``
- ``ParseSwift/initialize(applicationId:clientKey:masterKey:serverURL:liveQueryServerURL:allowingCustomObjectIds:usingTransactions:usingEqualQueryConstraint:usingPostForQuery:keyValueStore:requestCachePolicy:cacheMemoryCapacity:cacheDiskCapacity:migratingFromObjcSDK:usingDataProtectionKeychain:deletingKeychainIfNeeded:httpAdditionalHeaders:maxConnectionAttempts:authentication:)``

9 changes: 4 additions & 5 deletions Sources/ParseSwift/Extensions/URLSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,10 @@ internal extension URLSession {
let data = try ParseCoding.jsonEncoder().encode(location)
return try .success(mapper(data))
} catch {
guard let parseError = error as? ParseError else {
return .failure(ParseError(code: .unknownError,
// swiftlint:disable:next line_length
message: "Error decoding parse-server response: \(response) with error: \(String(describing: error))"))
}
let defaultError = ParseError(code: .unknownError,
// swiftlint:disable:next line_length
message: "Error decoding parse-server response: \(response) with error: \(String(describing: error))")
let parseError = error as? ParseError ?? defaultError
return .failure(parseError)
}
}
Expand Down
71 changes: 33 additions & 38 deletions Sources/ParseSwift/Objects/ParseInstallation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

/**
Objects that conform to the `ParseInstallation` protocol have a local representation of an
installation persisted to the Parse cloud. This protocol inherits from the
installation persisted to the Keychain and Parse Server. This protocol inherits from the
`ParseObject` protocol, and retains the same functionality of a `ParseObject`, but also extends
it with installation-specific fields and related immutability and validity
checks.
Expand All @@ -21,16 +21,15 @@ import Foundation
is automatically updated to match the device's time zone
when the `ParseInstallation` is saved, thus these fields might not reflect the
latest device state if the installation has not recently been saved.
`ParseInstallation`s which have a valid `deviceToken` and are saved to
the Parse Server can be used to target push notifications. Use `setDeviceToken` to set the
`deviceToken` properly.
- warning: If the use of badge is desired, it should be retrieved by using UIKit, AppKit, etc. and
stored in `ParseInstallation.badge` before saving/updating the installation.
- warning: Linux developers should set `appName`, `appIdentifier`, and `appVersion`
manually as `ParseSwift` does not have access to Bundle.main.
stored in `ParseInstallation.badge` when saving/updating the installation.
- warning: Linux, Android, and Windows developers should set `appName`,
`appIdentifier`, and `appVersion` manually as `ParseSwift` does not have access
to Bundle.main.
*/
public protocol ParseInstallation: ParseObject {

Expand Down Expand Up @@ -479,13 +478,12 @@ extension ParseInstallation {
try Self.updateKeychainIfNeeded([foundResult])
completion(.success(foundResult))
} catch {
let returnError: ParseError!
if let parseError = error as? ParseError {
returnError = parseError
} else {
returnError = ParseError(code: .unknownError, message: error.localizedDescription)
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
completion(.failure(parseError))
}
completion(.failure(returnError))
}
} else {
completion(result)
Expand Down Expand Up @@ -533,6 +531,7 @@ extension ParseInstallation {
- returns: Returns saved `ParseInstallation`.
- important: If an object saved has the same objectId as current, it will automatically update the current.
*/
@discardableResult
public func save(options: API.Options = []) throws -> Self {
try save(ignoringCustomObjectIdConfig: false,
options: options)
Expand Down Expand Up @@ -560,6 +559,7 @@ extension ParseInstallation {
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
@discardableResult
public func save(ignoringCustomObjectIdConfig: Bool,
options: API.Options = []) throws -> Self {
var options = options
Expand Down Expand Up @@ -724,12 +724,11 @@ extension ParseInstallation {
completion(result)
}
} catch {
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
if let parseError = error as? ParseError {
completion(.failure(parseError))
} else {
completion(.failure(.init(code: .unknownError, message: error.localizedDescription)))
}
completion(.failure(parseError))
}
}
return
Expand Down Expand Up @@ -862,13 +861,12 @@ extension ParseInstallation {
try Self.updateKeychainIfNeeded([self], deleting: true)
completion(.success(()))
} catch {
let returnError: ParseError!
if let parseError = error as? ParseError {
returnError = parseError
} else {
returnError = ParseError(code: .unknownError, message: error.localizedDescription)
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
completion(.failure(parseError))
}
completion(.failure(returnError))
}
case .failure(let error):
completion(.failure(error))
Expand Down Expand Up @@ -937,6 +935,7 @@ public extension Sequence where Element: ParseInstallation {
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
@discardableResult
func saveAll(batchLimit limit: Int? = nil, // swiftlint:disable:this function_body_length
transaction: Bool = configuration.isUsingTransactions,
ignoringCustomObjectIdConfig: Bool = false,
Expand Down Expand Up @@ -1232,12 +1231,11 @@ public extension Sequence where Element: ParseInstallation {
commands.append(try installation.updateCommand())
}
} catch {
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
if let parseError = error as? ParseError {
completion(.failure(parseError))
} else {
completion(.failure(.init(code: .unknownError, message: error.localizedDescription)))
}
completion(.failure(parseError))
}
return
}
Expand Down Expand Up @@ -1275,12 +1273,11 @@ public extension Sequence where Element: ParseInstallation {
}
}
} catch {
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
if let parseError = error as? ParseError {
completion(.failure(parseError))
} else {
completion(.failure(.init(code: .unknownError, message: error.localizedDescription)))
}
completion(.failure(parseError))
}
}
}
Expand Down Expand Up @@ -1499,12 +1496,10 @@ public extension Sequence where Element: ParseInstallation {
}
}
} catch {
let defaultError = ParseError(code: .unknownError,
message: error.localizedDescription)
let parseError = error as? ParseError ?? defaultError
callbackQueue.async {
guard let parseError = error as? ParseError else {
completion(.failure(ParseError(code: .unknownError,
message: error.localizedDescription)))
return
}
completion(.failure(parseError))
}
}
Expand Down
Loading

0 comments on commit d94dcad

Please sign in to comment.