Skip to content

Commit

Permalink
Merge pull request #277 from mohssenfathi/bump-deployment-target-11
Browse files Browse the repository at this point in the history
Bump deployment target to iOS 11
  • Loading branch information
mohssenfathi authored Apr 18, 2023
2 parents 8e4315d + 5f43693 commit 6660233
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 342 deletions.
28 changes: 1 addition & 27 deletions source/UberCore/Authentication/LoginManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -290,17 +290,10 @@ import SafariServices
// Delegates a web login to SFAuthenticationSession, SFSafariViewController, or just Safari
private func executeWebLogin(presentingViewController: UIViewController?, authenticator: UberAuthenticating) {
self.authenticator = authenticator
if #available(iOS 11.0, *) {
executeSafariAuthLogin(authenticator: authenticator)
} else if #available(iOS 9.0, *) {
executeSafariVCLogin(presentingViewController: presentingViewController, authenticator: authenticator)
} else {
UIApplication.shared.openURL(authenticator.authorizationURL)
}
executeSafariAuthLogin(authenticator: authenticator)
}

/// Login using SFAuthenticationSession
@available(iOS 11.0, *)
private func executeSafariAuthLogin(authenticator: UberAuthenticating) {
guard let bundleID = Bundle.main.bundleIdentifier else {
preconditionFailure("You do not have a Bundle ID set for your app. You need a Bundle ID to use Uber Authentication")
Expand All @@ -318,25 +311,6 @@ import SafariServices
self.safariAuthenticationSession = safariAuthenticationSession
}

/// Login using SFSafariViewController
@available(iOS 9.0, *)
private func executeSafariVCLogin(presentingViewController: UIViewController?, authenticator: UberAuthenticating) {
// Find the topmost view controller, and present from it
var presentingViewController = presentingViewController
if presentingViewController == nil {
var topController = UIApplication.shared.keyWindow?.rootViewController
while let vc = topController?.presentedViewController {
topController = vc
}
presentingViewController = topController
}

let safariVC = SFSafariViewController(url: authenticator.authorizationURL)

presentingViewController?.present(safariVC, animated: true, completion: nil)
oauthViewController = safariVC
}

/// Login using native deeplink
private func executeDeeplinkLogin(presentingViewController: UIViewController?, authenticationProvider: AuthenticationProvider) {

Expand Down
114 changes: 18 additions & 96 deletions source/UberCore/Deeplinks/DeeplinkManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ class DeeplinkManager {
private var urlQueue: [URL] = []
private var callback: DeeplinkCompletionHandler?

private var waitingOnSystemPromptResponse = false
private var checkingSystemPromptResponse = false
private var promptTimer: Timer?

/// Open a deeplink, utilizing its fallback URLs.
func open(_ deeplink: Deeplinking, completion: DeeplinkCompletionHandler? = nil) {
urlQueue = deeplink.fallbackURLs
Expand All @@ -53,13 +49,17 @@ class DeeplinkManager {
//Mark: Internal Interface

private func open(_ url: URL) {
if #available(iOS 10.0, *) {
executeOnIOS10(deeplink: url)
}
else if #available(iOS 9.0, *) {
executeOnIOS9(deeplink: url)
if urlOpener.canOpenURL(url) {
urlOpener.open(url, completionHandler: { (succeeded) in
if !succeeded {
self.deeplinkDidFinish(error: DeeplinkErrorFactory.errorForType(.unableToFollow))
}
else {
self.deeplinkDidFinish(error: nil)
}
})
} else {
executeOnBelowIOS9(deeplink: url)
deeplinkDidFinish(error: DeeplinkErrorFactory.errorForType(.unableToOpen))
}
}

Expand All @@ -72,105 +72,27 @@ class DeeplinkManager {
open(urlQueue.removeFirst())
return
}
if #available(iOS 9.0, *) {
NotificationCenter.default.removeObserver(self)
self.promptTimer?.invalidate()
self.promptTimer = nil
self.checkingSystemPromptResponse = false
self.waitingOnSystemPromptResponse = false
}


callback?(error)

self.urlQueue = []
self.callback = nil
}

@available(iOS 10.0, *)
private func executeOnIOS10(deeplink url: URL) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: { (succeeded) in
if !succeeded {
self.deeplinkDidFinish(error: DeeplinkErrorFactory.errorForType(.deeplinkNotFollowed))
}
else {
self.deeplinkDidFinish(error: nil)
}
})
} else {
deeplinkDidFinish(error: DeeplinkErrorFactory.errorForType(.unableToOpen))
}
}

private func executeOnIOS9(deeplink url: URL) {
subscribeToNotifications()

var error: NSError?
let openedURL = urlOpener.openURL(url)
if !openedURL {
error = DeeplinkErrorFactory.errorForType(.unableToOpen)
}

if error != nil {
deeplinkDidFinish(error: error)
}
}

private func subscribeToNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActiveHandler), name: UIApplication.willResignActiveNotification, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActiveHandler), name: UIApplication.didBecomeActiveNotification, object: nil);
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackgroundHandler), name: UIApplication.didEnterBackgroundNotification, object: nil)
}

private func executeOnBelowIOS9(deeplink url: URL) {
var error: NSError?
if urlOpener.canOpenURL(url) {
let openedURL = urlOpener.openURL(url)
if !openedURL {
error = DeeplinkErrorFactory.errorForType(.unableToFollow)
}
} else {
error = DeeplinkErrorFactory.errorForType(.unableToOpen)
}

deeplinkDidFinish(error: error)
}

// Mark: App Lifecycle Notifications

@objc private func appWillResignActiveHandler(_ notification: Notification) {
if !waitingOnSystemPromptResponse {
waitingOnSystemPromptResponse = true
} else if checkingSystemPromptResponse {
deeplinkDidFinish(error: nil)
}
}

@objc private func appDidBecomeActiveHandler(_ notification: Notification) {
if waitingOnSystemPromptResponse {
checkingSystemPromptResponse = true
promptTimer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(deeplinkHelper), userInfo: nil, repeats: false)
}
}

@objc private func appDidEnterBackgroundHandler(_ notification: Notification) {
deeplinkDidFinish(error: nil)
}

@objc private func deeplinkHelper() {
let error = DeeplinkErrorFactory.errorForType(.deeplinkNotFollowed)
deeplinkDidFinish(error: error)
}
}

protocol URLOpening {
public protocol URLOpening {
func canOpenURL(_ url: URL) -> Bool
func openURL(_ url: URL) -> Bool
func open(_ url: URL, completionHandler: ((Bool) -> Void)?)
}

extension UIApplication: URLOpening {}

// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
extension UIApplication: URLOpening {
public func open(_ url: URL, completionHandler: ((Bool) -> Void)?) {
open(url, options: [:], completionHandler: completionHandler)
}
}
48 changes: 15 additions & 33 deletions source/UberCoreTests/DeeplinkManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ class DeeplinkManagerTests: XCTestCase {

deeplinkManager = DeeplinkManager()
urlOpener = URLOpeningMock()
urlOpener.canOpenURLHandler = { _ in true }
deeplinkManager.urlOpener = urlOpener
testDeeplink = TestDeeplink()
}

func testDeeplinkOpensFirstURL() {
let expectCallback = self.expectation(description: "Callback is run")
urlOpener.openURLHandler = { url in
return true // All URLS can open
urlOpener.openURLHandler = { url, completionHandler in
completionHandler?(true) // All URLS can open
}
openDeeplink(testDeeplink) { error in
XCTAssertNil(error)
Expand All @@ -40,11 +41,11 @@ class DeeplinkManagerTests: XCTestCase {
func testDeeplinkOpensSecondURL() {
let expectCorrectURLScheme = self.expectation(description: "We need to open up the app2 URL scheme")
let expectCallback = self.expectation(description: "Callback is run")
urlOpener.openURLHandler = { url in
urlOpener.openURLHandler = { url, completionHandler in
if url.scheme == "app2" {
expectCorrectURLScheme.fulfill()
}
return url.scheme == "app2"
completionHandler?(url.scheme == "app2")
}
openDeeplink(testDeeplink) { error in
XCTAssertNil(error)
Expand All @@ -57,11 +58,11 @@ class DeeplinkManagerTests: XCTestCase {

func testDeeplinkErrorsWhenNoFallbacks() {
let expectCallback = self.expectation(description: "Callback is run")
urlOpener.openURLHandler = { url in
return false
urlOpener.openURLHandler = { url, completionHandler in
completionHandler?(false)
}
openDeeplink(testDeeplink) { error in
let expectedError = DeeplinkErrorFactory.errorForType(DeeplinkErrorType.unableToOpen)
let expectedError = DeeplinkErrorFactory.errorForType(DeeplinkErrorType.unableToFollow)
XCTAssertNotNil(error)
XCTAssertEqual(error, expectedError)
expectCallback.fulfill()
Expand All @@ -73,8 +74,8 @@ class DeeplinkManagerTests: XCTestCase {

func testDeeplinkOpensURLWhenIOSPromptsPermission() {
let expectCallback = self.expectation(description: "Callback is run")
urlOpener.openURLHandler = { url in
return true // All URLS can open
urlOpener.openURLHandler = { url, completionHandler in
completionHandler?(true) // All URLS can open
}
openDeeplinkWithSuccessfulPrompt(testDeeplink) { error in
XCTAssertNil(error)
Expand All @@ -85,21 +86,6 @@ class DeeplinkManagerTests: XCTestCase {
self.waitForExpectations(timeout: 0.5, handler: nil)
}

func testDeeplinkErrorsWhenUserDeniesPermission() {
let expectCallback = self.expectation(description: "Callback is run")
urlOpener.openURLHandler = { url in
return true // All URLS can open
}
openDeeplinkWithCancelledPrompt(testDeeplink) { error in
let expectedError = DeeplinkErrorFactory.errorForType(.deeplinkNotFollowed)
XCTAssertEqual(error, expectedError)
expectCallback.fulfill()
}
XCTAssertEqual(urlOpener.openURLCallCount, 1)

self.waitForExpectations(timeout: 0.5, handler: nil)
}

private func openDeeplink(_ deeplink: Deeplinking, completion: @escaping DeeplinkCompletionHandler) {
deeplinkManager.open(deeplink, completion: completion)
NotificationCenter.default.post(Notification(name: Notification.Name.UIApplicationDidEnterBackground))
Expand All @@ -111,11 +97,6 @@ class DeeplinkManagerTests: XCTestCase {
NotificationCenter.default.post(Notification(name: Notification.Name.UIApplicationDidBecomeActive))
NotificationCenter.default.post(Notification(name: Notification.Name.UIApplicationDidEnterBackground))
}

private func openDeeplinkWithCancelledPrompt(_ deeplink: Deeplinking, completion: @escaping DeeplinkCompletionHandler) {
deeplinkManager.open(deeplink, completion: completion)
NotificationCenter.default.post(Notification(name: Notification.Name.UIApplicationWillResignActive))
NotificationCenter.default.post(Notification(name: Notification.Name.UIApplicationDidBecomeActive)) }
}

private class TestDeeplink: Deeplinking {
Expand All @@ -137,10 +118,11 @@ private class URLOpeningMock: URLOpening {
}

var openURLCallCount = 0
var openURLHandler: ((URL) -> Bool)?
func openURL(_ url: URL) -> Bool {
var openURLHandler: ((URL, ((Bool) -> Void)?) -> Void)?
func open(_ url: URL,
completionHandler: ((Bool) -> Void)?) {
openURLCallCount += 1

return openURLHandler?(url) ?? false
openURLHandler?(url, completionHandler)
}
}
7 changes: 4 additions & 3 deletions source/UberCoreTests/RefreshEndpointTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import XCTest
import OHHTTPStubs
import OHHTTPStubsSwift
@testable import UberCore

class RefreshEndpointTests: XCTestCase {
Expand All @@ -42,7 +43,7 @@ class RefreshEndpointTests: XCTestCase {
}

override func tearDown() {
OHHTTPStubs.removeAllStubs()
HTTPStubs.removeAllStubs()
Configuration.restoreDefaults()
super.tearDown()
}
Expand All @@ -52,7 +53,7 @@ class RefreshEndpointTests: XCTestCase {
*/
func test200Response() {
stub(condition: isHost("login.uber.com")) { _ in
return OHHTTPStubsResponse(fileAtPath:OHPathForFile("refresh.json", type(of: self))!, statusCode:200, headers:self.headers)
return HTTPStubsResponse(fileAtPath:OHPathForFile("refresh.json", type(of: self))!, statusCode:200, headers:self.headers)
}
let refreshToken = "ThisIsRefresh"
let expectation = self.expectation(description: "200 success response")
Expand Down Expand Up @@ -105,7 +106,7 @@ class RefreshEndpointTests: XCTestCase {

stub(condition: isHost("login.uber.com")) { _ in
let json = ["error": error]
return OHHTTPStubsResponse(jsonObject: json, statusCode: 400, headers: self.headers)
return HTTPStubsResponse(jsonObject: json, statusCode: 400, headers: self.headers)
}

let refreshToken = "ThisIsRefresh"
Expand Down
Loading

0 comments on commit 6660233

Please sign in to comment.