diff --git a/CHANGELOG.md b/CHANGELOG.md index 863c68c89..427a68268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ [Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.0.1...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift) * _Contributing to this repo? Add info about your change here to be included in the next release_ +__New features__ +* 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). + ### 5.0.1 [Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.0.0...5.0.1), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.0.1/documentation/parseswift) diff --git a/Sources/ParseSwift/Objects/ParseUser+async.swift b/Sources/ParseSwift/Objects/ParseUser+async.swift index 372839c34..238c21388 100644 --- a/Sources/ParseSwift/Objects/ParseUser+async.swift +++ b/Sources/ParseSwift/Objects/ParseUser+async.swift @@ -99,6 +99,25 @@ public extension ParseUser { } } + /** + Logs in a `ParseUser` *asynchronously* with a session token. On success, this saves the logged in + `ParseUser`with this session to the keychain, so you can retrieve the currently logged in user using + *current*. + + - parameter sessionToken: The sessionToken of the user to login. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the logged in `ParseUser`. + - 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`. + */ + @discardableResult static func become(sessionToken: String, + options: API.Options = []) async throws -> Self { + try await withCheckedThrowingContinuation { continuation in + Self.become(sessionToken: sessionToken, options: options, completion: continuation.resume) + } + } + #if !os(Linux) && !os(Android) && !os(Windows) /** Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain. diff --git a/Sources/ParseSwift/Objects/ParseUser+combine.swift b/Sources/ParseSwift/Objects/ParseUser+combine.swift index 187c3b4ef..9f354d65f 100644 --- a/Sources/ParseSwift/Objects/ParseUser+combine.swift +++ b/Sources/ParseSwift/Objects/ParseUser+combine.swift @@ -96,6 +96,24 @@ public extension ParseUser { } } + /** + Logs in a `ParseUser` *asynchronously* with a session token. On success, this saves the logged in + `ParseUser`with this session to the keychain, so you can retrieve the currently logged in user using + *current*. + + - parameter sessionToken: The sessionToken of the user to login. + - 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 becomePublisher(sessionToken: String, + options: API.Options = []) -> Future { + Future { promise in + Self.become(sessionToken: sessionToken, options: options, completion: promise) + } + } + #if !os(Linux) && !os(Android) && !os(Windows) /** Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain. diff --git a/Tests/ParseSwiftTests/ParseUserCombineTests.swift b/Tests/ParseSwiftTests/ParseUserCombineTests.swift index c167665b7..66c14d116 100644 --- a/Tests/ParseSwiftTests/ParseUserCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseUserCombineTests.swift @@ -349,6 +349,73 @@ class ParseUserCombineTests: XCTestCase { // swiftlint:disable:this type_body_le wait(for: [expectation1], timeout: 20.0) } + func testBecomeTypeMethod() async throws { + try await login() + MockURLProtocol.removeAll() + + let user = try await User.current() + XCTAssertNotNil(user.objectId) + + var serverResponse = LoginSignupResponse() + serverResponse.createdAt = user.createdAt + serverResponse.updatedAt = user.updatedAt?.addingTimeInterval(+300) + serverResponse.sessionToken = "newValue" + serverResponse.username = "stop" + + var subscriptions = Set() + MockURLProtocol.mockRequests { _ in + do { + let encoded = try serverResponse.getEncoder().encode(serverResponse, skipKeys: .none) + return MockURLResponse(data: encoded, statusCode: 200) + } catch { + return nil + } + } + + let expectation1 = XCTestExpectation(description: "Become user1") + let expectation2 = XCTestExpectation(description: "Become user2") + let publisher = User.becomePublisher(sessionToken: serverResponse.sessionToken) + .sink(receiveCompletion: { result in + + if case let .failure(error) = result { + XCTFail(error.localizedDescription) + expectation2.fulfill() + } + expectation1.fulfill() + + }, receiveValue: { signedUp in + XCTAssertNotNil(signedUp) + XCTAssertNotNil(signedUp.createdAt) + XCTAssertNotNil(signedUp.updatedAt) + XCTAssertNotNil(signedUp.email) + XCTAssertNotNil(signedUp.username) + XCTAssertNil(signedUp.password) + XCTAssertNotNil(signedUp.objectId) + XCTAssertNotNil(signedUp.customKey) + XCTAssertNil(signedUp.ACL) + + Task { + do { + let userFromKeychain = try await BaseParseUser.current() + XCTAssertNotNil(userFromKeychain.createdAt) + XCTAssertNotNil(userFromKeychain.updatedAt) + XCTAssertNotNil(userFromKeychain.email) + XCTAssertNotNil(userFromKeychain.username) + XCTAssertNil(userFromKeychain.password) + XCTAssertNotNil(userFromKeychain.objectId) + XCTAssertNil(userFromKeychain.ACL) + } catch { + XCTFail(error.localizedDescription) + } + DispatchQueue.main.async { + expectation2.fulfill() + } + } + }) + publisher.store(in: &subscriptions) + wait(for: [expectation1], timeout: 20.0) + } + func testLogout() async throws { try await login() MockURLProtocol.removeAll() diff --git a/Tests/ParseSwiftTests/ParseUserTests.swift b/Tests/ParseSwiftTests/ParseUserTests.swift index 7ce48b4b5..1bd305c89 100644 --- a/Tests/ParseSwiftTests/ParseUserTests.swift +++ b/Tests/ParseSwiftTests/ParseUserTests.swift @@ -551,6 +551,53 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertNil(userFromKeychain.ACL) } + @MainActor + func testBecomeTypeMethod() async throws { + try await login() + MockURLProtocol.removeAll() + + let user = try await User.current() + + var serverResponse = LoginSignupResponse() + serverResponse.updatedAt = user.updatedAt?.addingTimeInterval(+300) + serverResponse.sessionToken = "newValue" + serverResponse.username = "stop" + + MockURLProtocol.mockRequests { _ in + do { + let encoded = try serverResponse.getEncoder().encode(serverResponse, skipKeys: .none) + return MockURLResponse(data: encoded, statusCode: 200) + } catch { + return nil + } + } + + guard let sessionToken = serverResponse.sessionToken else { + XCTFail("Should have unwrapped") + return + } + let signedUp = try await User.become(sessionToken: sessionToken) + XCTAssertNotNil(signedUp) + XCTAssertNotNil(signedUp.updatedAt) + XCTAssertNotNil(signedUp.email) + XCTAssertNotNil(signedUp.username) + XCTAssertNil(signedUp.password) + XCTAssertNotNil(signedUp.objectId) + XCTAssertNotNil(signedUp.customKey) + XCTAssertNil(signedUp.ACL) + + let userFromKeychain = try await BaseParseUser.current() + + XCTAssertNotNil(userFromKeychain.createdAt) + XCTAssertNotNil(userFromKeychain.updatedAt) + XCTAssertNotNil(userFromKeychain.email) + XCTAssertNotNil(userFromKeychain.username) + XCTAssertNil(userFromKeychain.password) + XCTAssertNotNil(userFromKeychain.objectId) + _ = try await BaseParseUser.sessionToken() + XCTAssertNil(userFromKeychain.ACL) + } + @MainActor func testLogutCommand() async throws { let command = User.logoutCommand()