Skip to content

Commit

Permalink
Explore EUID Support
Browse files Browse the repository at this point in the history
  • Loading branch information
dcaunt committed Aug 8, 2024
1 parent 923fe66 commit 4890a22
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@ class RootViewModel: ObservableObject {

/// `UID2Settings` must be configured prior to accessing the `UID2Manager` instance.
/// Configuring them here makes it less likely that an access occurs before configuration.
private let manager: UID2Manager = {
UID2Settings.shared.isLoggingEnabled = true
private let manager: UIDManager = {
let environment: UID2.Environment

// Only the development app should use the integration environment.
// If you have copied the dev app for testing, you probably want to use the default
// environment, which is production.
if Bundle.main.bundleIdentifier == "com.uid2.UID2SDKDevelopmentApp" {
UID2Settings.shared.environment = .custom(url: URL(string: "https://operator-integ.uidapi.com")!)
environment = .custom(url: URL(string: "https://operator-integ.uidapi.com")!)
} else {
environment = .production
}

return UID2Manager.shared
return UID.shared.uid2Manager(isLoggingEnabled: true, environment: environment)
}()

init() {
Expand Down
88 changes: 67 additions & 21 deletions Sources/UID2/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,74 @@

import Foundation

/// For more information, see https://unifiedid.com/docs/getting-started/gs-environments
public struct Environment: Hashable, Sendable {
struct Environment: Hashable, Sendable {

/// API base URL
var endpoint: URL

/// Equivalent to `ohio`
public static let production = ohio

/// AWS US East (Ohio)
public static let ohio = Self(endpoint: URL(string: "https://prod.uidapi.com")!)
/// AWS US West (Oregon)
public static let oregon = Self(endpoint: URL(string: "https://usw.prod.uidapi.com")!)
/// AWS Asia Pacific (Singapore)
public static let singapore = Self(endpoint: URL(string: "https://sg.prod.uidapi.com")!)
/// AWS Asia Pacific (Sydney)
public static let sydney = Self(endpoint: URL(string: "https://au.prod.uidapi.com")!)
/// AWS Asia Pacific (Tokyo)
public static let tokyo = Self(endpoint: URL(string: "https://jp.prod.uidapi.com")!)

/// A custom endpoint
public static func custom(url: URL) -> Self {
Self(endpoint: url)
let endpoint: URL
let isProduction: Bool
}

extension Environment {
init(_ environment: UID2.Environment) {
endpoint = environment.endpoint
isProduction = (environment == .production)
}

init(_ environment: EUID.Environment) {
endpoint = environment.endpoint
isProduction = (environment == .production)
}
}

// Namespaces
public enum EUID {}
public enum UID2 {}

extension UID2 {
/// For more information, see https://unifiedid.com/docs/getting-started/gs-environments
public struct Environment: Hashable, Sendable {

/// API base URL
var endpoint: URL

/// Equivalent to `ohio`
public static let production = ohio

/// AWS US East (Ohio)
public static let ohio = Self(endpoint: URL(string: "https://prod.uidapi.com")!)
/// AWS US West (Oregon)
public static let oregon = Self(endpoint: URL(string: "https://usw.prod.uidapi.com")!)
/// AWS Asia Pacific (Singapore)
public static let singapore = Self(endpoint: URL(string: "https://sg.prod.uidapi.com")!)
/// AWS Asia Pacific (Sydney)
public static let sydney = Self(endpoint: URL(string: "https://au.prod.uidapi.com")!)
/// AWS Asia Pacific (Tokyo)
public static let tokyo = Self(endpoint: URL(string: "https://jp.prod.uidapi.com")!)

/// A custom endpoint
public static func custom(url: URL) -> Self {
Self(endpoint: url)
}
}
}

extension EUID {
/// See https://euid.eu/docs/getting-started/gs-environments
public struct Environment: Hashable, Sendable {

/// API base URL
var endpoint: URL

/// Equivalent to `london`
public static let production = london

/// AWS EU West 2 (London)
public static let london = Self(endpoint: URL(string: "https://prod.euid.eu/v2")!)

/// A custom endpoint
public static func custom(url: URL) -> Self {
// TODO: Assert/enforce euid.eu host

Check warning on line 76 in Sources/UID2/Environment.swift

View workflow job for this annotation

GitHub Actions / Code Tests

TODOs should be resolved (Assert/enforce euid.eu host) (todo)
Self(endpoint: url)
}
}
}
17 changes: 13 additions & 4 deletions Sources/UID2/KeychainManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import Foundation
import Security

extension Storage {
static func keychainStorage() -> Storage {
let storage = KeychainManager()
static func keychainStorage(account: Account) -> Storage {
let storage = KeychainManager(account: account)
return .init(
loadIdentity: { await storage.loadIdentity() },
saveIdentity: { await storage.saveIdentity($0) },
Expand All @@ -16,13 +16,22 @@ extension Storage {
}
}

enum Account: String {
case uid2
case euid
}

/// Securely manages data in the Keychain
actor KeychainManager {

private let attrAccount = "uid2"
private let attrAccount: Account

private static let attrService = "auth-state"

init(account: Account = .uid2) {
attrAccount = account
}

func loadIdentity() -> IdentityPackage? {
let query = query(with: [
String(kSecReturnData): true
Expand Down Expand Up @@ -77,7 +86,7 @@ actor KeychainManager {
private func query(with queryElements: [String: Any]) -> CFDictionary {
let commonElements = [
String(kSecClass): kSecClassGenericPassword,
String(kSecAttrAccount): attrAccount,
String(kSecAttrAccount): attrAccount.rawValue,
String(kSecAttrService): Self.attrService
] as [String: Any]

Expand Down
4 changes: 2 additions & 2 deletions Sources/UID2/UID2Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal final class UID2Client: Sendable {
init(
sdkVersion: String,
isLoggingEnabled: Bool = false,
environment: Environment = .production,
environment: Environment,
session: NetworkSession = URLSession.shared,
cryptoUtil: CryptoUtil = .liveValue
) {
Expand Down Expand Up @@ -122,7 +122,7 @@ internal final class UID2Client: Sendable {
let statusCode = response.statusCode
let responseText = String(data: data, encoding: .utf8) ?? "<none>"

Check warning on line 123 in Sources/UID2/UID2Client.swift

View workflow job for this annotation

GitHub Actions / Code Tests

Prefer using UTF-8 encoded strings when converting between `String` and `Data` (non_optional_string_data_conversion)
os_log("Request failure (%d) %@", log: log, type: .error, statusCode, responseText)
if environment != .production {
if !environment.isProduction {
os_log("Failed request is using non-production API endpoint %@, is this intentional?", log: log, type: .error, baseURL.description)
}
throw TokenGenerationError.requestFailure(
Expand Down
8 changes: 4 additions & 4 deletions Sources/UID2/UID2Manager.State.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//
// UID2Manager.State.swift
// UIDManager.State.swift
//

import Foundation

extension UID2Manager {
extension UIDManager {
public enum State: Hashable, Sendable, Codable {
case optout
case established(UID2Identity)
Expand All @@ -15,7 +15,7 @@ extension UID2Manager {
}
}

extension UID2Manager.State {
extension UIDManager.State {
/// A 'case path' returning the current `IdentityStatus`.
public var identityStatus: IdentityStatus {
switch self {
Expand Down Expand Up @@ -49,7 +49,7 @@ extension UID2Manager.State {
}
}

extension UID2Manager.State {
extension UIDManager.State {
init?(_ package: IdentityPackage) {
switch package.status {
case .established:
Expand Down
64 changes: 53 additions & 11 deletions Sources/UID2/UID2Manager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,43 @@ import Combine
import Foundation
import OSLog

public final class UID: @unchecked Sendable {

public static let shared = UID()

public init() {}

private var uid2: UIDManager?
public func uid2Manager(isLoggingEnabled: Bool, environment: UID2.Environment) -> UIDManager {
guard let uid2 else {
let manager = UIDManager(
isLoggingEnabled: isLoggingEnabled,
environment: Environment(environment),
account: .uid2
)
uid2 = manager
return manager
}
return uid2
}

private var euid: UIDManager?
public func euidManager(isLoggingEnabled: Bool, environment: EUID.Environment) -> UIDManager {
guard let euid else {
let manager = UIDManager(
isLoggingEnabled: isLoggingEnabled,
environment: Environment(environment),
account: .uid2
)
euid = manager
return manager
}
return euid
}
}

// swiftlint:disable:next type_body_length
public final actor UID2Manager {
public final actor UIDManager {
private enum InitializationState {
case pending
case complete
Expand All @@ -32,8 +67,12 @@ public final actor UID2Manager {
}

/// Singleton access point for UID2Manager
public static let shared = UID2Manager()

@available(*, deprecated, renamed: "uid2Manager()")
public static let shared = UID.shared.uid2Manager(
isLoggingEnabled: false,
environment: .production
)

/// Enable or Disable Automatic Refresh via RepeatingTimer
public var automaticRefreshEnabled = true {
didSet {
Expand Down Expand Up @@ -103,21 +142,24 @@ public final actor UID2Manager {

// MARK: - Defaults

internal init() {
init(
isLoggingEnabled: Bool,
environment: Environment,
account: Account
) {
// App Supplied Properties
let environment: Environment
if let apiUrlOverride = Bundle.main.object(forInfoDictionaryKey: "UID2ApiUrl") as? String,
let clientEnvironment: Environment
if let apiUrlOverride = Bundle.main.object(forInfoDictionaryKey: "UID2ApiUrl") as? String,
!apiUrlOverride.isEmpty,
let apiUrl = URL(string: apiUrlOverride) {
environment = Environment(endpoint: apiUrl)
clientEnvironment = Environment(endpoint: apiUrl, isProduction: false)
} else {
environment = UID2Settings.shared.environment
clientEnvironment = environment
}

let sdkVersion = UID2SDKProperties.getUID2SDKVersion()
let clientVersion = "\(sdkVersion.major).\(sdkVersion.minor).\(sdkVersion.patch)"

let isLoggingEnabled = UID2Settings.shared.isLoggingEnabled
let log = isLoggingEnabled
? OSLog(subsystem: "com.uid2", category: "UID2Manager")
: .disabled
Expand All @@ -126,9 +168,9 @@ public final actor UID2Manager {
uid2Client: UID2Client(
sdkVersion: clientVersion,
isLoggingEnabled: isLoggingEnabled,
environment: environment
environment: clientEnvironment
),
storage: .keychainStorage(),
storage: .keychainStorage(account: account),
sdkVersion: sdkVersion,
log: log
)
Expand Down
52 changes: 0 additions & 52 deletions Sources/UID2/UID2Settings.swift

This file was deleted.

5 changes: 3 additions & 2 deletions Tests/UID2Tests/RefreshRequestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ final class RefreshRequestTests: XCTestCase {

func testRequest() async throws {
let request = Request.refresh(token: "im-a-refresh-token")
let client = UID2Client(
sdkVersion: "1.2.3"
let client = UID2Client.test(
sdkVersion: "1.2.3",
session: URLSession.shared
)
let urlRequest = client.urlRequest(request)

Expand Down
Loading

0 comments on commit 4890a22

Please sign in to comment.