Skip to content

Commit fb61b78

Browse files
cbaker6TomWFox
andauthored
Add combine publishers for all objects and types (#73)
* wip * Update * Update * Need test cases * updates * Update * Update authentication * Update * Fixed deleteAll * Working publishers * Fix Swift 5.2 closures and new cache for jazzy * Add minimum catalyst support, nits, prepare for release * Nit * Update Sources/ParseSwift/Types/ParseFile+combine.swift Co-authored-by: Tom Fox <[email protected]> * Bring Xcode, SPM, and Cocoapods minimum deployments to the same level: [.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v5)] Co-authored-by: Tom Fox <[email protected]>
1 parent 48a871e commit fb61b78

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+4996
-1163
lines changed

.github/workflows/ci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ jobs:
109109
uses: actions/cache@v2
110110
with:
111111
path: vendor/bundle
112-
key: ${{ runner.os }}-gem-v2-${{ hashFiles('**/Gemfile.lock') }}
112+
key: ${{ runner.os }}-gem-v3-${{ hashFiles('**/Gemfile.lock') }}
113113
restore-keys: |
114-
${{ runner.os }}-gem-v2
114+
${{ runner.os }}-gem-v3
115115
- name: Install Bundle
116116
run: |
117117
bundle config path vendor/bundle

.github/workflows/release.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ jobs:
2626
uses: actions/cache@v2
2727
with:
2828
path: vendor/bundle
29-
key: ${{ runner.os }}-gem-v2-${{ hashFiles('**/Gemfile.lock') }}
29+
key: ${{ runner.os }}-gem-v3-${{ hashFiles('**/Gemfile.lock') }}
3030
restore-keys: |
31-
${{ runner.os }}-gem-v2
31+
${{ runner.os }}-gem-v3
3232
- name: Install Bundle
3333
run: |
3434
bundle config path vendor/bundle

CHANGELOG.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
# Parse-Swift Changelog
22

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

7+
### 1.1.3
8+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.2...1.1.3)
9+
10+
__New features__
11+
- SwiftUI ready! ([#73](https://github.com/parse-community/Parse-Swift/pull/73)), thanks to [Corey Baker](https://github.com/cbaker6).
12+
13+
__Fixes__
14+
- Fixes some issues with `ParseUser.logout` ([#73](https://github.com/parse-community/Parse-Swift/pull/73)), thanks to [Corey Baker](https://github.com/cbaker6).
15+
716
### 1.1.2
817
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.1...1.1.2)
918

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import PackageDescription
44

55
let package = Package(
66
name: "ParseSwift",
7-
platforms: [.iOS(.v11), .macOS(.v10_13), .tvOS(.v11), .watchOS(.v4)],
7+
platforms: [.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v5)],
88
products: [
99
.library(
1010
name: "ParseSwift",

ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift

+6-5
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,13 @@ do {
238238
[scoreToFetch, score2ToFetch].deleteAll { result in
239239
switch result {
240240
case .success(let deletedScores):
241-
deletedScores.forEach { error in
242-
guard let error = error else {
243-
print("Successfully deleted scores")
244-
return
241+
deletedScores.forEach { result in
242+
switch result {
243+
case .success:
244+
print("Successfully deleted score")
245+
case .failure(let error):
246+
print("Error deleting: \(error)")
245247
}
246-
print("Error deleting: \(error)")
247248
}
248249
case .failure(let error):
249250
assertionFailure("Error deleting: \(error)")

ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift

+9-1
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,17 @@ User.current?.signup { result in
101101
}
102102
}
103103

104+
//: Logging out - synchronously.
105+
do {
106+
try User.logout()
107+
print("Successfully logged out")
108+
} catch let error {
109+
print("Error logging out: \(error)")
110+
}
111+
104112
//: Password Reset Request - synchronously.
105113
do {
106-
try User.verificationEmailRequest(email: "[email protected]")
114+
try User.verificationEmail(email: "[email protected]")
107115
print("Successfully requested verification email be sent")
108116
} catch let error {
109117
print("Error requesting verification email be sent: \(error)")

ParseSwift.playground/contents.xcplayground

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
<page name='8 - Pointers'/>
1212
<page name='9 - Files'/>
1313
<page name='10 - Cloud Code'/>
14-
<page name='11 - LiveQuery'/>
15-
<page name='12 - Roles and Relations'/>
16-
<page name='13 - Operations'/>
14+
<page name='11 - LiveQuery'/>
15+
<page name='12 - Roles and Relations'/>
16+
<page name='13 - Operations'/>
1717
<page name='14 - Config'/>
1818
</pages>
19-
</playground>
19+
</playground>

ParseSwift.podspec

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "ParseSwift"
3-
s.version = "1.1.2"
3+
s.version = "1.1.3"
44
s.summary = "Parse Pure Swift SDK"
55
s.homepage = "https://github.com/parse-community/Parse-Swift"
66
s.authors = {
@@ -10,10 +10,10 @@ Pod::Spec.new do |s|
1010
:git => "#{s.homepage}.git",
1111
:tag => "#{s.version}",
1212
}
13-
s.ios.deployment_target = "11.0"
13+
s.ios.deployment_target = "12.0"
1414
s.osx.deployment_target = "10.13"
15-
s.tvos.deployment_target = "11.0"
16-
s.watchos.deployment_target = "4.0"
15+
s.tvos.deployment_target = "12.0"
16+
s.watchos.deployment_target = "5.0"
1717
s.swift_versions = ['5.1', '5.2', '5.3']
1818
s.source_files = "Sources/ParseSwift/**/*.swift"
1919
s.license = {

ParseSwift.xcodeproj/project.pbxproj

+192-10
Large diffs are not rendered by default.

Scripts/jazzy.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ bundle exec jazzy \
55
--author_url http://parseplatform.org \
66
--github_url https://github.com/parse-community/Parse-Swift \
77
--root-url http://parseplatform.org/Parse-Swift/api/ \
8-
--module-version 1.1.2 \
8+
--module-version 1.1.3 \
99
--theme fullwidth \
1010
--skip-undocumented \
1111
--output ./docs/api \

Sources/ParseSwift/API/API+Commands.swift

+26-19
Original file line numberDiff line numberDiff line change
@@ -452,38 +452,41 @@ extension API.Command where T: ParseObject {
452452
}
453453

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

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

466-
let decodingType = [ParseError?].self
465+
let decodingType = [BatchResponseItem<NoBody>].self
467466
do {
468467
let responses = try ParseCoding.jsonDecoder().decode(decodingType, from: data)
469-
return responses.enumerated().map({ (object) -> ParseError? in
468+
return responses.enumerated().map({ (object) -> (Result<Void, ParseError>) in
470469
let response = responses[object.offset]
471-
if let error = response {
472-
return error
470+
if response.success != nil {
471+
return .success(())
473472
} else {
474-
return nil
473+
guard let parseError = response.error else {
474+
return .failure(ParseError(code: .unknownError, message: "unknown error"))
475+
}
476+
477+
return .failure(parseError)
475478
}
476479
})
477480
} catch {
478-
guard (try? ParseCoding.jsonDecoder().decode(NoBody.self, from: data)) != nil else {
479-
return [ParseError(code: .unknownError, message: "decoding error: \(error)")]
481+
guard let parseError = error as? ParseError else {
482+
return [(.failure(ParseError(code: .unknownError, message: "decoding error: \(error)")))]
480483
}
481-
return [nil]
484+
return [(.failure(parseError))]
482485
}
483486
}
484487

485488
let batchCommand = BatchCommandNoBody(requests: commands)
486-
return RESTBatchCommandNoBodyType<ParseError?>(method: .POST, path: .batch, body: batchCommand, mapper: mapper)
489+
return RESTBatchCommandNoBodyType<NoBody>(method: .POST, path: .batch, body: batchCommand, mapper: mapper)
487490
}
488491
}
489492

@@ -642,17 +645,21 @@ internal extension API {
642645

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

651-
return API.NonParseBodyCommand<NoBody, ParseError?>(
653+
return API.NonParseBodyCommand<NoBody, NoBody>(
652654
method: .DELETE,
653655
path: object.endpoint
654-
) { (data) -> ParseError? in
655-
try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data)
656+
) { (data) -> NoBody in
657+
let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data)
658+
if let error = error {
659+
throw error
660+
} else {
661+
return NoBody()
662+
}
656663
}
657664
}
658665
} // swiftlint:disable:this file_length

Sources/ParseSwift/API/API.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public struct API {
3131
case logout
3232
case file(fileName: String)
3333
case passwordReset
34-
case verificationEmailRequest
34+
case verificationEmail
3535
case functions(name: String)
3636
case jobs(name: String)
3737
case aggregate(className: String)
@@ -70,7 +70,7 @@ public struct API {
7070
return "/files/\(fileName)"
7171
case .passwordReset:
7272
return "/requestPasswordReset"
73-
case .verificationEmailRequest:
73+
case .verificationEmail:
7474
return "/verificationEmailRequest"
7575
case .functions(name: let name):
7676
return "/functions/\(name)"

Sources/ParseSwift/API/BatchUtils.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ typealias ParseObjectBatchResponse<T> = [(Result<T, ParseError>)]
1313
// swiftlint:disable line_length
1414
typealias RESTBatchCommandType<T> = API.Command<ParseObjectBatchCommand<T>, ParseObjectBatchResponse<T>> where T: ParseObject
1515

16-
typealias ParseObjectBatchCommandNoBody<T> = BatchCommandNoBody<NoBody, ParseError?>
17-
typealias ParseObjectBatchResponseNoBody<NoBody> = [ParseError?]
16+
typealias ParseObjectBatchCommandNoBody<T> = BatchCommandNoBody<NoBody, NoBody>
17+
typealias ParseObjectBatchResponseNoBody<NoBody> = [(Result<Void, ParseError>)]
1818
typealias RESTBatchCommandNoBodyType<T> = API.NonParseBodyCommand<ParseObjectBatchCommandNoBody<T>, ParseObjectBatchResponseNoBody<T>> where T: Encodable
1919
/*
2020
typealias ParseObjectBatchCommandEncodable<T> = BatchCommand<T, PointerType> where T: ParseType

Sources/ParseSwift/Authentication/3rd Party/ParseApple.swift

+73
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
//
88

99
import Foundation
10+
#if canImport(Combine)
11+
import Combine
12+
#endif
1013

1114
// swiftlint:disable line_length
1215

@@ -90,6 +93,41 @@ public extension ParseApple {
9093
callbackQueue: callbackQueue,
9194
completion: completion)
9295
}
96+
97+
#if canImport(Combine)
98+
99+
/**
100+
Login a `ParseUser` *asynchronously* using Apple authentication. Publishes when complete.
101+
- parameter user: The `user` from `ASAuthorizationAppleIDCredential`.
102+
- parameter identityToken: The `identityToken` from `ASAuthorizationAppleIDCredential`.
103+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
104+
- returns: A publisher that eventually produces a single value and then finishes or fails.
105+
*/
106+
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
107+
func loginPublisher(user: String,
108+
identityToken: String,
109+
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
110+
loginPublisher(authData: AuthenticationKeys.id.makeDictionary(user: user, identityToken: identityToken),
111+
options: options)
112+
}
113+
114+
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
115+
func loginPublisher(authData: [String: String]?,
116+
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
117+
guard AuthenticationKeys.id.verifyMandatoryKeys(authData: authData),
118+
let authData = authData else {
119+
let error = ParseError(code: .unknownError,
120+
message: "Should have authData in consisting of keys \"id\" and \"token\".")
121+
return Future { promise in
122+
promise(.failure(error))
123+
}
124+
}
125+
return AuthenticatedUser.loginPublisher(Self.__type,
126+
authData: authData,
127+
options: options)
128+
}
129+
130+
#endif
93131
}
94132

95133
// MARK: Link
@@ -133,6 +171,41 @@ public extension ParseApple {
133171
callbackQueue: callbackQueue,
134172
completion: completion)
135173
}
174+
175+
#if canImport(Combine)
176+
177+
/**
178+
Link the *current* `ParseUser` *asynchronously* using Apple authentication. Publishes when complete.
179+
- parameter user: The `user` from `ASAuthorizationAppleIDCredential`.
180+
- parameter identityToken: The `identityToken` from `ASAuthorizationAppleIDCredential`.
181+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
182+
- returns: A publisher that eventually produces a single value and then finishes or fails.
183+
*/
184+
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
185+
func linkPublisher(user: String,
186+
identityToken: String,
187+
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
188+
linkPublisher(authData: AuthenticationKeys.id.makeDictionary(user: user, identityToken: identityToken),
189+
options: options)
190+
}
191+
192+
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
193+
func linkPublisher(authData: [String: String]?,
194+
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
195+
guard AuthenticationKeys.id.verifyMandatoryKeys(authData: authData),
196+
let authData = authData else {
197+
let error = ParseError(code: .unknownError,
198+
message: "Should have authData in consisting of keys \"id\" and \"token\".")
199+
return Future { promise in
200+
promise(.failure(error))
201+
}
202+
}
203+
return AuthenticatedUser.linkPublisher(Self.__type,
204+
authData: authData,
205+
options: options)
206+
}
207+
208+
#endif
136209
}
137210

138211
// MARK: 3rd Party Authentication - ParseApple

Sources/ParseSwift/Authentication/Internal/ParseAnonymous.swift

+27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
//
88

99
import Foundation
10+
#if canImport(Combine)
11+
import Combine
12+
#endif
1013

1114
/**
1215
Provides utility functions for working with Anonymously logged-in users.
@@ -68,6 +71,18 @@ public extension ParseAnonymous {
6871
callbackQueue: callbackQueue,
6972
completion: completion)
7073
}
74+
75+
#if canImport(Combine)
76+
77+
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
78+
func loginPublisher(authData: [String: String]? = nil,
79+
options: API.Options = []) -> Future<AuthenticatedUser, ParseError> {
80+
AuthenticatedUser.loginPublisher(__type,
81+
authData: AuthenticationKeys.id.makeDictionary(),
82+
options: options)
83+
}
84+
85+
#endif
7186
}
7287

7388
// MARK: Link
@@ -81,6 +96,18 @@ public extension ParseAnonymous {
8196
completion(.failure(ParseError(code: .unknownError, message: "Not supported")))
8297
}
8398
}
99+
100+
#if canImport(Combine)
101+
102+
@available(macOS 10.15, iOS 13.0, macCatalyst 13.0, watchOS 6.0, tvOS 13.0, *)
103+
func linkPublisher(authData: [String: String]?,
104+
options: API.Options) -> Future<AuthenticatedUser, ParseError> {
105+
Future { promise in
106+
promise(.failure(ParseError(code: .unknownError, message: "Not supported")))
107+
}
108+
}
109+
110+
#endif
84111
}
85112

86113
// MARK: ParseAnonymous

0 commit comments

Comments
 (0)