Skip to content

remove some direct references to the Keychain #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
286d038
add ACL tests with ACL fixes
cbaker6 Aug 22, 2020
f687de8
bump codecov
cbaker6 Aug 22, 2020
62d7947
Increase timeout
cbaker6 Aug 22, 2020
7cd68aa
reduce test threads
cbaker6 Aug 22, 2020
8fde3ce
Store ParseUser and default ACL to Keychain. Additional fixes:
cbaker6 Sep 6, 2020
82b8c09
Make logoutAsync thread test pass
cbaker6 Sep 6, 2020
c9d589e
Add more code coverage
cbaker6 Sep 6, 2020
e92ba7f
reduce codecov
cbaker6 Sep 6, 2020
656e8e3
Use BaseParseUser to get currentUser from Keychain
cbaker6 Sep 6, 2020
2f00074
Add initial documentation
cbaker6 Sep 6, 2020
b85cdfc
Bump minimum deployment for all OSs.
cbaker6 Sep 7, 2020
41285e5
Fix watchOS build
cbaker6 Sep 7, 2020
c9a841e
Really fix watchOS build
cbaker6 Sep 7, 2020
660e885
Bump OS versions in podspec
cbaker6 Sep 8, 2020
70347bb
Finish Installation support with unit tests.
cbaker6 Sep 9, 2020
92e800e
Add back old ParseUser threading test. Bump codecov
cbaker6 Sep 9, 2020
39ed8f1
Improve Keychain Installation tests
cbaker6 Sep 9, 2020
836f57e
Update installation in keychain whenever a badge update occurs
cbaker6 Sep 9, 2020
7a4f3fb
Only persist BaseParseUser and BaseInstallation values to keychain. A…
cbaker6 Sep 9, 2020
0544b04
Removed threadSafe SignUp, Login, and Logout since these are unrealis…
cbaker6 Sep 9, 2020
5dcefe7
Only use ParseInstall on main thread
cbaker6 Sep 11, 2020
70a26ad
Updates
cbaker6 Sep 11, 2020
e5a218f
update documentation
cbaker6 Sep 12, 2020
e07da5d
More updates to documentation
cbaker6 Sep 12, 2020
90dd572
Update README.md
cbaker6 Sep 12, 2020
cd2b6cd
Use Queryable protocol
cbaker6 Sep 12, 2020
25f966b
Merge remote-tracking branch 'refs/remotes/origin/acl'
cbaker6 Sep 12, 2020
82d22e4
Merge branch 'main'
cbaker6 Sep 12, 2020
2d03844
make links point to main branch
cbaker6 Sep 12, 2020
5b839ab
documentation fixes
TomWFox Sep 12, 2020
91ff90d
Change source of logo image to show in docs
cbaker6 Sep 12, 2020
159cbf0
Updates with broken login
cbaker6 Sep 13, 2020
6364814
fixed URL components contruction along with adding body. Added improv…
cbaker6 Sep 13, 2020
799b543
Add Install example to playgrounds.
cbaker6 Sep 13, 2020
973c8a1
Improve playgrounds and add ACL example
cbaker6 Sep 13, 2020
0ae3711
Switch ACL value type name to ParseACL as it causes issues with parse…
cbaker6 Sep 13, 2020
da0b08a
Fix ACL saving to parse-server
cbaker6 Sep 13, 2020
a146f2b
Partially fixed decoding ParseError from ParseServer. Still doesn't d…
cbaker6 Sep 13, 2020
285fa07
Fixed decoding ParseError from server
cbaker6 Sep 13, 2020
2f3c557
Updates to Query
cbaker6 Sep 14, 2020
c65e2c1
Added constants enum
cbaker6 Sep 14, 2020
cd1dc1a
Update constants enum
cbaker6 Sep 14, 2020
17c1285
remove some references to Keychain
pranjalsatija Sep 15, 2020
b0d79a1
Merge branch 'acl' of https://github.com/parse-community/Parse-Swift
pranjalsatija Sep 15, 2020
95042c4
update tests
pranjalsatija Sep 17, 2020
6d6dc58
Merge branch 'main' of https://github.com/parse-community/Parse-Swift
pranjalsatija Sep 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 22 additions & 25 deletions Sources/ParseSwift/Objects/ParseInstallation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,26 +94,22 @@ struct CurrentInstallationContainer<T: ParseInstallation>: Codable {
extension ParseInstallation {
static var currentInstallationContainer: CurrentInstallationContainer<Self> {
get {
guard let installationInMemory: CurrentInstallationContainer<Self> =
try? ParseStorage.shared.get(valueFor: ParseStorage.Keys.currentInstallation) else {
guard let installationFromKeyChain: CurrentInstallationContainer<Self> =
try? KeychainStore.shared.get(valueFor: ParseStorage.Keys.currentInstallation)
else {
var newInstallation = CurrentInstallationContainer<Self>()
let newInstallationId = UUID().uuidString.lowercased()
newInstallation.installationId = newInstallationId
newInstallation.currentInstallation?.createInstallationId(newId: newInstallationId)
newInstallation.currentInstallation?.updateAutomaticInfo()
try? KeychainStore.shared.set(newInstallation, for: ParseStorage.Keys.currentInstallation)
try? ParseStorage.shared.set(newInstallation, for: ParseStorage.Keys.currentInstallation)
return newInstallation
}
return installationFromKeyChain
if let installation: CurrentInstallationContainer<Self> =
try? ParseStorage.shared.secureStore.get(valueFor: ParseStorage.Keys.currentInstallation) {
return installation
} else {
var newInstallation = CurrentInstallationContainer<Self>()
let newInstallationId = UUID().uuidString.lowercased()
newInstallation.installationId = newInstallationId
newInstallation.currentInstallation?.createInstallationId(newId: newInstallationId)
newInstallation.currentInstallation?.updateAutomaticInfo()
try? ParseStorage.shared.secureStore.set(newInstallation, for: ParseStorage.Keys.currentInstallation)
return newInstallation
}
return installationInMemory
}

set {
try? ParseStorage.shared.set(newValue, for: ParseStorage.Keys.currentInstallation)
try? ParseStorage.shared.secureStore.set(newValue, for: ParseStorage.Keys.currentInstallation)
}
}

Expand All @@ -128,17 +124,18 @@ extension ParseInstallation {
Self.currentInstallationContainer.currentInstallation?.updateAutomaticInfo()
}

internal static func saveCurrentContainerToKeychain() {
//Only save the BaseParseInstallation to keep Keychain footprint finite
guard let currentInstallationInMemory: CurrentInstallationContainer<BaseParseInstallation>
= try? ParseStorage.shared.get(valueFor: ParseStorage.Keys.currentInstallation) else {
internal static func saveCurrentContainer() {
//Only save the BaseParseInstallation to keep memory footprint finite
guard let currentInstallation: CurrentInstallationContainer<BaseParseInstallation>
= try? ParseStorage.shared.secureStore.get(valueFor: ParseStorage.Keys.currentInstallation) else {
return
}
try? KeychainStore.shared.set(currentInstallationInMemory, for: ParseStorage.Keys.currentInstallation)

try? ParseStorage.shared.secureStore.set(currentInstallation, for: ParseStorage.Keys.currentInstallation)
}

/**
Gets/Sets properties of the current installation in the Keychain.
Gets/Sets properties of the current installation in the shared secure store.

- returns: Returns a `ParseInstallation` that is the current device. If there is none, returns `nil`.
*/
Expand Down Expand Up @@ -202,8 +199,8 @@ extension ParseInstallation {

if badge != applicationBadge {
badge = applicationBadge
//Since this changes, update the Keychain whenever it changes
Self.saveCurrentContainerToKeychain()
//Since this changes, update secure storage whenever it changes
Self.saveCurrentContainer()
}
}

Expand Down
14 changes: 7 additions & 7 deletions Sources/ParseSwift/Objects/ParseObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ extension ParseObject {

// MARK: Fetchable
extension ParseObject {
internal static func updateKeychainIfNeeded(_ results: [Self], saving: Bool = false) throws {
internal static func updateSecureStorageIfNeeded(_ results: [Self], saving: Bool = false) throws {
guard let currentUser = BaseParseUser.current else {
return
}
Expand All @@ -152,7 +152,7 @@ extension ParseObject {
let encoded = try ParseCoding.parseEncoder(skipKeys: false).encode(foundCurrentUser)
let updatedCurrentUser = try ParseCoding.jsonDecoder().decode(BaseParseUser.self, from: encoded)
BaseParseUser.current = updatedCurrentUser
BaseParseUser.saveCurrentContainerToKeychain()
BaseParseUser.saveCurrentUserContainer()
} else if results.first?.className == BaseParseInstallation.className {
guard let currentInstallation = BaseParseInstallation.current else {
return
Expand All @@ -169,7 +169,7 @@ extension ParseObject {
let updatedCurrentInstallation =
try ParseCoding.jsonDecoder().decode(BaseParseInstallation.self, from: encoded)
BaseParseInstallation.current = updatedCurrentInstallation
BaseParseInstallation.saveCurrentContainerToKeychain()
BaseParseInstallation.saveCurrentContainer()
}
}
}
Expand All @@ -182,7 +182,7 @@ extension ParseObject {
*/
public func fetch(options: API.Options = []) throws -> Self {
let result: Self = try fetchCommand().execute(options: options)
try? Self.updateKeychainIfNeeded([result])
try? Self.updateSecureStorageIfNeeded([result])
return result
}

Expand All @@ -203,7 +203,7 @@ extension ParseObject {
do {
try fetchCommand().executeAsync(options: options, callbackQueue: callbackQueue) { result in
if case .success(let foundResult) = result {
try? Self.updateKeychainIfNeeded([foundResult])
try? Self.updateSecureStorageIfNeeded([foundResult])
}
completion(result)
}
Expand Down Expand Up @@ -255,7 +255,7 @@ extension ParseObject {
*/
public func save(options: API.Options = []) throws -> Self {
let result: Self = try saveCommand().execute(options: options)
try? Self.updateKeychainIfNeeded([result], saving: true)
try? Self.updateSecureStorageIfNeeded([result], saving: true)
return result
}

Expand All @@ -274,7 +274,7 @@ extension ParseObject {
) {
saveCommand().executeAsync(options: options, callbackQueue: callbackQueue) { result in
if case .success(let foundResults) = result {
try? Self.updateKeychainIfNeeded([foundResults], saving: true)
try? Self.updateSecureStorageIfNeeded([foundResults], saving: true)
}
completion(result)
}
Expand Down
35 changes: 15 additions & 20 deletions Sources/ParseSwift/Objects/ParseUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,22 @@ struct CurrentUserContainer<T: ParseUser>: Codable {
// MARK: Current User Support
extension ParseUser {
static var currentUserContainer: CurrentUserContainer<Self>? {
get {
guard let currentUserInMemory: CurrentUserContainer<Self>
= try? ParseStorage.shared.get(valueFor: ParseStorage.Keys.currentUser) else {
return try? KeychainStore.shared.get(valueFor: ParseStorage.Keys.currentUser)
}
return currentUserInMemory
}
set { try? ParseStorage.shared.set(newValue, for: ParseStorage.Keys.currentUser) }
get { try? ParseStorage.shared.secureStore.get(valueFor: ParseStorage.Keys.currentUser) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may change the original intent. Basically in this manner, any change the user makes to currentUser is automatically saved to the secureStore. Before this PR, all changes are made in memory and are only stored to the secureStore (Keychain) if the user calls .save or a variation of it.

It seems like there's no way for the user to revert a change if they accidentally changed a value, and that same changed would have to sync to the parse-server at some point.

Copy link
Contributor Author

@pranjalsatija pranjalsatija Sep 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to clarify some stuff! I wasn't quite sure what the intent of the old code was, so I'll double check with you. The old code seems like it first tries to get the current installation from ParseStorage.shared and then tries to get it from the keychain, and if both fail, then it creates a new one.

The old implementation was relying on the fact that ParseStorage.shared is an in-memory store, which it shouldn't do: it's 100% possible that ParseStorage.shared (which is now ParseStorage.primitiveStore) is backed by the keychain, a file on disk, etc. That being said, I updated it thinking that the fallback to KeychainStore is only there in the event that the user didn't provide a shared store, which is no longer possible because the shared store isn't optional anymore.

As far as reverting changes goes, I don't think it's correct to say that the old version provided a way to revert changes. It just so happens to be the case that the store it was reading / writing from first was in memory, but there was (and still is) nothing enforcing that that's true. If a user updates the value, I think we should store it persistently immediately. AFAIK, The old SDK and other SDKs like Firebase doesn't support this either; we can't be liable if the user accidentally sets the wrong value, and they should take care to write code that doesn't set the wrong value for the current user / installation.

As a matter of fact, should the current installation and user even be publicly settable? We can do public(get) and internal(set) so the property is read-only to end users. I understand updating the current user but again, I don't think that should be done by doing ParseUser.current = ..., it should be done through signing in, logging out, updating the object and saving it, etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to clarify some stuff! I wasn't quite sure what the intent of the old code was, so I'll double check with you. The old code seems like it first tries to get the current installation from ParseStorage.shared and then tries to get it from the keychain, and if both fail, then it creates a new one.

This is true.

The old implementation was relying on the fact that ParseStorage.shared is an in-memory store, which it shouldn't do: it's 100% possible that ParseStorage.shared (which is now ParseStorage.primitiveStore) is backed by the keychain, a file on disk, etc. That being said, I updated it thinking that the fallback to KeychainStore is only there in the event that the user didn't provide a shared store, which is no longer possible because the shared store isn't optional anymore.

Kind of true... There wasn’t any persisted storage implemented. I used the in-memory ParseStore here on purpose so that changes to currentUser and currentInstallation happen in memory. When a “save” is successful, only then is it saved to the Keychain. This is done because there is currently no way to mark something is dirty and to determine when/how to sync with the parse-server. So the only way to confirm a change is as valid is after it’s committed to the parse-server.

As far as reverting changes goes, I don't think it's correct to say that the old version provided a way to revert changes. It just so happens to be the case that the store it was reading / writing from first was in memory, but there was (and still is) nothing enforcing that that's true. If a user updates the value, I think we should store it persistently immediately. AFAIK, The old SDK and other SDKs like Firebase doesn't support this either; we can't be liable if the user accidentally sets the wrong value, and they should take care to write code that doesn't set the wrong value for the current user / installation.

The old version provides a way to revert because only saves to the parse-server are committed to the Keychain. If the user exits the app and iOS clears the memory, the last saved version that’s in the keychain (which was last synced to the parse-server) are returned. Memory is only committed after a save. In addition, if a user adds “bad” values to memory, they can simply pull the last version from the keychain (reverting). My point here, if there’s currently no way to tell if something is dirty, we shouldn’t be storing the data locally (which is what your changes will allow). It will make everything to get out sync. If a “bad” value is saved to the parse-server, then a “bad” value is stored in the Keychain

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might have gotten lost in my responses, but I believe changes in the local storage should only be allowed when there are proper mechanisms in place to sync them to the parse-server. Until then, we shouldn’t persist changes locally until the parse-server says they have been committed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old SDK and other SDKs like Firebase doesn't support this either; we can't be liable if the user accidentally sets the wrong value, and they should take care to write code that doesn't set the wrong value for the current user / installation.

For the current version of the implementation. I didn’t take hints from the Obj-c SDK and I have no clue how Firebase works. In this situation, the local storage isn’t complete yet, so I’m not sure how they can be compared. If you have a SDK/app that relies on a centralized server (parse-server in this case), and you don’t have local storage, you should only commit after the centralized server tells you to. The obj-c has some form of local storage, so it doesn’t apply to this scenario

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then what if we update BaseParseUser and BaseParseInstallation to include an isConfirmed field or something that only gets set to true when the server has confirmed a change? And regardless of that result, until we properly implement local storage, should I make the setters internal only?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then what if we update BaseParseUser and BaseParseInstallation to include an isConfirmed field or something that only gets set to true when the server has confirmed a change?

This lets you know something has been changed locally, but how does it help synchronize the changes with the parse-server? I think syncing local changes need to be fully flushed out before allowing the user to change data locally. It will cause issues, for example, are the local changes older or newer than the parse-server? How do you know? In the current form (without full support of local storage and sync), the SDK works, with the limitation that data isn't changed until it's committed to the parse-server. This okay IMO for now until the completion of local storage.

And regardless of that result, until we properly implement local storage, should I make the setters internal only?

Sounds reasonable to me. The one thing I've been doing is using the playground files to make sure everything is correct on the parse-server. The unit-tests are good, but they are mocking the parse-server. If you aren't already, you should make sure your changes work via playgrounds with a real parse-server. I posted some basic instructions and docker container (has parse-dashboard also) just for this here.

set { try? ParseStorage.shared.secureStore.set(newValue, for: ParseStorage.Keys.currentUser)}
}

internal static func saveCurrentContainerToKeychain() {
//Only save the BaseParseUser to keep Keychain footprint finite
guard let currentUserInMemory: CurrentUserContainer<BaseParseUser>
= try? ParseStorage.shared.get(valueFor: ParseStorage.Keys.currentUser) else {
internal static func saveCurrentUserContainer() {
//Only save the BaseParseUser to keep memory footprint finite
guard let currentUser: CurrentUserContainer<BaseParseUser> =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to my previous comment, basically all changes are permanent before they are actually saved to the parse-server with no way to revert

try? ParseStorage.shared.secureStore.get(valueFor: ParseStorage.Keys.currentUser) else {
return
}
try? KeychainStore.shared.set(currentUserInMemory, for: ParseStorage.Keys.currentUser)

try? ParseStorage.shared.secureStore.set(currentUser, for: ParseStorage.Keys.currentUser)
}

/**
Gets the currently logged in user from the Keychain and returns an instance of it.
Gets the currently logged in user from the shared secure store and returns an instance of it.

- returns: Returns a `ParseUser` that is the currently logged in user. If there is none, returns `nil`.
*/
Expand Down Expand Up @@ -140,7 +135,7 @@ extension ParseUser {
currentUser: user,
sessionToken: response.sessionToken
)
Self.saveCurrentContainerToKeychain()
Self.saveCurrentUserContainer()
return user
}
}
Expand All @@ -150,7 +145,7 @@ extension ParseUser {
extension ParseUser {

/**
Logs out the currently logged in user in Keychain *synchronously*.
Logs out the currently logged in user in the shared secure store *synchronously*.
*/
public static func logout() throws {
_ = try logoutCommand().execute(options: [])
Expand All @@ -159,7 +154,7 @@ extension ParseUser {
/**
Logs out the currently logged in user *asynchronously*.

This will also remove the session from the Keychain, log out of linked services
This will also remove the session from the shared secure store, log out of linked services
and all future calls to `current` will return `nil`. This is preferable to using `logout`,
unless your code is already running from a background thread.

Expand All @@ -176,7 +171,7 @@ extension ParseUser {
private static func logoutCommand() -> API.Command<NoBody, Void> {
return API.Command(method: .POST, path: .logout) { (_) -> Void in
currentUserContainer = nil
try? KeychainStore.shared.delete(valueFor: ParseStorage.Keys.currentUser)
try? ParseStorage.shared.secureStore.delete(valueFor: ParseStorage.Keys.currentUser)
}
}
}
Expand Down Expand Up @@ -263,7 +258,7 @@ extension ParseUser {
currentUser: user,
sessionToken: response.sessionToken
)
Self.saveCurrentContainerToKeychain()
Self.saveCurrentUserContainer()
return user
}
}
Expand All @@ -279,7 +274,7 @@ extension ParseUser {
currentUser: user,
sessionToken: response.sessionToken
)
Self.saveCurrentContainerToKeychain()
Self.saveCurrentUserContainer()
return user
}
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/ParseSwift/Parse Types/ACL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,15 @@ public struct ParseACL: Codable, Equatable {
// Default ACL
extension ParseACL {
/**
Get the default ACL from the Keychain.
Get the default ACL from the shared secure store.

- returns: Returns the default ACL.
*/
public static func defaultACL() throws -> Self {

let currentUser = BaseParseUser.current
let aclController: DefaultACL? =
try? KeychainStore.shared.get(valueFor: ParseStorage.Keys.defaultACL)
try? ParseStorage.shared.secureStore.get(valueFor: ParseStorage.Keys.defaultACL)

if aclController != nil {
if !aclController!.useCurrentUser {
Expand Down Expand Up @@ -261,7 +261,7 @@ extension ParseACL {
DefaultACL(defaultACL: acl, lastCurrentUser: currentUser, useCurrentUser: withAccessForCurrentUser)
}

try? KeychainStore.shared.set(aclController, for: ParseStorage.Keys.defaultACL)
try? ParseStorage.shared.secureStore.set(aclController, for: ParseStorage.Keys.defaultACL)

return aclController.defaultACL
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal struct BaseParseInstallation: ParseInstallation {
var ACL: ParseACL?

init() {
//Force installation in keychain to be created if it hasn't already
//Force installation in secure store to be created if it hasn't already
Self.current = self
}
}
8 changes: 4 additions & 4 deletions Sources/ParseSwift/Parse Types/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ extension Query: Queryable {
*/
public func find(options: API.Options = []) throws -> [ResultType] {
let foundResults = try findCommand().execute(options: options)
try? ResultType.updateKeychainIfNeeded(foundResults)
try? ResultType.updateSecureStorageIfNeeded(foundResults)
return foundResults
}

Expand All @@ -254,7 +254,7 @@ extension Query: Queryable {
completion: @escaping (Result<[ResultType], ParseError>) -> Void) {
findCommand().executeAsync(options: options, callbackQueue: callbackQueue) { results in
if case .success(let foundResults) = results {
try? ResultType.updateKeychainIfNeeded(foundResults)
try? ResultType.updateSecureStorageIfNeeded(foundResults)
}
completion(results)
}
Expand All @@ -273,7 +273,7 @@ extension Query: Queryable {
public func first(options: API.Options = []) throws -> ResultType? {
let result = try firstCommand().execute(options: options)
if let foundResult = result {
try? ResultType.updateKeychainIfNeeded([foundResult])
try? ResultType.updateSecureStorageIfNeeded([foundResult])
}
return result
}
Expand All @@ -300,7 +300,7 @@ extension Query: Queryable {
completion(.failure(ParseError(code: .unknownError, message: "unable to unwrap data") ))
return
}
try? ResultType.updateKeychainIfNeeded([first])
try? ResultType.updateSecureStorageIfNeeded([first])
completion(.success(first))
case .failure(let error):
completion(.failure(error))
Expand Down
7 changes: 5 additions & 2 deletions Sources/ParseSwift/Parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public func initialize(
clientKey: String? = nil,
masterKey: String? = nil,
serverURL: URL,
primitiveObjectStore: PrimitiveObjectStore? = nil
primitiveObjectStore: PrimitiveObjectStore? = nil,
secureStore: PrimitiveObjectStore? = nil
) {
ParseConfiguration.applicationId = applicationId
ParseConfiguration.clientKey = clientKey
Expand All @@ -23,7 +24,9 @@ public func initialize(
.filter { $0 != "/" }
.joined(separator: "/")

ParseStorage.shared.use(primitiveObjectStore ?? CodableInMemoryPrimitiveObjectStore())
ParseStorage.shared.secureStore = secureStore ?? KeychainStore.shared
ParseStorage.shared.primitiveStore = primitiveObjectStore ?? CodableInMemoryPrimitiveObjectStore()

DispatchQueue.main.async {
_ = BaseParseInstallation()
}
Expand Down
36 changes: 2 additions & 34 deletions Sources/ParseSwift/Storage/ParseStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,12 @@
public struct ParseStorage {
public static var shared = ParseStorage()

private var backingStore: PrimitiveObjectStore!

mutating func use(_ store: PrimitiveObjectStore) {
self.backingStore = store
}

private mutating func requireBackingStore() {
guard backingStore != nil else {
print("You can't use ParseStorage without a backing store. An in-memory store is being used as a fallback.")
return
}
}
var primitiveStore: PrimitiveObjectStore = CodableInMemoryPrimitiveObjectStore()
var secureStore: PrimitiveObjectStore = KeychainStore.shared

enum Keys {
static let currentUser = "_currentUser"
static let currentInstallation = "_currentInstallation"
static let defaultACL = "_defaultACL"
}
}

// MARK: PrimitiveObjectStore
extension ParseStorage: PrimitiveObjectStore {
public mutating func delete(valueFor key: String) throws {
requireBackingStore()
return try backingStore.delete(valueFor: key)
}

public mutating func deleteAll() throws {
requireBackingStore()
return try backingStore.deleteAll()
}
public mutating func get<T>(valueFor key: String) throws -> T? where T: Decodable {
requireBackingStore()
return try backingStore.get(valueFor: key)
}

public mutating func set<T>(_ object: T, for key: String) throws where T: Encodable {
requireBackingStore()
return try backingStore.set(object, for: key)
}
}
3 changes: 1 addition & 2 deletions Tests/ParseSwiftTests/ACLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ class ACLTests: XCTestCase {

override func tearDown() {
super.tearDown()
try? KeychainStore.shared.deleteAll()
try? ParseStorage.shared.deleteAll()
try? ParseStorage.shared.secureStore.deleteAll()
}

struct User: ParseUser {
Expand Down
3 changes: 1 addition & 2 deletions Tests/ParseSwiftTests/APICommandTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ class APICommandTests: XCTestCase {
override func tearDown() {
super.tearDown()
MockURLProtocol.removeAll()
try? KeychainStore.shared.deleteAll()
try? ParseStorage.shared.deleteAll()
try? ParseStorage.shared.secureStore.deleteAll()
}

func testExecuteCorrectly() {
Expand Down
Loading