Skip to content

Add combine publishers for all objects and types #73

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

Merged
merged 15 commits into from
Jan 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ jobs:
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-v2-${{ hashFiles('**/Gemfile.lock') }}
key: ${{ runner.os }}-gem-v3-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gem-v2
${{ runner.os }}-gem-v3
- name: Install Bundle
run: |
bundle config path vendor/bundle
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ jobs:
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-v2-${{ hashFiles('**/Gemfile.lock') }}
key: ${{ runner.os }}-gem-v3-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gem-v2
${{ runner.os }}-gem-v3
- name: Install Bundle
run: |
bundle config path vendor/bundle
Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
# Parse-Swift Changelog

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

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

__New features__
- SwiftUI ready! ([#73](https://github.com/parse-community/Parse-Swift/pull/73)), thanks to [Corey Baker](https://github.com/cbaker6).

__Fixes__
- Fixes some issues with `ParseUser.logout` ([#73](https://github.com/parse-community/Parse-Swift/pull/73)), thanks to [Corey Baker](https://github.com/cbaker6).

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

Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import PackageDescription

let package = Package(
name: "ParseSwift",
platforms: [.iOS(.v11), .macOS(.v10_13), .tvOS(.v11), .watchOS(.v4)],
platforms: [.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v5)],
products: [
.library(
name: "ParseSwift",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,13 @@ do {
[scoreToFetch, score2ToFetch].deleteAll { result in
switch result {
case .success(let deletedScores):
deletedScores.forEach { error in
guard let error = error else {
print("Successfully deleted scores")
return
deletedScores.forEach { result in
switch result {
case .success:
print("Successfully deleted score")
case .failure(let error):
print("Error deleting: \(error)")
}
print("Error deleting: \(error)")
}
case .failure(let error):
assertionFailure("Error deleting: \(error)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,17 @@ User.current?.signup { result in
}
}

//: Logging out - synchronously.
do {
try User.logout()
print("Successfully logged out")
} catch let error {
print("Error logging out: \(error)")
}

//: Password Reset Request - synchronously.
do {
try User.verificationEmailRequest(email: "[email protected]")
try User.verificationEmail(email: "[email protected]")
print("Successfully requested verification email be sent")
} catch let error {
print("Error requesting verification email be sent: \(error)")
Expand Down
8 changes: 4 additions & 4 deletions ParseSwift.playground/contents.xcplayground
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
<page name='8 - Pointers'/>
<page name='9 - Files'/>
<page name='10 - Cloud Code'/>
<page name='11 - LiveQuery'/>
<page name='12 - Roles and Relations'/>
<page name='13 - Operations'/>
<page name='11 - LiveQuery'/>
<page name='12 - Roles and Relations'/>
<page name='13 - Operations'/>
<page name='14 - Config'/>
</pages>
</playground>
</playground>
8 changes: 4 additions & 4 deletions ParseSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "ParseSwift"
s.version = "1.1.2"
s.version = "1.1.3"
s.summary = "Parse Pure Swift SDK"
s.homepage = "https://github.com/parse-community/Parse-Swift"
s.authors = {
Expand All @@ -10,10 +10,10 @@ Pod::Spec.new do |s|
:git => "#{s.homepage}.git",
:tag => "#{s.version}",
}
s.ios.deployment_target = "11.0"
s.ios.deployment_target = "12.0"
s.osx.deployment_target = "10.13"
s.tvos.deployment_target = "11.0"
s.watchos.deployment_target = "4.0"
s.tvos.deployment_target = "12.0"
s.watchos.deployment_target = "5.0"
s.swift_versions = ['5.1', '5.2', '5.3']
s.source_files = "Sources/ParseSwift/**/*.swift"
s.license = {
Expand Down
202 changes: 192 additions & 10 deletions ParseSwift.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Scripts/jazzy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ bundle exec jazzy \
--author_url http://parseplatform.org \
--github_url https://github.com/parse-community/Parse-Swift \
--root-url http://parseplatform.org/Parse-Swift/api/ \
--module-version 1.1.2 \
--module-version 1.1.3 \
--theme fullwidth \
--skip-undocumented \
--output ./docs/api \
Expand Down
45 changes: 26 additions & 19 deletions Sources/ParseSwift/API/API+Commands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,38 +452,41 @@ extension API.Command where T: ParseObject {
}

// MARK: Batch - Deleting
// swiftlint:disable:next line_length
static func batch(commands: [API.NonParseBodyCommand<NoBody, ParseError?>]) -> RESTBatchCommandNoBodyType<ParseError?> {
let commands = commands.compactMap { (command) -> API.NonParseBodyCommand<NoBody, ParseError?>? in
static func batch(commands: [API.NonParseBodyCommand<NoBody, NoBody>]) -> RESTBatchCommandNoBodyType<NoBody> {
let commands = commands.compactMap { (command) -> API.NonParseBodyCommand<NoBody, NoBody>? in
let path = ParseConfiguration.mountPath + command.path.urlComponent
return API.NonParseBodyCommand<NoBody, ParseError?>(
return API.NonParseBodyCommand<NoBody, NoBody>(
method: command.method,
path: .any(path), mapper: command.mapper)
}

let mapper = { (data: Data) -> [ParseError?] in
let mapper = { (data: Data) -> [(Result<Void, ParseError>)] in

let decodingType = [ParseError?].self
let decodingType = [BatchResponseItem<NoBody>].self
do {
let responses = try ParseCoding.jsonDecoder().decode(decodingType, from: data)
return responses.enumerated().map({ (object) -> ParseError? in
return responses.enumerated().map({ (object) -> (Result<Void, ParseError>) in
let response = responses[object.offset]
if let error = response {
return error
if response.success != nil {
return .success(())
} else {
return nil
guard let parseError = response.error else {
return .failure(ParseError(code: .unknownError, message: "unknown error"))
}

return .failure(parseError)
}
})
} catch {
guard (try? ParseCoding.jsonDecoder().decode(NoBody.self, from: data)) != nil else {
return [ParseError(code: .unknownError, message: "decoding error: \(error)")]
guard let parseError = error as? ParseError else {
return [(.failure(ParseError(code: .unknownError, message: "decoding error: \(error)")))]
}
return [nil]
return [(.failure(parseError))]
}
}

let batchCommand = BatchCommandNoBody(requests: commands)
return RESTBatchCommandNoBodyType<ParseError?>(method: .POST, path: .batch, body: batchCommand, mapper: mapper)
return RESTBatchCommandNoBodyType<NoBody>(method: .POST, path: .batch, body: batchCommand, mapper: mapper)
}
}

Expand Down Expand Up @@ -642,17 +645,21 @@ internal extension API {

internal extension API.NonParseBodyCommand {
// MARK: Deleting
// swiftlint:disable:next line_length
static func deleteCommand<T>(_ object: T) throws -> API.NonParseBodyCommand<NoBody, ParseError?> where T: ParseObject {
static func deleteCommand<T>(_ object: T) throws -> API.NonParseBodyCommand<NoBody, NoBody> where T: ParseObject {
guard object.isSaved else {
throw ParseError(code: .unknownError, message: "Cannot Delete an object without id")
}

return API.NonParseBodyCommand<NoBody, ParseError?>(
return API.NonParseBodyCommand<NoBody, NoBody>(
method: .DELETE,
path: object.endpoint
) { (data) -> ParseError? in
try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data)
) { (data) -> NoBody in
let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data)
if let error = error {
throw error
} else {
return NoBody()
}
}
}
} // swiftlint:disable:this file_length
4 changes: 2 additions & 2 deletions Sources/ParseSwift/API/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public struct API {
case logout
case file(fileName: String)
case passwordReset
case verificationEmailRequest
case verificationEmail
case functions(name: String)
case jobs(name: String)
case aggregate(className: String)
Expand Down Expand Up @@ -70,7 +70,7 @@ public struct API {
return "/files/\(fileName)"
case .passwordReset:
return "/requestPasswordReset"
case .verificationEmailRequest:
case .verificationEmail:
return "/verificationEmailRequest"
case .functions(name: let name):
return "/functions/\(name)"
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/API/BatchUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ typealias ParseObjectBatchResponse<T> = [(Result<T, ParseError>)]
// swiftlint:disable line_length
typealias RESTBatchCommandType<T> = API.Command<ParseObjectBatchCommand<T>, ParseObjectBatchResponse<T>> where T: ParseObject

typealias ParseObjectBatchCommandNoBody<T> = BatchCommandNoBody<NoBody, ParseError?>
typealias ParseObjectBatchResponseNoBody<NoBody> = [ParseError?]
typealias ParseObjectBatchCommandNoBody<T> = BatchCommandNoBody<NoBody, NoBody>
typealias ParseObjectBatchResponseNoBody<NoBody> = [(Result<Void, ParseError>)]
typealias RESTBatchCommandNoBodyType<T> = API.NonParseBodyCommand<ParseObjectBatchCommandNoBody<T>, ParseObjectBatchResponseNoBody<T>> where T: Encodable
/*
typealias ParseObjectBatchCommandEncodable<T> = BatchCommand<T, PointerType> where T: ParseType
Expand Down
73 changes: 73 additions & 0 deletions Sources/ParseSwift/Authentication/3rd Party/ParseApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
//

import Foundation
#if canImport(Combine)
import Combine
#endif

// swiftlint:disable line_length

Expand Down Expand Up @@ -90,6 +93,41 @@ public extension ParseApple {
callbackQueue: callbackQueue,
completion: completion)
}

#if canImport(Combine)

/**
Login a `ParseUser` *asynchronously* using Apple authentication. Publishes when complete.
- parameter user: The `user` from `ASAuthorizationAppleIDCredential`.
- parameter identityToken: The `identityToken` from `ASAuthorizationAppleIDCredential`.
- 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.
*/
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
func loginPublisher(user: String,
identityToken: String,
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
loginPublisher(authData: AuthenticationKeys.id.makeDictionary(user: user, identityToken: identityToken),
options: options)
}

@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
func loginPublisher(authData: [String: String]?,
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
guard AuthenticationKeys.id.verifyMandatoryKeys(authData: authData),
let authData = authData else {
let error = ParseError(code: .unknownError,
message: "Should have authData in consisting of keys \"id\" and \"token\".")
return Future { promise in
promise(.failure(error))
}
}
return AuthenticatedUser.loginPublisher(Self.__type,
authData: authData,
options: options)
}

#endif
}

// MARK: Link
Expand Down Expand Up @@ -133,6 +171,41 @@ public extension ParseApple {
callbackQueue: callbackQueue,
completion: completion)
}

#if canImport(Combine)

/**
Link the *current* `ParseUser` *asynchronously* using Apple authentication. Publishes when complete.
- parameter user: The `user` from `ASAuthorizationAppleIDCredential`.
- parameter identityToken: The `identityToken` from `ASAuthorizationAppleIDCredential`.
- 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.
*/
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
func linkPublisher(user: String,
identityToken: String,
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
linkPublisher(authData: AuthenticationKeys.id.makeDictionary(user: user, identityToken: identityToken),
options: options)
}

@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
func linkPublisher(authData: [String: String]?,
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
guard AuthenticationKeys.id.verifyMandatoryKeys(authData: authData),
let authData = authData else {
let error = ParseError(code: .unknownError,
message: "Should have authData in consisting of keys \"id\" and \"token\".")
return Future { promise in
promise(.failure(error))
}
}
return AuthenticatedUser.linkPublisher(Self.__type,
authData: authData,
options: options)
}

#endif
}

// MARK: 3rd Party Authentication - ParseApple
Expand Down
27 changes: 27 additions & 0 deletions Sources/ParseSwift/Authentication/Internal/ParseAnonymous.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
//

import Foundation
#if canImport(Combine)
import Combine
#endif

/**
Provides utility functions for working with Anonymously logged-in users.
Expand Down Expand Up @@ -68,6 +71,18 @@ public extension ParseAnonymous {
callbackQueue: callbackQueue,
completion: completion)
}

#if canImport(Combine)

@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
func loginPublisher(authData: [String: String]? = nil,
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
AuthenticatedUser.loginPublisher(__type,
authData: AuthenticationKeys.id.makeDictionary(),
options: options)
}

#endif
}

// MARK: Link
Expand All @@ -81,6 +96,18 @@ public extension ParseAnonymous {
completion(.failure(ParseError(code: .unknownError, message: "Not supported")))
}
}

#if canImport(Combine)

@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
func linkPublisher(authData: [String: String]?,
options: API.Options) -> Future<AuthenticatedUser, ParseError> {
Future { promise in
promise(.failure(ParseError(code: .unknownError, message: "Not supported")))
}
}

#endif
}

// MARK: ParseAnonymous
Expand Down
Loading