import Foundation import MyIdSDK import UIKit /// Translates the JS payload into the SDK's own config objects. /// /// A property is assigned only when its key is present, so an omitted option — /// or an unrecognised enum string — keeps the default `MyIdConfig()` and /// `MyIdAppearance()` already set. enum MyidSdkConfigBuilder { static func makeConfig( from dictionary: NSDictionary, appearance appearanceDictionary: NSDictionary ) -> MyIdConfig { let config = MyIdConfig() config.sessionId = dictionary.string(for: "sessionId") config.clientHash = dictionary.string(for: "clientHash") config.clientHashId = dictionary.string(for: "clientHashId") if let minAge = dictionary.number(for: "minAge") { config.minAge = minAge.intValue } if let distance = dictionary.number(for: "distance") { config.distance = distance.floatValue } if let showErrorScreen = dictionary.number(for: "showErrorScreen") { config.showErrorScreen = showErrorScreen.boolValue } if let environment = dictionary.enumValue(for: "environment") { switch environment { case "DEBUG": config.environment = .debug case "PRODUCTION": config.environment = .production default: break } } if let entryType = dictionary.enumValue(for: "entryType") { switch entryType { case "IDENTIFICATION": config.entryType = .identification case "VIDEO_IDENTIFICATION": config.entryType = .videoIdentification case "FACE_DETECTION": config.entryType = .faceDetection default: break } } if let residency = dictionary.enumValue(for: "residency") { switch residency { case "USER_DEFINED": config.residency = .userDefined case "RESIDENT": config.residency = .resident case "NON_RESIDENT": config.residency = .nonResident default: break } } if let locale = dictionary.enumValue(for: "locale") { switch locale { case "ENGLISH": config.locale = .english case "RUSSIAN": config.locale = .russian // iOS has no Cyrillic, Karakalpak or Tajik locale. case "UZBEK", "UZBEK_CYRILLIC", "KARAKALPAK", "TAJIK": config.locale = .uzbek default: break } } if let cameraShape = dictionary.enumValue(for: "cameraShape") { switch cameraShape { case "CIRCLE": config.cameraShape = .circle case "ELLIPSE": config.cameraShape = .ellipse default: break } } if let cameraSelector = dictionary.enumValue(for: "cameraSelector") { switch cameraSelector { case "FRONT": config.cameraSelector = .front case "BACK": config.cameraSelector = .back default: break } } if let presentationStyle = dictionary.enumValue(for: "presentationStyle") { switch presentationStyle { case "SHEET": config.presentationStyle = .sheet case "FULL": config.presentationStyle = .full default: break } } // An empty details object would replace the SDK default, which carries // MyID's call-centre number. if let details = dictionary["organizationDetails"] as? NSDictionary, details.count > 0 { let organizationDetails = MyIdOrganizationDetails() if let phone = details.string(for: "phone") { organizationDetails.phoneNumber = phone } if let logoName = details.string(for: "logo") { organizationDetails.logo = UIImage(named: logoName) } config.organizationDetails = organizationDetails } if appearanceDictionary.count > 0 { config.appearance = makeAppearance(from: appearanceDictionary) } return config } /// Assignments are guarded: `MyIdAppearance()` seeds its own values, so /// writing nil for an omitted key would strip them. private static func makeAppearance(from dictionary: NSDictionary) -> MyIdAppearance { let appearance = MyIdAppearance() let colors: [(String, (UIColor) -> Void)] = [ ("colorPrimary", { appearance.colorPrimary = $0 }), ("colorOnPrimary", { appearance.colorOnPrimary = $0 }), ("colorError", { appearance.colorError = $0 }), ("colorOnError", { appearance.colorOnError = $0 }), ("colorOutline", { appearance.colorOutline = $0 }), ("colorDivider", { appearance.colorDivider = $0 }), ("colorSuccess", { appearance.colorSuccess = $0 }), ("colorButtonContainer", { appearance.colorButtonContainer = $0 }), ("colorButtonContainerDisabled", { appearance.colorButtonContainerDisabled = $0 }), ("colorButtonContent", { appearance.colorButtonContent = $0 }), ("colorButtonContentDisabled", { appearance.colorButtonContentDisabled = $0 }), ("colorScanButtonContainer", { appearance.colorScanButtonContainer = $0 }), ] for (key, assign) in colors { if let color = dictionary.color(for: key) { assign(color) } } if let cornerRadius = dictionary.number(for: "buttonCornerRadius") { appearance.buttonCornerRadius = cornerRadius.floatValue } return appearance } } private extension NSDictionary { func string(for key: String) -> String? { self[key] as? String } func number(for key: String) -> NSNumber? { self[key] as? NSNumber } /// Normalise case so "debug" works like "DEBUG". func enumValue(for key: String) -> String? { string(for: key)?.uppercased() } func color(for key: String) -> UIColor? { guard let hex = string(for: key) else { return nil } return UIColor(myIdHex: hex) } } extension UIColor { /// Accepts `#RRGGBB` and `RRGGBB`; nil for anything unparseable. convenience init?(myIdHex hex: String) { var value = hex.trimmingCharacters(in: .whitespacesAndNewlines) if value.hasPrefix("#") { value.removeFirst() } guard value.count == 6 else { return nil } var rgb: UInt64 = 0 guard Scanner(string: value).scanHexInt64(&rgb) else { return nil } self.init( red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgb & 0x0000FF) / 255.0, alpha: 1.0 ) } }