Skip to content

Commit ed86261

Browse files
authored
Add a method taking a completion argument for asynchronous error updates (google#229)
1 parent a82d26c commit ed86261

File tree

4 files changed

+95
-31
lines changed

4 files changed

+95
-31
lines changed

GTMAppAuth/Sources/AuthSession.swift

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -285,26 +285,30 @@ public final class AuthSession: NSObject, GTMSessionFetcherAuthorizer, NSSecureC
285285
}
286286
let callbackQueue = fetcherService?.callbackQueue ?? DispatchQueue.main
287287

288-
if let error = args.error, let delegate = self.delegate {
289-
// If there is an updated error, use that; otherwise, use whatever is already in `args.error`
290-
let newError = delegate.updatedError?(forAuthSession: self, originalError: error)
291-
args.error = newError ?? error
288+
let callback: () -> Void = {
289+
callbackQueue.async {
290+
switch args.callbackStyle {
291+
case .completion(let callback):
292+
self.invokeCompletionCallback(with: callback, error: args.error)
293+
case .delegate(let delegate, let selector):
294+
self.invokeCallback(
295+
withDelegate: delegate,
296+
selector: selector,
297+
request: request,
298+
error: args.error
299+
)
300+
}
301+
}
292302
}
293303

294-
callbackQueue.async { [weak self] in
295-
guard let self = self else { return }
296-
297-
switch args.callbackStyle {
298-
case .completion(let callback):
299-
self.invokeCompletionCallback(with: callback, error: args.error)
300-
case .delegate(let delegate, let selector):
301-
self.invokeCallback(
302-
withDelegate: delegate,
303-
selector: selector,
304-
request: request,
305-
error: args.error
306-
)
304+
if let error = args.error, let delegate = self.delegate, delegate.updateError != nil {
305+
// Use updated error if exists; otherwise, use whatever is already in `args.error`
306+
delegate.updateError?(forAuthSession: self, originalError: error) { updatedError in
307+
args.error = updatedError ?? error
308+
callback()
307309
}
310+
} else {
311+
callback()
308312
}
309313
}
310314

GTMAppAuth/Sources/AuthSessionDelegate.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,16 @@ public protocol AuthSessionDelegate {
3030

3131
/// A method notifying the delegate that the authorization request failed.
3232
///
33-
/// Use this method to examine the error behind the failed authorization request and supply a more
34-
/// custom error specifying whatever context is needed.
33+
/// Use this method to examine the error behind the failed authorization request and supply a
34+
/// customized error created asynchronously that specifies whatever context is needed.
3535
///
3636
/// - Parameters:
3737
/// - authSession: The `AuthSession` whose authorization request failed.
3838
/// - originalError: The original `Error` associated with the failure.
39-
/// - Returns: The new error for `AuthSession` to send back or nil if no error should be sent.
40-
@objc optional func updatedError(
39+
/// - completion: An escaping closure to pass back the updated error.
40+
@objc optional func updateError(
4141
forAuthSession authSession: AuthSession,
42-
originalError: Error
43-
) -> Error?
42+
originalError: Error,
43+
completion: @escaping (Error?) -> Void
44+
)
4445
}

GTMAppAuth/Tests/Helpers/AuthorizationTestingHelp.swift

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,42 @@ public class AuthorizationTestDelegate: NSObject {
6363
}
6464
}
6565

66+
/// A testing helper that does not implement the update error method in `AuthSessionDelegate`.
67+
@objc(GTMAuthSessionDelegateProvideMissingEMMErrorHandling)
68+
public class AuthSessionDelegateProvideMissingEMMErrorHandling: NSObject, AuthSessionDelegate {
69+
/// The `AuthSession` to which this delegate was given.
70+
@objc public var originalAuthSession: AuthSession?
71+
72+
/// Whether or not the delegate callback for additional refresh parameters was called.
73+
///
74+
/// - Note: Defaults to `false`.
75+
@objc public var additionalRefreshParametersCalled = false
76+
77+
/// Whether or not the delegate callback for authorization request failure was called.
78+
///
79+
/// - Note: Defaults to `false`.
80+
@objc public var updatedErrorCalled = false
81+
82+
/// The expected error from the delegate callback.
83+
@objc public let expectedError: NSError?
84+
85+
@objc public init(originalAuthSession: AuthSession, expectedError: NSError? = nil) {
86+
self.originalAuthSession = originalAuthSession
87+
self.expectedError = expectedError
88+
}
89+
90+
public func additionalTokenRefreshParameters(
91+
forAuthSession authSession: AuthSession
92+
) -> [String : String]? {
93+
XCTAssertEqual(authSession, originalAuthSession)
94+
additionalRefreshParametersCalled = true
95+
return [:]
96+
}
97+
}
98+
6699
/// A testing helper given to `AuthSession`'s `delegate` to verify delegate callbacks.
67100
@objc(GTMAuthSessionDelegateProvider)
68101
public class AuthSessionDelegateProvider: NSObject, AuthSessionDelegate {
69-
70102
/// The `AuthSession` to which this delegate was given.
71103
@objc public var originalAuthSession: AuthSession?
72104

@@ -96,15 +128,13 @@ public class AuthSessionDelegateProvider: NSObject, AuthSessionDelegate {
96128
return [:]
97129
}
98130

99-
public func updatedError(
131+
public func updateError(
100132
forAuthSession authSession: AuthSession,
101-
originalError: Error
102-
) -> Error? {
133+
originalError: Error,
134+
completion: @escaping (Error?) -> Void
135+
) {
103136
XCTAssertEqual(authSession, originalAuthSession)
104137
updatedErrorCalled = true
105-
guard let expectedError = expectedError else {
106-
return nil
107-
}
108-
return expectedError
138+
completion(expectedError)
109139
}
110140
}

GTMAppAuth/Tests/Unit/AuthSessionTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,35 @@ class AuthSessionTests: XCTestCase {
268268
XCTAssertTrue(authSessionDelegate.updatedErrorCalled)
269269
}
270270

271+
func testAuthSessionAuthorizeRequestWithCompletionFailDelegateNoImplementUpdateErrorCallback() {
272+
let authorizeSecureRequestExpectation = expectation(
273+
description: "Authorize with completion expectation"
274+
)
275+
let authSession = AuthSession(
276+
authState: OIDAuthState.testInstance()
277+
)
278+
279+
// There must be an error here for the delegate to get the `updatedError` callback
280+
let insecureRequest = NSMutableURLRequest(url: insecureFakeURL)
281+
let expectedError = AuthSession.Error.cannotAuthorizeRequest(insecureRequest as URLRequest)
282+
let authSessionDelegate = AuthSessionDelegateProvideMissingEMMErrorHandling(
283+
originalAuthSession: authSession
284+
)
285+
authSession.delegate = authSessionDelegate
286+
287+
authSession.authorizeRequest(insecureRequest) { error in
288+
guard let error = error else {
289+
return XCTFail("There should be an `NSError` authorizing \(insecureRequest)")
290+
}
291+
XCTAssertEqual(error as? AuthSession.Error, expectedError)
292+
authorizeSecureRequestExpectation.fulfill()
293+
}
294+
XCTAssertTrue(authSession.isAuthorizingRequest(insecureRequest as URLRequest))
295+
waitForExpectations(timeout: expectationTimeout)
296+
XCTAssertFalse(authSession.isAuthorizedRequest(insecureRequest as URLRequest))
297+
XCTAssertFalse(authSessionDelegate.updatedErrorCalled)
298+
}
299+
271300
func testAuthSessionAuthorizeRequestWithCompletionDidFailButDelegateDoesntUpdateError() {
272301
let authorizeSecureRequestExpectation = expectation(
273302
description: "Authorize with completion expectation"

0 commit comments

Comments
 (0)