From b39135a38840cfb3955b0799c14aa5800c14c434 Mon Sep 17 00:00:00 2001 From: mohssenfathi Date: Thu, 8 Aug 2024 14:25:29 -0700 Subject: [PATCH] Consolidate UberRides and UberAuth configurations --- Sources/UberAuth/AuthDestination.swift | 36 +- .../AuthorizationCodeAuthProvider.swift | 29 +- .../UberAuth/Authorize/AuthorizeRequest.swift | 1 + Sources/UberAuth/ConfigurationProvider.swift | 54 --- Sources/UberAuth/Errors/UberAuthError.swift | 1 + Sources/UberAuth/Token/TokenManager.swift | 4 +- Sources/UberCore/ConfigurationProvider.swift | 96 +++++ Sources/UberCore/PlistParser.swift | 4 +- Sources/UberCore/UberApp.swift | 41 +++ Sources/UberRides/RequestDeeplink.swift | 13 +- Sources/UberRides/RideRequestView.swift | 2 - .../UberRides/RideRequestViewController.swift | 8 +- .../RideRequestViewRequestingBehavior.swift | 5 +- Sources/UberRides/RidesClient.swift | 19 +- .../UberRides/RidesCore/Configuration.swift | 338 ------------------ .../Deeplinks/EatsAppStoreDeeplink.swift | 6 +- .../Deeplinks/RidesAppStoreDeeplink.swift | 6 +- .../RidesCore/Networking/APIEndpoint.swift | 3 +- .../Networking/EndpointsManager.swift | 3 +- .../RidesCore/Utilities/RidesUtil.swift | 4 +- .../UberSDK/UberSDK.xcodeproj/project.pbxproj | 2 + .../xcshareddata/xcschemes/UberSDK.xcscheme | 9 +- examples/UberSDK/UberSDK.xctestplan | 29 ++ examples/UberSDK/UberSDK/Info.plist | 8 +- .../UberSDK/UberSDKTests/Mocks/Mocks.swift | 45 ++- .../UberAuth/AuthorizeRequestTests.swift | 1 + 26 files changed, 267 insertions(+), 500 deletions(-) delete mode 100644 Sources/UberAuth/ConfigurationProvider.swift create mode 100644 Sources/UberCore/ConfigurationProvider.swift create mode 100644 Sources/UberCore/UberApp.swift delete mode 100644 Sources/UberRides/RidesCore/Configuration.swift create mode 100644 examples/UberSDK/UberSDK.xctestplan diff --git a/Sources/UberAuth/AuthDestination.swift b/Sources/UberAuth/AuthDestination.swift index 8a7a0a34..8fda2bb7 100644 --- a/Sources/UberAuth/AuthDestination.swift +++ b/Sources/UberAuth/AuthDestination.swift @@ -4,6 +4,7 @@ import Foundation +import UberCore /// An enum that describes where login should occur public enum AuthDestination { @@ -19,38 +20,3 @@ public enum AuthDestination { appPriority: [UberApp] = [.rides, .eats, .driver] ) } - -/// An enum corresponding to each Uber client application -public enum UberApp: CaseIterable { - - // Uber Eats - case eats - - // Uber Driver - case driver - - // Uber - case rides - - var deeplinkScheme: String { - switch self { - case .eats: - return "ubereats" - case .driver: - return "uberdriver" - case .rides: - return "uber" - } - } - - var urlIdentifier: String { - switch self { - case .eats: - return "eats" - case .driver: - return "drivers" - case .rides: - return "riders" - } - } -} diff --git a/Sources/UberAuth/Authorize/AuthorizationCodeAuthProvider.swift b/Sources/UberAuth/Authorize/AuthorizationCodeAuthProvider.swift index d4daf1d0..08be719f 100644 --- a/Sources/UberAuth/Authorize/AuthorizationCodeAuthProvider.swift +++ b/Sources/UberAuth/Authorize/AuthorizationCodeAuthProvider.swift @@ -56,21 +56,12 @@ public final class AuthorizationCodeAuthProvider: AuthProviding { scopes: [String] = AuthorizationCodeAuthProvider.defaultScopes, shouldExchangeAuthCode: Bool = false, prompt: Prompt? = nil) { - self.configurationProvider = DefaultConfigurationProvider() - - guard let clientID: String = configurationProvider.clientID else { - preconditionFailure("No clientID specified in Info.plist") - } - - guard let redirectURI: String = configurationProvider.redirectURI else { - preconditionFailure("No redirectURI specified in Info.plist") - } - + self.configurationProvider = ConfigurationProvider() self.applicationLauncher = UIApplication.shared self.authenticationSessionBuilder = nil - self.clientID = clientID + self.clientID = configurationProvider.clientID self.presentationAnchor = presentationAnchor - self.redirectURI = redirectURI + self.redirectURI = configurationProvider.redirectURI self.responseParser = AuthorizationCodeResponseParser() self.shouldExchangeAuthCode = shouldExchangeAuthCode self.networkProvider = NetworkProvider(baseUrl: Constants.baseUrl) @@ -84,26 +75,18 @@ public final class AuthorizationCodeAuthProvider: AuthProviding { scopes: [String] = AuthorizationCodeAuthProvider.defaultScopes, prompt: Prompt? = nil, shouldExchangeAuthCode: Bool = false, - configurationProvider: ConfigurationProviding = DefaultConfigurationProvider(), + configurationProvider: ConfigurationProviding = ConfigurationProvider(), applicationLauncher: ApplicationLaunching = UIApplication.shared, responseParser: AuthorizationCodeResponseParsing = AuthorizationCodeResponseParser(), networkProvider: NetworkProviding = NetworkProvider(baseUrl: Constants.baseUrl), tokenManager: TokenManaging = TokenManager()) { - guard let clientID: String = configurationProvider.clientID else { - preconditionFailure("No clientID specified in Info.plist") - } - - guard let redirectURI: String = configurationProvider.redirectURI else { - preconditionFailure("No redirectURI specified in Info.plist") - } - self.applicationLauncher = applicationLauncher self.authenticationSessionBuilder = authenticationSessionBuilder - self.clientID = clientID + self.clientID = configurationProvider.clientID self.configurationProvider = configurationProvider self.presentationAnchor = presentationAnchor - self.redirectURI = redirectURI + self.redirectURI = configurationProvider.redirectURI self.responseParser = responseParser self.shouldExchangeAuthCode = shouldExchangeAuthCode self.networkProvider = networkProvider diff --git a/Sources/UberAuth/Authorize/AuthorizeRequest.swift b/Sources/UberAuth/Authorize/AuthorizeRequest.swift index 66238329..9dfc53e2 100644 --- a/Sources/UberAuth/Authorize/AuthorizeRequest.swift +++ b/Sources/UberAuth/Authorize/AuthorizeRequest.swift @@ -4,6 +4,7 @@ import Foundation +import UberCore /// /// Defines a network request conforming to the OAuth 2.0 standard authorization request diff --git a/Sources/UberAuth/ConfigurationProvider.swift b/Sources/UberAuth/ConfigurationProvider.swift deleted file mode 100644 index adc57b72..00000000 --- a/Sources/UberAuth/ConfigurationProvider.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright © Uber Technologies, Inc. All rights reserved. -// - - -import Foundation -import UIKit -import UberCore - -/// @mockable -protocol ConfigurationProviding { - var clientID: String? { get } - var redirectURI: String? { get } - - func isInstalled(app: UberApp, defaultIfUnregistered: Bool) -> Bool -} - -struct DefaultConfigurationProvider: ConfigurationProviding { - - private let parser: PlistParser - private let contents: [String: Any] - - init() { - let parser = PlistParser(plistName: "Info") - self.parser = parser - self.contents = parser["UberAuth"] ?? [:] - } - - var clientID: String? { - contents["ClientID"] as? String - } - - var redirectURI: String? { - contents["RedirectURI"] as? String - } - - /// Attempts to determine if the provided `UberApp` is installed on the current device. - /// First checks the Info.plist to see if the required url schemes are registered. If not registered, - /// returns `defaultIfUnregistered`. - /// If registered, returns whether the scheme can be opened, indicating if the app is installed. - /// - /// - Parameters: - /// - app: The Uber application to check - /// - defaultIfUnregistered: The boolean value to return if the app's url scheme is not registered in the Info.plist - /// - Returns: A boolean indicating if the app is installed - func isInstalled(app: UberApp, defaultIfUnregistered: Bool) -> Bool { - guard let registeredSchemes: [String] = parser["LSApplicationQueriesSchemes"], - registeredSchemes.contains(where: { $0 == app.deeplinkScheme }), - let url = URL(string: "\(app.deeplinkScheme)://") else { - return defaultIfUnregistered - } - return UIApplication.shared.canOpenURL(url) - } -} diff --git a/Sources/UberAuth/Errors/UberAuthError.swift b/Sources/UberAuth/Errors/UberAuthError.swift index 2b156594..58de465e 100644 --- a/Sources/UberAuth/Errors/UberAuthError.swift +++ b/Sources/UberAuth/Errors/UberAuthError.swift @@ -4,6 +4,7 @@ import AuthenticationServices +import UberCore import Foundation public enum UberAuthError: Error { diff --git a/Sources/UberAuth/Token/TokenManager.swift b/Sources/UberAuth/Token/TokenManager.swift index 404404cf..81772e7a 100644 --- a/Sources/UberAuth/Token/TokenManager.swift +++ b/Sources/UberAuth/Token/TokenManager.swift @@ -67,7 +67,9 @@ public extension TokenManaging { public final class TokenManager: TokenManaging { - public static let defaultAccessTokenIdentifier = "UberAccessTokenKey" + public static let defaultAccessTokenIdentifier: String = "UberAccessTokenKey" + + public static let defaultKeychainAccessGroup: String = "" private let keychainUtility: KeychainUtilityProtocol diff --git a/Sources/UberCore/ConfigurationProvider.swift b/Sources/UberCore/ConfigurationProvider.swift new file mode 100644 index 00000000..902a6447 --- /dev/null +++ b/Sources/UberCore/ConfigurationProvider.swift @@ -0,0 +1,96 @@ +// +// Copyright © Uber Technologies, Inc. All rights reserved. +// + + +import Foundation +import UIKit +import UberCore + +/// @mockable +public protocol ConfigurationProviding { + var clientID: String { get } + var redirectURI: String { get } + var sdkVersion: String { get } + var serverToken: String? { get } + static var isSandbox: Bool { get } + + func isInstalled(app: UberApp, defaultIfUnregistered: Bool) -> Bool +} + +public struct ConfigurationProvider: ConfigurationProviding { + + // MARK: Private Properties + + private let parser: PlistParser + + // MARK: Initializers + + public init() { + let parser = PlistParser(plistName: "Info") + self.parser = parser + + guard let contents: [String: String] = parser[ConfigurationKey.base] else { + preconditionFailure("Configuration item not found: \(ConfigurationKey.base)") + } + + guard let clientID = contents[ConfigurationKey.clientID] as? String else { + preconditionFailure("Configuration item not found: \(ConfigurationKey.base)/\(ConfigurationKey.clientID)") + } + + guard let redirectURI = contents[ConfigurationKey.redirectURI] as? String else { + preconditionFailure("Configuration item not found: \(ConfigurationKey.base)/\(ConfigurationKey.clientID)") + } + + self.clientID = clientID + self.redirectURI = redirectURI + Self.isSandbox = Bool(contents[ConfigurationKey.sandbox] ?? "") ?? false + self.serverToken = contents[ConfigurationKey.serverToken] + } + + // MARK: ConfigurationProviding + + public let clientID: String + + public let redirectURI: String + + public let serverToken: String? + + public static var isSandbox: Bool = false + + /// Attempts to determine if the provided `UberApp` is installed on the current device. + /// First checks the Info.plist to see if the required url schemes are registered. If not registered, + /// returns `defaultIfUnregistered`. + /// If registered, returns whether the scheme can be opened, indicating if the app is installed. + /// + /// - Parameters: + /// - app: The Uber application to check + /// - defaultIfUnregistered: The boolean value to return if the app's url scheme is not registered in the Info.plist + /// - Returns: A boolean indicating if the app is installed + public func isInstalled(app: UberApp, defaultIfUnregistered: Bool) -> Bool { + guard let registeredSchemes: [String] = parser["LSApplicationQueriesSchemes"], + registeredSchemes.contains(where: { $0 == app.deeplinkScheme }), + let url = URL(string: "\(app.deeplinkScheme)://") else { + return defaultIfUnregistered + } + return UIApplication.shared.canOpenURL(url) + } + + /// The current version of the SDK as a string + public var sdkVersion: String { + guard let version = Bundle(for: UberButton.self).object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String else { + return "Unknown" + } + return version + } + + // MARK: Constants + + private enum ConfigurationKey { + static let base = "Uber" + static let clientID = "ClientID" + static let redirectURI = "RedirectURI" + static let serverToken = "RedirectURI" + static let sandbox = "Sandbox" + } +} diff --git a/Sources/UberCore/PlistParser.swift b/Sources/UberCore/PlistParser.swift index e09343d9..7adf30c2 100644 --- a/Sources/UberCore/PlistParser.swift +++ b/Sources/UberCore/PlistParser.swift @@ -17,7 +17,7 @@ public struct PlistParser { /// - name: The name of the plist to access /// - bundle: The bundle the plist is contained in public init(plistName: String, - bundle: Bundle = .main) { + bundle: Bundle = .main) { guard let plistUrl = bundle.url(forResource: plistName, withExtension: "plist") else { self.contents = [:] return @@ -39,7 +39,7 @@ public struct PlistParser { /// - name: The name of the plist to access /// - bundle: The bundle the plist is contained in public init(name: String, - bundle: Bundle = .main) throws { + bundle: Bundle = .main) throws { guard let plistUrl = bundle.url(forResource: name, withExtension: "plist") else { throw ParserError.noResourceFound } diff --git a/Sources/UberCore/UberApp.swift b/Sources/UberCore/UberApp.swift new file mode 100644 index 00000000..e70e2b8a --- /dev/null +++ b/Sources/UberCore/UberApp.swift @@ -0,0 +1,41 @@ +// +// Copyright © Uber Technologies, Inc. All rights reserved. +// + + +import Foundation + +/// An enum corresponding to each Uber client application +public enum UberApp: CaseIterable { + + // Uber Eats + case eats + + // Uber Driver + case driver + + // Uber + case rides + + public var deeplinkScheme: String { + switch self { + case .eats: + return "ubereats" + case .driver: + return "uberdriver" + case .rides: + return "uber" + } + } + + public var urlIdentifier: String { + switch self { + case .eats: + return "eats" + case .driver: + return "drivers" + case .rides: + return "riders" + } + } +} diff --git a/Sources/UberRides/RequestDeeplink.swift b/Sources/UberRides/RequestDeeplink.swift index e9c48ec5..1425d215 100644 --- a/Sources/UberRides/RequestDeeplink.swift +++ b/Sources/UberRides/RequestDeeplink.swift @@ -25,6 +25,7 @@ import CoreLocation import UIKit +import UberAuth import UberCore /** @@ -35,6 +36,7 @@ public class RequestDeeplink: BaseDeeplink { private let rideParameters: RideParameters private let fallbackType: DeeplinkFallbackType + private let configurationProvider: ConfigurationProviding /** Initialize a ride request deeplink. If the Uber app is not installed, fallback to the App Store. @@ -46,10 +48,13 @@ public class RequestDeeplink: BaseDeeplink { /** Initialize a ride request deeplink. */ - public init(rideParameters: RideParameters, fallbackType: DeeplinkFallbackType) { + public init(rideParameters: RideParameters, + fallbackType: DeeplinkFallbackType, + configurationProvider: ConfigurationProviding = ConfigurationProvider()) { self.rideParameters = rideParameters self.fallbackType = fallbackType self.rideParameters.source = rideParameters.source ?? RequestDeeplink.sourceString + self.configurationProvider = configurationProvider let queryItems = RequestURLUtil.buildRequestQueryParameters(rideParameters) let scheme = "uber" @@ -75,7 +80,11 @@ public class RequestDeeplink: BaseDeeplink { return urlComponents.url } case .appStore: - let deeplink = RidesAppStoreDeeplink(userAgent: rideParameters.userAgent) + let deeplink = RidesAppStoreDeeplink( + userAgent: rideParameters.userAgent, + clientID: configurationProvider.clientID ?? "", + sdkVersion: configurationProvider.sdkVersion + ) return deeplink.url default: break diff --git a/Sources/UberRides/RideRequestView.swift b/Sources/UberRides/RideRequestView.swift index 5f773f37..f10de5ff 100644 --- a/Sources/UberRides/RideRequestView.swift +++ b/Sources/UberRides/RideRequestView.swift @@ -79,7 +79,6 @@ public class RideRequestView: UIView { self.rideParameters = rideParameters self.accessToken = accessToken let configuration = WKWebViewConfiguration() - configuration.processPool = Configuration.shared.processPool webView = WKWebView(frame: CGRect.zero, configuration: configuration) super.init(frame: frame) initialSetup() @@ -145,7 +144,6 @@ public class RideRequestView: UIView { required public init?(coder aDecoder: NSCoder) { rideParameters = RideParametersBuilder().build() let configuration = WKWebViewConfiguration() - configuration.processPool = Configuration.shared.processPool webView = WKWebView(frame: CGRect.zero, configuration: configuration) super.init(coder: aDecoder) initialSetup() diff --git a/Sources/UberRides/RideRequestViewController.swift b/Sources/UberRides/RideRequestViewController.swift index 28e7ceca..0157dce3 100644 --- a/Sources/UberRides/RideRequestViewController.swift +++ b/Sources/UberRides/RideRequestViewController.swift @@ -74,8 +74,8 @@ public class RideRequestViewController: UIViewController { - returns: An initialized RideRequestViewController, or nil if something went wrong */ public required init?(coder aDecoder: NSCoder) { - self.accessTokenIdentifier = Configuration.shared.defaultAccessTokenIdentifier - self.keychainAccessGroup = Configuration.shared.defaultKeychainAccessGroup + self.accessTokenIdentifier = "" + self.keychainAccessGroup = "" super.init(coder: aDecoder) @@ -94,8 +94,8 @@ public class RideRequestViewController: UIViewController { - returns: An initialized RideRequestViewController */ public init(rideParameters: RideParameters, - accessTokenIdentifier: String = Configuration.shared.defaultAccessTokenIdentifier, - keychainAccessGroup: String = Configuration.shared.defaultKeychainAccessGroup) { + accessTokenIdentifier: String = TokenManager.defaultAccessTokenIdentifier, + keychainAccessGroup: String = TokenManager.defaultKeychainAccessGroup) { self.accessTokenIdentifier = accessTokenIdentifier self.keychainAccessGroup = keychainAccessGroup diff --git a/Sources/UberRides/RideRequestViewRequestingBehavior.swift b/Sources/UberRides/RideRequestViewRequestingBehavior.swift index bf398a54..454e08ba 100644 --- a/Sources/UberRides/RideRequestViewRequestingBehavior.swift +++ b/Sources/UberRides/RideRequestViewRequestingBehavior.swift @@ -23,6 +23,7 @@ // THE SOFTWARE. import Foundation +import UberAuth import UberCore import UIKit @@ -51,8 +52,8 @@ public class RideRequestViewRequestingBehavior { - returns: An initialized RideRequestViewRequestingBehavior object */ public init(presentingViewController: UIViewController, - accessTokenIdentifier: String = Configuration.shared.defaultAccessTokenIdentifier, - keychainAccessGroup: String = Configuration.shared.defaultKeychainAccessGroup) { + accessTokenIdentifier: String = TokenManager.defaultAccessTokenIdentifier, + keychainAccessGroup: String = TokenManager.defaultKeychainAccessGroup) { self.presentingViewController = presentingViewController let rideRequestViewController = RideRequestViewController( rideParameters: RideParametersBuilder().build(), diff --git a/Sources/UberRides/RidesClient.swift b/Sources/UberRides/RidesClient.swift index db1dff71..56f6ec81 100644 --- a/Sources/UberRides/RidesClient.swift +++ b/Sources/UberRides/RidesClient.swift @@ -30,7 +30,7 @@ import UberCore public class RidesClient { /// Application client ID. Required for every instance of RidesClient. - var clientID: String = Configuration.shared.clientID + var clientID: String /// The Access Token Identifier. The identifier to use for looking up this client's accessToken let accessTokenIdentifier: String @@ -42,7 +42,7 @@ public class RidesClient { var session: URLSession /// Developer server token. - private var serverToken: String? = Configuration.shared.serverToken + private let serverToken: String? private let tokenManager = TokenManager() @@ -60,7 +60,12 @@ public class RidesClient { - returns: An initialized RidesClient */ - public init(accessTokenIdentifier: String, sessionConfiguration: URLSessionConfiguration, keychainAccessGroup: String) { + public init(accessTokenIdentifier: String, + sessionConfiguration: URLSessionConfiguration, + keychainAccessGroup: String, + configurationProvider: ConfigurationProviding = ConfigurationProvider()) { + self.clientID = configurationProvider.clientID + self.serverToken = configurationProvider.serverToken self.accessTokenIdentifier = accessTokenIdentifier self.keychainAccessGroup = keychainAccessGroup self.session = URLSession(configuration: sessionConfiguration) @@ -101,7 +106,7 @@ public class RidesClient { public convenience init(accessTokenIdentifier: String, sessionConfiguration: URLSessionConfiguration) { self.init(accessTokenIdentifier: accessTokenIdentifier, sessionConfiguration: sessionConfiguration, - keychainAccessGroup: Configuration.shared.defaultKeychainAccessGroup) + keychainAccessGroup: TokenManager.defaultKeychainAccessGroup) } /** @@ -118,7 +123,7 @@ public class RidesClient { public convenience init(accessTokenIdentifier: String) { self.init(accessTokenIdentifier: accessTokenIdentifier, sessionConfiguration: URLSessionConfiguration.default, - keychainAccessGroup: Configuration.shared.defaultKeychainAccessGroup) + keychainAccessGroup: TokenManager.defaultKeychainAccessGroup) } /** @@ -131,9 +136,9 @@ public class RidesClient { - returns: An initialized RidesClient */ public convenience init() { - self.init(accessTokenIdentifier: Configuration.shared.defaultAccessTokenIdentifier, + self.init(accessTokenIdentifier: TokenManager.defaultAccessTokenIdentifier, sessionConfiguration: URLSessionConfiguration.default, - keychainAccessGroup: Configuration.shared.defaultKeychainAccessGroup) + keychainAccessGroup: TokenManager.defaultKeychainAccessGroup) } /** diff --git a/Sources/UberRides/RidesCore/Configuration.swift b/Sources/UberRides/RidesCore/Configuration.swift deleted file mode 100644 index 975fb81a..00000000 --- a/Sources/UberRides/RidesCore/Configuration.swift +++ /dev/null @@ -1,338 +0,0 @@ -// -// Configuration.swift -// UberRides -// -// Copyright © 2016 Uber Technologies, Inc. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -import Foundation -import WebKit - -private let clientIDKey = "UberClientID" -private let appNameKey = "UberDisplayName" -private let serverTokenKey = "UberServerToken" -private let callbackURIKey = "UberCallbackURI" -private let callbackURIsKey = "UberCallbackURIs" -private let callbackURIsTypeKey = "UberCallbackURIType" -private let callbackURIStringKey = "URIString" - -/** - An enum to represent the possible callback URI types. Each form of authorization - could potentially use a different URI, these are the possible types. - - - AuthorizationCode: Callback URI to use for Authorization Code Grant flow - - General: Callback URI to use for any flow - - Implicit: Callback URI to use for Implicit Grant flow - - Native: Callback URI to use for Native (SSO) flow - */ -public enum CallbackURIType : Int { - case authorizationCode - case general - case implicit - case native - - func toString() -> String { - switch self { - case .authorizationCode: - return "AuthorizationCode" - case .general: - return "General" - case .implicit: - return "Implicit" - case .native: - return "Native" - } - } - - static func fromString(_ string: String) -> CallbackURIType { - switch string { - case CallbackURIType.authorizationCode.toString(): - return .authorizationCode - case CallbackURIType.implicit.toString(): - return .implicit - case CallbackURIType.native.toString(): - return .native - case CallbackURIType.general.toString(): - fallthrough - default: - return .general - } - } -} - -/** - Class responsible for handling all of the SDK Configuration options. Provides - default values for Application-wide configuration properties. All properties are - configurable via the respective setter method -*/ -public class Configuration { - // MARK : Variables - public static var shared: Configuration = Configuration() - - /// The .plist file to use, default is Info.plist - public static var plistName = "Info" - - /// The bundle that contains the .plist file. Default is the mainBundle() - public static var bundle = Bundle.main - - public var processPool = WKProcessPool() - - /** - Gets the client ID of this app. Defaults to the value stored in your Application's - plist if not set (UberClientID) - - - returns: The string to use for the Client ID - */ - public var clientID: String - - private var callbackURIs = [CallbackURIType: URL]() - - /** - Gets the display name of this app. Defaults to the value stored in your Appication's - plist if not set (UberClientID) - - - returns: The app's name - */ - public var appDisplayName: String - - /** - Gets the Server Token of this app. Defaults to the value stored in your Appication's - plist if not set (UberServerToken) - Optional. Used by the Request Button to get time estimates without requiring - login - - - returns: The string Representing your app's server token - */ - public var serverToken: String? - - /** - Gets the default keychain access group to save access tokens to. Advanced setting - for sharing access tokens between multiple of your apps. Defaults an empty string - - - returns: The default keychain access group to use - */ - public var defaultKeychainAccessGroup: String = "" - - /** - Gets the default key to use when saving access tokens to the keychain. Defaults - to using "RidesAccessTokenKey" - - - returns: The default access token identifier to use - */ - public var defaultAccessTokenIdentifier: String = "RidesAccessTokenKey" - - /** - Returns if sandbox is enabled or not - - - returns: true if Sandbox is enabled, false otherwise - */ - public var isSandbox: Bool = false - - /** - Returns if the fallback to use Authorization Code Grant is enabled. If true, - a failed SSO attempt will follow up with an attempt to do Authorization Code Grant - (if requesting priveleged scopes). If false, the user will be redirected to the app store - - - returns: true if fallback enabled, false otherwise - */ - public var useFallback: Bool = false - - /** - Returns if the fallback to use Authorization Code Grant is enabled. If true, - a failed SSO attempt will follow up with an attempt to do Authorization Code Grant, - regardless of scopes requested. - - - returns: true if fallback enabled, false otherwise - */ - public var alwaysUseAuthCodeFallback: Bool = false - - public init() { - self.clientID = "" - self.appDisplayName = "" - - if let defaultValue = getDefaultValue(clientIDKey) { - self.clientID = defaultValue - } else { - fatalConfigurationError("ClientID", key: clientIDKey) - } - if let defaultValue = getDefaultValue(appNameKey) { - self.appDisplayName = defaultValue - } else { - fatalConfigurationError("appDisplayName", key: appNameKey) - } - serverToken = getDefaultValue(serverTokenKey) - } - - /// The current version of the SDK as a string - public var sdkVersion: String { - guard let version = Bundle(for: Configuration.self).object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String else { - return "Unknown" - } - return version - } - - /** - Resets all of the Configuration's values to default - */ - public static func restoreDefaults() { - shared = Configuration() - } - - // MARK: Getters - - /** - Gets the callback URI of this app. Defaults to the value stored in your Application's - plist if not set (UberCallbackURI) - - - returns: The string to use for the Callback URI - */ - public func getCallbackURI() -> URL { - return getCallbackURI(for: .general) - } - - /** - Gets the callback URIString of this app. Defaults to the value stored in your Application's - plist if not set (UberCallbackURI) - - - returns: The string to use for the Callback URI - */ - public func getCallbackURIString() -> String { - return getCallbackURI().absoluteString - } - - /** - Gets the callback URI for the given CallbackURIType. Defaults to the value - stored in your Applications' plist (under the UberCallbackURIs key). If the requested - type is not defined in your plist, it will attempt to use the .General type. If the - .General type is not defined, it will attempt to use the value stored under the UberCallbackURI key. - Throws a fatal error if no value can be determined - - - parameter type: The CallbackURIType to get a callback string for - - - returns: The callbackURI for the the requested type - */ - public func getCallbackURI(for type: CallbackURIType) -> URL { - if callbackURIs[type] == nil { - let defaultCallbacks = parseCallbackURIs() - var fallback = defaultCallbacks[type] ?? callbackURIs[.general] - fallback = fallback ?? defaultCallbacks[.general] - if let defaultValue = getDefaultValue(callbackURIKey) { - fallback = fallback ?? URL(string: defaultValue) - } - guard let fallbackCallback = fallback else { - fatalConfigurationError("CallbackURI[\(type.toString())]", key: callbackURIsKey) - } - callbackURIs[type] = fallbackCallback - } - return callbackURIs[type]! - } - - /** - Gets the callback URIString for the given CallbackURIType. Defaults to the value - stored in your Applications' plist (under the UberCallbackURIs key). If the requested - type is not defined in your plist, it will attempt to use the .General type. If the - .General type is not defined, it will attempt to use the value stored under the UberCallbackURI key. - Throws a fatal error if no value can be determined - - - parameter type: The CallbackURIType to get a callback string for - - - returns: The callbackURIString for the the requested type - */ - public func getCallbackURIString(for type: CallbackURIType) -> String { - return getCallbackURI(for: type).absoluteString - } - - //MARK: Setters - - /** - Sets a string to use as the Callback URI String. Overwrites the default value provided by - the plist. Setting to nil will result in using the default value. - If you're setting a custom value, be sure your app is configured to handle deeplinks - from this URI & you've added it to the redirect URIs on your Uber developer dashboard - - - parameter callbackURI: The callback URI String to use - */ - public func setCallbackURI(_ callbackURI: URL?) { - setCallbackURI(callbackURI, type: .general) - } - - /** - Sets a string to use as the Callback URI String for the provided CallbackURIType. - Overwrites the default value provided by the plist. Setting to nil will result - in using the default value. - If you're setting a custom value, be sure your app is configured to handle deeplinks - from this URI & you've added it to the redirect URIs on your Uber developer dashboard - - - parameter callbackURI: The callback URI String to use - - parameter type: The Callback URI Type to use - */ - public func setCallbackURI(_ callbackURI: URL?, type: CallbackURIType) { - var callbackURIs = self.callbackURIs - callbackURIs[type] = callbackURI - self.callbackURIs = callbackURIs - } - - public func resetProcessPool() { - DispatchQueue.main.async { - self.processPool = WKProcessPool() - } - } - - // MARK: Private - - private func parseCallbackURIs() -> [CallbackURIType : URL] { - guard let plist = getPlistDictionary(), let callbacks = plist[callbackURIsKey] as? [[String: AnyObject]] else { - return [CallbackURIType: URL]() - } - var callbackURIs = [CallbackURIType : URL]() - - for callbackObject in callbacks { - guard let callbackTypeString = callbackObject[callbackURIsTypeKey] as? String, - let uriString = callbackObject[callbackURIStringKey] as? String, - let uri = URL(string: uriString) else { - continue - } - let callbackType = CallbackURIType.fromString(callbackTypeString) - callbackURIs[callbackType] = uri - } - return callbackURIs - } - - private func fatalConfigurationError(_ variableName: String, key: String ) -> Never { - fatalError("Unable to get your \(variableName). Did you forget to set it in your \(Configuration.plistName).plist? (Should be under \(key) key)") - } - - private func getPlistDictionary() -> [String : AnyObject]? { - guard let path = Configuration.bundle.path(forResource: Configuration.plistName, ofType: "plist"), - let dictionary = NSDictionary(contentsOfFile: path) as? [String: AnyObject] else { - return nil - } - return dictionary - } - - private func getDefaultValue(_ key: String) -> String? { - guard let dictionary = getPlistDictionary(), - let defaultValue = dictionary[key] as? String else { - return nil - } - - return defaultValue - } -} diff --git a/Sources/UberRides/RidesCore/Deeplinks/EatsAppStoreDeeplink.swift b/Sources/UberRides/RidesCore/Deeplinks/EatsAppStoreDeeplink.swift index 0303d50e..3b9c219d 100644 --- a/Sources/UberRides/RidesCore/Deeplinks/EatsAppStoreDeeplink.swift +++ b/Sources/UberRides/RidesCore/Deeplinks/EatsAppStoreDeeplink.swift @@ -34,14 +34,14 @@ public class EatsAppStoreDeeplink: BaseDeeplink { - returns: An initialized AppStoreDeeplink */ - public init(userAgent: String?) { + public init(userAgent: String?, clientID: String, sdkVersion: String) { let scheme = "https" let domain = "itunes.apple.com" let path = "/app/id1058959277" - let clientIDQueryItem = URLQueryItem(name: "client_id", value: Configuration.shared.clientID) + let clientIDQueryItem = URLQueryItem(name: "client_id", value: clientID) - let userAgent = userAgent ?? "rides-ios-v\(Configuration.shared.sdkVersion)" + let userAgent = userAgent ?? "rides-ios-v\(sdkVersion)" let userAgentQueryItem = URLQueryItem(name: "user-agent", value: userAgent) diff --git a/Sources/UberRides/RidesCore/Deeplinks/RidesAppStoreDeeplink.swift b/Sources/UberRides/RidesCore/Deeplinks/RidesAppStoreDeeplink.swift index 8cc3edc6..5979cb71 100644 --- a/Sources/UberRides/RidesCore/Deeplinks/RidesAppStoreDeeplink.swift +++ b/Sources/UberRides/RidesCore/Deeplinks/RidesAppStoreDeeplink.swift @@ -34,14 +34,14 @@ public class RidesAppStoreDeeplink: BaseDeeplink { - returns: An initialized AppStoreDeeplink */ - public init(userAgent: String?) { + public init(userAgent: String?, clientID: String, sdkVersion: String) { let scheme = "https" let domain = "m.uber.com" let path = "/sign-up" - let clientIDQueryItem = URLQueryItem(name: "client_id", value: Configuration.shared.clientID) + let clientIDQueryItem = URLQueryItem(name: "client_id", value: clientID) - let userAgent = userAgent ?? "rides-ios-v\(Configuration.shared.sdkVersion)" + let userAgent = userAgent ?? "rides-ios-v\(sdkVersion)" let userAgentQueryItem = URLQueryItem(name: "user-agent", value: userAgent) diff --git a/Sources/UberRides/RidesCore/Networking/APIEndpoint.swift b/Sources/UberRides/RidesCore/Networking/APIEndpoint.swift index 5f4b129f..e26fd4df 100644 --- a/Sources/UberRides/RidesCore/Networking/APIEndpoint.swift +++ b/Sources/UberRides/RidesCore/Networking/APIEndpoint.swift @@ -23,6 +23,7 @@ // THE SOFTWARE. import Foundation +import UberCore /** * Protocol for all endpoints to conform to. @@ -47,7 +48,7 @@ public extension APIEndpoint { } var host: String { - if Configuration.shared.isSandbox { + if ConfigurationProvider.isSandbox { return "https://sandbox-api.uber.com" } else { return "https://api.uber.com" diff --git a/Sources/UberRides/RidesCore/Networking/EndpointsManager.swift b/Sources/UberRides/RidesCore/Networking/EndpointsManager.swift index c9434c86..667ec910 100644 --- a/Sources/UberRides/RidesCore/Networking/EndpointsManager.swift +++ b/Sources/UberRides/RidesCore/Networking/EndpointsManager.swift @@ -23,6 +23,7 @@ // THE SOFTWARE. import CoreLocation +import UberAuth import UberCore /// Convenience enum for managing versions of resources. @@ -82,7 +83,7 @@ enum Components: APIEndpoint { var query: [URLQueryItem] { switch self { case .rideRequestWidget(let rideParameters): - let environment = Configuration.shared.isSandbox ? "sandbox" : "production" + let environment = ConfigurationProvider.isSandbox ? "sandbox" : "production" var queryItems = queryBuilder( ("env", "\(environment)") ) if let rideParameters = rideParameters { diff --git a/Sources/UberRides/RidesCore/Utilities/RidesUtil.swift b/Sources/UberRides/RidesCore/Utilities/RidesUtil.swift index 2eef6a1c..59215d73 100644 --- a/Sources/UberRides/RidesCore/Utilities/RidesUtil.swift +++ b/Sources/UberRides/RidesCore/Utilities/RidesUtil.swift @@ -25,6 +25,7 @@ import Foundation import CoreLocation +import UberAuth import UberCore import UIKit @@ -97,6 +98,7 @@ class RequestURLUtil { case Dropoff = "dropoff" } + private static let configurationProvider = ConfigurationProvider() static let actionKey = "action" static let setPickupValue = "setPickup" static let clientIDKey = "client_id" @@ -112,7 +114,7 @@ class RequestURLUtil { var queryItems = [URLQueryItem]() queryItems.append(URLQueryItem(name: RequestURLUtil.actionKey, value: RequestURLUtil.setPickupValue)) - queryItems.append(URLQueryItem(name: RequestURLUtil.clientIDKey, value: Configuration.shared.clientID)) + queryItems.append(URLQueryItem(name: RequestURLUtil.clientIDKey, value: configurationProvider.clientID)) if let productID = rideParameters.productID { queryItems.append(URLQueryItem(name: RequestURLUtil.productIDKey, value: productID)) diff --git a/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj b/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj index db35a694..fae906cc 100644 --- a/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj +++ b/examples/UberSDK/UberSDK.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + B21C2DCC2C656BFC00337E93 /* UberSDK.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UberSDK.xctestplan; sourceTree = ""; }; B21E42772BE2FF6500D9C5E1 /* KeychainUtilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtilityTests.swift; sourceTree = ""; }; B243DF562BFE474C0086160C /* UberSDK.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = UberSDK.entitlements; sourceTree = ""; }; B25D30892BD03F50008A67CF /* UberButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UberButtonView.swift; sourceTree = ""; }; @@ -103,6 +104,7 @@ B29303992B89113F00D28BAA = { isa = PBXGroup; children = ( + B21C2DCC2C656BFC00337E93 /* UberSDK.xctestplan */, B2D7AE1A2B979EDA007F03FB /* UberSDK */, B2D7AE2B2B979EDB007F03FB /* UberSDKTests */, B29303A32B89113F00D28BAA /* Products */, diff --git a/examples/UberSDK/UberSDK.xcodeproj/xcshareddata/xcschemes/UberSDK.xcscheme b/examples/UberSDK/UberSDK.xcodeproj/xcshareddata/xcschemes/UberSDK.xcscheme index a4fe23ec..4e3e9c79 100644 --- a/examples/UberSDK/UberSDK.xcodeproj/xcshareddata/xcschemes/UberSDK.xcscheme +++ b/examples/UberSDK/UberSDK.xcodeproj/xcshareddata/xcschemes/UberSDK.xcscheme @@ -26,8 +26,13 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - shouldAutocreateTestPlan = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - UberClientID - 9QZcD_Ki6NbhGCrVXSUHCxfevm-C9Khj UberDisplayName - Test + CFBundleURLTypes @@ -23,12 +21,14 @@ ubereats uberdriver - UberAuth + Uber ClientID 9QZcD_Ki6NbhGCrVXSUHCxfevm-C9Khj RedirectURI com.uber.UberSDK://oauth/consumer + DisplayName + Uber SDK diff --git a/examples/UberSDK/UberSDKTests/Mocks/Mocks.swift b/examples/UberSDK/UberSDKTests/Mocks/Mocks.swift index fde17e15..c0258ec4 100644 --- a/examples/UberSDK/UberSDKTests/Mocks/Mocks.swift +++ b/examples/UberSDK/UberSDKTests/Mocks/Mocks.swift @@ -51,13 +51,13 @@ class NetworkProvidingMock: NetworkProviding { } } -class ApplicationLaunchingMock: ApplicationLaunching { - init() { } +public class ApplicationLaunchingMock: ApplicationLaunching { + public init() { } - private(set) var openCallCount = 0 - var openHandler: ((URL, [UIApplication.OpenExternalURLOptionsKey: Any], ((Bool) -> Void)?) -> ())? - func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey: Any], completionHandler: ((Bool) -> Void)?) { + public private(set) var openCallCount = 0 + public var openHandler: ((URL, [UIApplication.OpenExternalURLOptionsKey: Any], ((Bool) -> Void)?) -> ())? + public func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey: Any], completionHandler: ((Bool) -> Void)?) { openCallCount += 1 if let openHandler = openHandler { openHandler(url, options, completionHandler) @@ -66,23 +66,38 @@ class ApplicationLaunchingMock: ApplicationLaunching { } } -class ConfigurationProvidingMock: ConfigurationProviding { - init() { } - init(clientID: String? = nil, redirectURI: String? = nil) { +public class ConfigurationProvidingMock: ConfigurationProviding { + public init() { } + public init(clientID: String = "", redirectURI: String = "", sdkVersion: String = "", serverToken: String? = nil) { self.clientID = clientID self.redirectURI = redirectURI + self.sdkVersion = sdkVersion + self.serverToken = serverToken } - private(set) var clientIDSetCallCount = 0 - var clientID: String? = nil { didSet { clientIDSetCallCount += 1 } } + public private(set) var clientIDSetCallCount = 0 + public var clientID: String = "" { didSet { clientIDSetCallCount += 1 } } - private(set) var redirectURISetCallCount = 0 - var redirectURI: String? = nil { didSet { redirectURISetCallCount += 1 } } + public private(set) var redirectURISetCallCount = 0 + public var redirectURI: String = "" { didSet { redirectURISetCallCount += 1 } } + + public private(set) var sdkVersionSetCallCount = 0 + public var sdkVersion: String = "" { didSet { sdkVersionSetCallCount += 1 } } + + public private(set) var serverTokenSetCallCount = 0 + public var serverToken: String? = nil { didSet { serverTokenSetCallCount += 1 } } + + public static private(set) var isSandboxSetCallCount = 0 + static private var _isSandbox: Bool = false { didSet { isSandboxSetCallCount += 1 } } + public static var isSandbox: Bool { + get { return _isSandbox } + set { _isSandbox = newValue } + } - private(set) var isInstalledCallCount = 0 - var isInstalledHandler: ((UberApp, Bool) -> (Bool))? - func isInstalled(app: UberApp, defaultIfUnregistered: Bool) -> Bool { + public private(set) var isInstalledCallCount = 0 + public var isInstalledHandler: ((UberApp, Bool) -> (Bool))? + public func isInstalled(app: UberApp, defaultIfUnregistered: Bool) -> Bool { isInstalledCallCount += 1 if let isInstalledHandler = isInstalledHandler { return isInstalledHandler(app, defaultIfUnregistered) diff --git a/examples/UberSDK/UberSDKTests/UberAuth/AuthorizeRequestTests.swift b/examples/UberSDK/UberSDKTests/UberAuth/AuthorizeRequestTests.swift index 3ba30d1d..aaca6b90 100644 --- a/examples/UberSDK/UberSDKTests/UberAuth/AuthorizeRequestTests.swift +++ b/examples/UberSDK/UberSDKTests/UberAuth/AuthorizeRequestTests.swift @@ -4,6 +4,7 @@ @testable import UberAuth +import UberCore import XCTest final class AuthorizeRequestTests: XCTestCase {