Skip to content

Commit 209945c

Browse files
authored
Migrate from callbacks to async/await (Swift 6 concurrency) (#21)
2 parents c61de84 + f3b6a12 commit 209945c

Some content is hidden

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

46 files changed

+864
-951
lines changed

OAuth2.xcodeproj/project.pbxproj

Lines changed: 38 additions & 12 deletions
Large diffs are not rendered by default.

Package.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import PackageDescription
2424
let package = Package(
2525
name: "OAuth2",
2626
platforms: [
27-
.macOS(.v10_15), .iOS(.v12), .tvOS(.v12), .watchOS(.v5)
27+
.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)
2828
],
2929
products: [
3030
.library(name: "OAuth2", targets: ["OAuth2"]),
@@ -43,8 +43,9 @@ let package = Package(
4343
.target(name: "Flows", dependencies: [
4444
.target(name: "macOS"), .target(name: "iOS"), .target(name: "tvOS"), .target(name: "Constants")]),
4545
.target(name: "DataLoader", dependencies: [.target(name: "Flows")]),
46-
.testTarget(name: "BaseTests", dependencies: [.target(name: "Base"), .target(name: "Flows")]),
47-
.testTarget(name: "FlowTests", dependencies: [.target(name: "Flows")]),
46+
.target(name: "TestUtils", dependencies: [.target(name: "Base")]),
47+
.testTarget(name: "BaseTests", dependencies: [.target(name: "TestUtils"), .target(name: "Base"), .target(name: "Flows")]),
48+
.testTarget(name: "FlowTests", dependencies: [.target(name: "TestUtils"), .target(name: "Flows")]),
4849
// .testTarget(name: "DataLoaderTests", dependencies: [.target(name: "DataLoader")]),
4950
]
5051
)

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ OAuth2 frameworks for **macOS**, **iOS** and **tvOS** written in Swift 5.
1111
- [🖥 Sample macOS app][sample] (with data loader examples)
1212
- [📖 Technical Documentation](https://p2.github.io/OAuth2)
1313

14-
OAuth2 requires Xcode 12.4, the built framework can be used on **OS X 10.15** or **iOS 12** and later.
14+
OAuth2 requires Xcode 12.4, the built framework can be used on **OS X 10.15**, **iOS 13**, **tvOS 13**, **watchOS 6** and later.
1515
Happy to accept pull requests, please see [CONTRIBUTING.md](./Docs/CONTRIBUTING.md)
1616

1717
### Swift Version
@@ -374,7 +374,6 @@ let oauth2 = OAuth2CodeGrant(settings: [
374374
- [Facebook](https://github.com/p2/OAuth2/wiki/Facebook)
375375
- [Reddit](https://github.com/p2/OAuth2/wiki/Reddit)
376376
- [Google](https://github.com/p2/OAuth2/wiki/Google)
377-
- [LinkedIn](https://github.com/p2/OAuth2/wiki/LinkedIn)
378377
- [Instagram, Bitly, Pinterest, ...](https://github.com/p2/OAuth2/wiki/Instagram,-Bitly,-Pinterest-and-others)
379378
- [Uber](https://github.com/p2/OAuth2/wiki/Uber)
380379
- [BitBucket](https://github.com/p2/OAuth2/wiki/BitBucket)

Sources/Base/OAuth2Actor.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@globalActor public actor OAuth2Actor : GlobalActor {
2+
public static let shared = OAuth2Actor()
3+
}

Sources/Base/OAuth2AuthConfig.swift

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,14 @@ import UIKit
2626
/**
2727
Simple struct to hold settings describing how authorization appears to the user.
2828
*/
29-
public struct OAuth2AuthConfig {
29+
public struct OAuth2AuthConfig: Sendable {
3030

3131
/// Sub-stuct holding configuration relevant to UI presentation.
32-
public struct UI {
32+
public struct UI: Sendable {
3333

3434
/// Title to propagate to views handled by OAuth2, such as OAuth2WebViewController.
3535
public var title: String? = nil
3636

37-
/// By assigning your own UIBarButtonItem (!) you can override the back button that is shown in the iOS embedded web view (does NOT apply to `SFSafariViewController` or `ASWebAuthenticationSession`).
38-
@available(*, deprecated, message: "This will be removed in v6.")
39-
public var backButton: AnyObject? = nil
40-
41-
/// If true it makes the login cancellable, otherwise the cancel button is not shown in the embedded web view.
42-
@available(*, deprecated, message: "This will be removed in v6.")
43-
public var showCancelButton = true
44-
45-
/// Starting with iOS 9, `SFSafariViewController` will be used for embedded authorization instead of our custom class. You can turn this off here.
46-
public var useSafariView = false
47-
4837
/// Starting with iOS 12, `ASWebAuthenticationSession` can be used for embedded authorization instead of our custom class. You can turn this on here.
4938
public var useAuthenticationSession = true
5039

@@ -72,7 +61,7 @@ public struct OAuth2AuthConfig {
7261
/// Context information for the authorization flow:
7362
/// - iOS: The parent view controller to present from
7463
/// - macOS: An NSWindow from which to present a modal sheet _or_ `nil` to present in a new window
75-
public weak var authorizeContext: AnyObject? = nil
64+
public nonisolated(unsafe) weak var authorizeContext: (AnyObject)? = nil
7665

7766
/// UI-specific configuration.
7867
public var ui = UI()

Sources/Base/OAuth2AuthRequest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public enum OAuth2EndpointAuthMethod: String {
6666
/**
6767
Class representing an OAuth2 authorization request that can be used to create NSURLRequest instances.
6868
*/
69+
@OAuth2Actor
6970
open class OAuth2AuthRequest {
7071

7172
/// The url of the receiver. Queries may by added by parameters specified on `params`.

Sources/Base/OAuth2AuthorizerUI.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Foundation
2424
/**
2525
Platform-dependent authorizers must adopt this protocol.
2626
*/
27+
@OAuth2Actor
2728
public protocol OAuth2AuthorizerUI {
2829

2930
/// The OAuth2 instance this authorizer belongs to.
@@ -44,5 +45,5 @@ public protocol OAuth2AuthorizerUI {
4445
- parameter at: The authorize URL to open
4546
- throws: Can throw OAuth2Error if the method is unable to show the authorize screen
4647
*/
47-
func authorizeEmbedded(with config: OAuth2AuthConfig, at url: URL) throws
48+
func authorizeEmbedded(with config: OAuth2AuthConfig, at url: URL) async throws
4849
}

Sources/Base/OAuth2Base.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import Foundation
2222
import CommonCrypto
2323

24-
2524
/**
2625
Class extending on OAuth2Requestable, exposing configuration and maintaining context, serving as base class for `OAuth2`.
2726
*/
@@ -98,7 +97,7 @@ open class OAuth2Base: OAuth2Securable {
9897

9998
/// The receiver's id token.
10099
open var idToken: String? {
101-
get { return clientConfig.idToken }
100+
get { return clientConfig.idToken }
102101
set { clientConfig.idToken = newValue }
103102
}
104103

@@ -132,14 +131,8 @@ open class OAuth2Base: OAuth2Securable {
132131
set { clientConfig.customUserAgent = newValue }
133132
}
134133

135-
136-
/// This closure is internally used with `authorize(params:callback:)` and only exposed for subclassing reason, do not mess with it!
137-
public final var didAuthorizeOrFail: ((_ parameters: OAuth2JSON?, _ error: OAuth2Error?) -> Void)?
138-
139134
/// Returns true if the receiver is currently authorizing.
140-
public final var isAuthorizing: Bool {
141-
return nil != didAuthorizeOrFail
142-
}
135+
public final var isAuthorizing: Bool = false
143136

144137
/// Returns true if the receiver is currently exchanging the refresh token.
145138
public final var isExchangingRefreshToken: Bool = false
@@ -159,6 +152,7 @@ open class OAuth2Base: OAuth2Securable {
159152
*/
160153
public final var internalAfterAuthorizeOrFail: ((_ wasFailure: Bool, _ error: OAuth2Error?) -> Void)?
161154

155+
public final var doAuthorizeContinuation: CheckedContinuation<OAuth2JSON, any Error>?
162156

163157
/**
164158
Designated initializer.
@@ -268,7 +262,8 @@ open class OAuth2Base: OAuth2Securable {
268262

269263
- parameter redirect: The redirect URL returned by the server that you want to handle
270264
*/
271-
open func handleRedirectURL(_ redirect: URL) throws {
265+
@discardableResult
266+
open func handleRedirectURL(_ redirect: URL) async throws -> OAuth2JSON {
272267
throw OAuth2Error.generic("Abstract class use")
273268
}
274269

@@ -285,11 +280,14 @@ open class OAuth2Base: OAuth2Securable {
285280
storeTokensToKeychain()
286281
}
287282
callOnMainThread() {
288-
self.didAuthorizeOrFail?(parameters, nil)
289-
self.didAuthorizeOrFail = nil
283+
self.isAuthorizing = false
290284
self.internalAfterAuthorizeOrFail?(false, nil)
291285
self.afterAuthorizeOrFail?(parameters, nil)
292286
}
287+
288+
// Finish `doAuthorize` call
289+
self.doAuthorizeContinuation?.resume(returning: parameters)
290+
self.doAuthorizeContinuation = nil
293291
}
294292

295293
/**
@@ -309,11 +307,14 @@ open class OAuth2Base: OAuth2Securable {
309307
finalError = OAuth2Error.requestCancelled
310308
}
311309
callOnMainThread() {
312-
self.didAuthorizeOrFail?(nil, finalError)
313-
self.didAuthorizeOrFail = nil
310+
self.isAuthorizing = false
314311
self.internalAfterAuthorizeOrFail?(true, finalError)
315312
self.afterAuthorizeOrFail?(nil, finalError)
316313
}
314+
315+
// Finish `doAuthorize` call
316+
self.doAuthorizeContinuation?.resume(throwing: error ?? OAuth2Error.requestCancelled)
317+
self.doAuthorizeContinuation = nil
317318
}
318319

319320
/**

Sources/Base/OAuth2ClientConfig.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,10 @@ open class OAuth2ClientConfig {
218218

219219
- returns: A storable dictionary with credentials
220220
*/
221-
func storableCredentialItems() -> [String: Any]? {
221+
func storableCredentialItems() -> [String: any Sendable]? {
222222
guard let clientId = clientId, !clientId.isEmpty else { return nil }
223223

224-
var items: [String: Any] = ["id": clientId]
224+
var items: [String: any Sendable] = ["id": clientId]
225225
if let secret = clientSecret {
226226
items["secret"] = secret
227227
}
@@ -243,8 +243,8 @@ open class OAuth2ClientConfig {
243243

244244
- returns: A storable dictionary with token data
245245
*/
246-
func storableTokenItems() -> [String: Any]? {
247-
var items = [String: Any]()
246+
func storableTokenItems() -> [String: any Sendable]? {
247+
var items = [String: any Sendable]()
248248

249249
if let access = accessToken {
250250
items["accessToken"] = access

Sources/Base/OAuth2CustomAuthorizerUI.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
/**
2323
Platform-dependent login presenters that present custom login views must adopt this protocol.
2424
*/
25+
@OAuth2Actor
2526
public protocol OAuth2CustomAuthorizerUI {
2627

2728
/**
@@ -31,7 +32,7 @@ public protocol OAuth2CustomAuthorizerUI {
3132
- parameter fromContext: The presenting context, typically another controller of platform-dependent type
3233
- parameter animated: Whether the presentation should be animated
3334
*/
34-
func present(loginController: AnyObject, fromContext context: AnyObject?, animated: Bool) throws
35+
func present(loginController: AnyObject, fromContext context: AnyObject?, animated: Bool) async throws
3536

3637
/**
3738
This function must dismiss the login controller.

0 commit comments

Comments
 (0)