Skip to content

Commit

Permalink
feat: Add the ability to access ParseConfig as a dictionary (#68)
Browse files Browse the repository at this point in the history
* wip

* feat: Add the ability to access ParseConfig as a dictionary

* nits

* nits
  • Loading branch information
cbaker6 authored Mar 10, 2023
1 parent 11bb8e6 commit 6a3176e
Show file tree
Hide file tree
Showing 26 changed files with 1,171 additions and 265 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* _Contributing to this repo? Add info about your change here to be included in the next release_

__New features__
* Add ParseConfigCodable type that allows access to the Parse Server Config as a dictionary. ParseConfigCodable and types that conform to ParseConfig are interoperable ([#68](https://github.com/netreconlab/Parse-Swift/pull/68)), thanks to [Corey Baker](https://github.com/cbaker6).
* Make it easier to login with email or authData ([#67](https://github.com/netreconlab/Parse-Swift/pull/67)), thanks to [Corey Baker](https://github.com/cbaker6).
* Add missing async/await and Combine ParseUser.become() type methods ([#66](https://github.com/netreconlab/Parse-Swift/pull/66)), thanks to [Corey Baker](https://github.com/cbaker6).

Expand Down
52 changes: 49 additions & 3 deletions ParseSwift.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseInstallation+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ internal extension ParseInstallation {
callbackQueue: callbackQueue,
childObjects: savedChildObjects,
childFiles: savedChildFiles)
try? await Self.updateKeychainIfNeeded([saved])
try? await Self.updateStorageIfNeeded([saved])
return saved
} catch {
throw error as? ParseError ?? ParseError(swift: error)
Expand Down Expand Up @@ -424,7 +424,7 @@ internal extension Sequence where Element: ParseInstallation {
childFiles: childFiles)
returnBatch.append(contentsOf: saved)
}
try? await Self.Element.updateKeychainIfNeeded(returnBatch.compactMap {try? $0.get()})
try? await Self.Element.updateStorageIfNeeded(returnBatch.compactMap {try? $0.get()})
return returnBatch
} catch {
throw error as? ParseError ?? ParseError(swift: error)
Expand Down
14 changes: 7 additions & 7 deletions Sources/ParseSwift/Objects/ParseInstallation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ public extension ParseInstallation {
await Self.setCurrentContainer(currentContainer)
}

internal static func deleteCurrentContainerFromKeychain() async {
internal static func deleteCurrentContainerFromStorage() async {
try? await ParseStorage.shared.delete(valueFor: ParseStorage.Keys.currentInstallation)
#if !os(Linux) && !os(Android) && !os(Windows)
try? await KeychainStore.shared.delete(valueFor: ParseStorage.Keys.currentInstallation)
Expand Down Expand Up @@ -484,7 +484,7 @@ extension ParseInstallation {

// MARK: Fetchable
extension ParseInstallation {
internal static func updateKeychainIfNeeded(_ results: [Self], deleting: Bool = false) async throws {
internal static func updateStorageIfNeeded(_ results: [Self], deleting: Bool = false) async throws {
let currentInstallation = try await Self.current()
var foundCurrentInstallationObjects = results.filter { $0.hasSameInstallationId(as: currentInstallation) }
foundCurrentInstallationObjects = try foundCurrentInstallationObjects.sorted(by: {
Expand All @@ -499,7 +499,7 @@ extension ParseInstallation {
if !deleting {
await Self.setCurrent(foundCurrentInstallation)
} else {
await Self.deleteCurrentContainerFromKeychain()
await Self.deleteCurrentContainerFromStorage()
}
}
}
Expand Down Expand Up @@ -535,7 +535,7 @@ extension ParseInstallation {
if case .success(let foundResult) = result {
Task {
do {
try await Self.updateKeychainIfNeeded([foundResult])
try await Self.updateStorageIfNeeded([foundResult])
completion(.success(foundResult))
} catch {
let parseError = error as? ParseError ?? ParseError(swift: error)
Expand Down Expand Up @@ -834,7 +834,7 @@ extension ParseInstallation {
case .success:
Task {
do {
try await Self.updateKeychainIfNeeded([self], deleting: true)
try await Self.updateStorageIfNeeded([self], deleting: true)
completion(.success(()))
} catch {
let parseError = error as? ParseError ?? ParseError(swift: error)
Expand Down Expand Up @@ -1113,7 +1113,7 @@ public extension Sequence where Element: ParseInstallation {
}
let fetchedObjectsToReturn = fetchedObjectsToReturnMutable
Task {
try? await Self.Element.updateKeychainIfNeeded(fetchedObjects)
try? await Self.Element.updateStorageIfNeeded(fetchedObjects)
completion(.success(fetchedObjectsToReturn))
}
case .failure(let error):
Expand Down Expand Up @@ -1185,7 +1185,7 @@ public extension Sequence where Element: ParseInstallation {
if completed == (batches.count - 1) {
let returnBatchImmutable = returnBatch
Task {
try? await Self.Element.updateKeychainIfNeeded(self.compactMap {$0},
try? await Self.Element.updateStorageIfNeeded(self.compactMap {$0},
deleting: true)
completion(.success(returnBatchImmutable))
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseUser+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ internal extension ParseUser {
callbackQueue: callbackQueue,
childObjects: savedChildObjects,
childFiles: savedChildFiles)
try? await Self.updateKeychainIfNeeded([saved])
try? await Self.updateStorageIfNeeded([saved])
return saved
} catch {
throw error as? ParseError ?? ParseError(swift: error)
Expand Down Expand Up @@ -625,7 +625,7 @@ internal extension Sequence where Element: ParseUser {
childFiles: childFiles)
returnBatch.append(contentsOf: saved)
}
try? await Self.Element.updateKeychainIfNeeded(returnBatch.compactMap {try? $0.get()})
try? await Self.Element.updateStorageIfNeeded(returnBatch.compactMap {try? $0.get()})
return returnBatch
} catch {
throw error as? ParseError ?? ParseError(swift: error)
Expand Down
24 changes: 12 additions & 12 deletions Sources/ParseSwift/Objects/ParseUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ extension ParseUser {
}

static func deleteCurrentKeychain() async {
await deleteCurrentContainerFromKeychain()
await BaseParseInstallation.deleteCurrentContainerFromKeychain()
await ParseACL.deleteDefaultFromKeychain()
await BaseConfig.deleteCurrentContainerFromKeychain()
await deleteCurrentContainerFromStorage()
await BaseParseInstallation.deleteCurrentContainerFromStorage()
await ParseACL.deleteDefaultFromStorage()
await BaseConfig.deleteCurrentContainerFromStorage()
clearCache()
}
}
Expand Down Expand Up @@ -147,7 +147,7 @@ public extension ParseUser {
#endif
}

internal static func deleteCurrentContainerFromKeychain() async {
internal static func deleteCurrentContainerFromStorage() async {
try? await ParseStorage.shared.delete(valueFor: ParseStorage.Keys.currentUser)
#if !os(Linux) && !os(Android) && !os(Windows)
await URLSession.liveQuery.closeAll()
Expand Down Expand Up @@ -399,7 +399,7 @@ extension ParseUser {
if let current = try? await Self.current() {
let isAnonymous = await self.anonymous.isLinked()
if !current.hasSameObjectId(as: user) && isAnonymous {
await Self.deleteCurrentContainerFromKeychain()
await Self.deleteCurrentContainerFromStorage()
}
}

Expand Down Expand Up @@ -777,7 +777,7 @@ extension ParseUser {

// MARK: Fetchable
extension ParseUser {
internal static func updateKeychainIfNeeded(_ results: [Self], deleting: Bool = false) async throws {
internal static func updateStorageIfNeeded(_ results: [Self], deleting: Bool = false) async throws {
let currentUser = try await Self.current()
var foundCurrentUserObjects = results.filter { $0.hasSameObjectId(as: currentUser) }
foundCurrentUserObjects = try foundCurrentUserObjects.sorted(by: {
Expand All @@ -792,7 +792,7 @@ extension ParseUser {
if !deleting {
try await Self.setCurrent(foundCurrentUser)
} else {
await Self.deleteCurrentContainerFromKeychain()
await Self.deleteCurrentContainerFromStorage()
}
}
}
Expand Down Expand Up @@ -826,7 +826,7 @@ extension ParseUser {
callbackQueue: callbackQueue) { result in
if case .success(let foundResult) = result {
Task {
try? await Self.updateKeychainIfNeeded([foundResult])
try? await Self.updateStorageIfNeeded([foundResult])
completion(.success(foundResult))
}
} else {
Expand Down Expand Up @@ -1127,7 +1127,7 @@ extension ParseUser {

case .success:
Task {
try? await Self.updateKeychainIfNeeded([self], deleting: true)
try? await Self.updateStorageIfNeeded([self], deleting: true)
completion(.success(()))
}
case .failure(let error):
Expand Down Expand Up @@ -1404,7 +1404,7 @@ public extension Sequence where Element: ParseUser {
}
let fetchedObjectsToReturn = fetchedObjectsToReturnMutable
Task {
try? await Self.Element.updateKeychainIfNeeded(fetchedObjects)
try? await Self.Element.updateStorageIfNeeded(fetchedObjects)
callbackQueue.async {
completion(.success(fetchedObjectsToReturn))
}
Expand Down Expand Up @@ -1478,7 +1478,7 @@ public extension Sequence where Element: ParseUser {
if completed == (batches.count - 1) {
let returnBatchImmutable = returnBatch
Task {
try? await Self.Element.updateKeychainIfNeeded(self.compactMap {$0},
try? await Self.Element.updateStorageIfNeeded(self.compactMap {$0},
deleting: true)
callbackQueue.async {
completion(.success(returnBatchImmutable))
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public func initialize(configuration: ParseConfiguration) async throws { // swif
// Migrate old installations made with ParseSwift < 1.3.0
if let currentInstallation = await BaseParseInstallation.currentContainer().currentInstallation {
if currentInstallation.objectId == nil {
await BaseParseInstallation.deleteCurrentContainerFromKeychain()
await BaseParseInstallation.deleteCurrentContainerFromStorage()
// Prepare installation
await BaseParseInstallation.createNewInstallationIfNeeded()
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/ParseConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

enum ParseConstants {
static let sdk = "swift"
static let version = "5.0.1"
static let version = "5.1.0"
static let fileManagementDirectory = "parse/"
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
static let fileManagementLibraryDirectory = "Library/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Foundation
Objects that conform to the `ParseConfig` protocol are able to access the Config on the Parse Server.
When conforming to `ParseConfig`, any properties added can be retrieved by the client or updated on
the server. The current `ParseConfig` is persisted to the Keychain and Parse Server.
- note: Stored and fetched versions `ParseConfigCodable` and types that conform
`ParseConfig`are interoperable and access the same Config.
*/
public protocol ParseConfig: ParseTypeable {}

Expand Down Expand Up @@ -45,7 +47,7 @@ extension ParseConfig {
return API.NonParseBodyCommand(method: .GET,
path: .config) { (data) -> Self in
let fetched = try ParseCoding.jsonDecoder().decode(ConfigFetchResponse<Self>.self, from: data).params
await Self.updateKeychainIfNeeded(fetched)
await Self.updateStorageIfNeeded(fetched)
return fetched
}
}
Expand Down Expand Up @@ -85,7 +87,7 @@ extension ParseConfig {
let updated = try ParseCoding.jsonDecoder().decode(BooleanResponse.self, from: data).result

if updated {
await Self.updateKeychainIfNeeded(self)
await Self.updateStorageIfNeeded(self)
}
return updated
}
Expand Down Expand Up @@ -123,15 +125,15 @@ public extension ParseConfig {
#endif
}

internal static func updateKeychainIfNeeded(_ result: Self, deleting: Bool = false) async {
internal static func updateStorageIfNeeded(_ result: Self, deleting: Bool = false) async {
if !deleting {
await Self.setCurrent(result)
} else {
await Self.deleteCurrentContainerFromKeychain()
await Self.deleteCurrentContainerFromStorage()
}
}

internal static func deleteCurrentContainerFromKeychain() async {
internal static func deleteCurrentContainerFromStorage() async {
try? await ParseStorage.shared.delete(valueFor: ParseStorage.Keys.currentConfig)
#if !os(Linux) && !os(Android) && !os(Windows)
try? await KeychainStore.shared.delete(valueFor: ParseStorage.Keys.currentConfig)
Expand Down
3 changes: 2 additions & 1 deletion Sources/ParseSwift/Types/ParseACL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,13 @@ extension ParseACL {
return modifiedACL
}

internal static func deleteDefaultFromKeychain() async {
internal static func deleteDefaultFromStorage() async {
try? await ParseStorage.shared.delete(valueFor: ParseStorage.Keys.defaultACL)
#if !os(Linux) && !os(Android) && !os(Windows)
try? await KeychainStore.shared.delete(valueFor: ParseStorage.Keys.defaultACL)
#endif
}

}

// Encoding and decoding
Expand Down
48 changes: 48 additions & 0 deletions Sources/ParseSwift/Types/ParseConfigCodable+async.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// ParseConfigCodable+async.swift
// ParseSwift
//
// Created by Corey Baker on 3/10/23.
// Copyright © 2023 Network Reconnaissance Lab. All rights reserved.
//

import Foundation

public extension ParseConfigCodable {

// MARK: Fetchable - Async/Await

/**
Fetch the Config *asynchronously*.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: The return type of self.
- throws: An error of type `ParseError`.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
static func fetch(options: API.Options = []) async throws -> [String: V] {
try await withCheckedThrowingContinuation { continuation in
Self.fetch(options: options,
completion: continuation.resume)
}
}

// MARK: Savable - Async/Await

/**
Update the Config *asynchronously*.
- parameter config: The Config to update on the server.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: **true** if saved, **false** if save is unsuccessful.
- throws: An error of type `ParseError`.
*/
static func save(_ config: [String: V],
options: API.Options = []) async throws -> Bool {
try await withCheckedThrowingContinuation { continuation in
Self.save(config,
options: options,
completion: continuation.resume)
}
}

}
48 changes: 48 additions & 0 deletions Sources/ParseSwift/Types/ParseConfigCodable+combine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// ParseConfigCodable+combine.swift
// ParseSwift
//
// Created by Corey Baker on 3/10/23.
// Copyright © 2023 Network Reconnaissance Lab. All rights reserved.
//

#if canImport(Combine)
import Foundation
import Combine

public extension ParseConfigCodable {

// MARK: Combine

/**
Fetch the Config *asynchronously*. Publishes when complete.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
static func fetchPublisher(options: API.Options = []) -> Future<[String: V], ParseError> {
Future { promise in
Self.fetch(options: options,
completion: promise)
}
}

/**
Update the Config *asynchronously*. Publishes when complete.
- parameter config: The Config to update on the server.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
*/
static func savePublisher(_ config: [String: V],
options: API.Options = []) -> Future<Bool, ParseError> {
Future { promise in
Self.save(config,
options: options,
completion: promise)
}
}

}

#endif
Loading

0 comments on commit 6a3176e

Please sign in to comment.