Repository: smartlook/ios-consent-sdk Branch: master Commit: 7ab10b7bb587 Files: 63 Total size: 184.8 KB Directory structure: gitextract_br9jtfir/ ├── .gitignore ├── .swiftpm/ │ └── xcode/ │ └── package.xcworkspace/ │ └── contents.xcworkspacedata ├── CHANGELOG.md ├── Demos/ │ ├── ConsentsDemo/ │ │ ├── ConsentsDemo.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── Sources/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── MainViewController.swift │ ├── ConsentsObjC/ │ │ ├── ConsentsObjC.xcodeproj/ │ │ │ └── project.pbxproj │ │ └── Sources/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── ViewController.h │ │ ├── ViewController.m │ │ └── main.m │ ├── ConsentsSwiftUI/ │ │ ├── ConsentsSwiftUI.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── Sources/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── ConsentStatesView.swift │ │ ├── ConsentsDemoSwiftUIApp.swift │ │ ├── ContentView.swift │ │ ├── Info.plist │ │ └── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ └── Shared/ │ ├── Base.lproj/ │ │ └── Localizable.strings │ └── Settings.bundle/ │ ├── Root.plist │ └── en.lproj/ │ └── Root.strings ├── LICENSE ├── Package.swift ├── README.md ├── SmartlookConsentSDK.podspec ├── SmartlookConsentSDK.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata/ │ └── xcschemes/ │ └── SmartlookConsentSDK.xcscheme ├── SmartlookConsentSDK.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist └── Sources/ ├── Info.plist ├── SmartlookConsentSDK+Consent.swift ├── SmartlookConsentSDK+Notification.swift ├── SmartlookConsentSDK+ObjC.swift ├── SmartlookConsentSDK.h ├── SmartlookConsentSDK.swift └── UI/ ├── Cells/ │ ├── ButtonCell.swift │ ├── ButtonCellDelegateProtocol.swift │ ├── ConsentCell.swift │ ├── ConsentCellDelegateProtocol.swift │ ├── HeaderCell.swift │ └── TopBorderCell.swift ├── ControlPanel.storyboard └── ControlPanelViewController.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store SmartlookConsentSDK.xcworkspace/xcuserdata/* SmartlookConsentSDK.xcworkspace/xcuserdatad/* xcuserdata ================================================ FILE: .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented here. ## [1.6.0] - 2021-01-15 ### Added - Support for apps managed with SceneDelegate ## [1.3.0] - 2020-04-21 ### Fixed - Fixed issues with dialog not appearing in new apps due to `keyWindow` behaviour changes in latest iOS ## [1.2.0] - 2019-11-04 ### Fixed - Consent form sheet behaviour on iOS 13 - Dark mode design ## [1.1.0] - 2019-03-07 ### Added - Documentation and code examples in Demo Apps of `Settings.bundle` integration - When Consents settings are touched, a `SmartlookConsentSDK.consentsTouchedNotification` notification is posted ================================================ FILE: Demos/ConsentsDemo/ConsentsDemo.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 8FA873F725AC5515003CA69F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338579CF221AD646006861C1 /* AppDelegate.swift */; }; 8FA873F925AC5515003CA69F /* SmartlookConsentSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FAA366925A8618A00945E91 /* SmartlookConsentSDK.framework */; }; 8FA873FC25AC5515003CA69F /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8FB02CBF25AC51EF00EC75E8 /* Settings.bundle */; }; 8FA873FD25AC5515003CA69F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 338579D8221AD647006861C1 /* LaunchScreen.storyboard */; }; 8FA873FE25AC5515003CA69F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 338579D6221AD647006861C1 /* Assets.xcassets */; }; 8FA873FF25AC5515003CA69F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 338579D3221AD646006861C1 /* Main.storyboard */; }; 8FA8740225AC5515003CA69F /* SmartlookConsentSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8FAA366925A8618A00945E91 /* SmartlookConsentSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8FA8740A25AC5747003CA69F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8FA8740825AC5747003CA69F /* Localizable.strings */; }; 8FA8740E25AC58EB003CA69F /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338579D1221AD646006861C1 /* MainViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 8FA8740125AC5515003CA69F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 8FA8740225AC5515003CA69F /* SmartlookConsentSDK.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 338579CF221AD646006861C1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 338579D1221AD646006861C1 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 338579D4221AD646006861C1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 338579D6221AD647006861C1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 338579D9221AD647006861C1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 338579DB221AD647006861C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8FA8740625AC5515003CA69F /* ConsentsDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConsentsDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8FA8740925AC5747003CA69F /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = ../../Shared/Base.lproj/Localizable.strings; sourceTree = ""; }; 8FAA366925A8618A00945E91 /* SmartlookConsentSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SmartlookConsentSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8FB02CBF25AC51EF00EC75E8 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = ../Shared/Settings.bundle; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8FA873F825AC5515003CA69F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8FA873F925AC5515003CA69F /* SmartlookConsentSDK.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 338579C3221AD646006861C1 = { isa = PBXGroup; children = ( 8FB02CBF25AC51EF00EC75E8 /* Settings.bundle */, 338579CE221AD646006861C1 /* Sources */, 338579CD221AD646006861C1 /* Products */, 8FAA366825A8618A00945E91 /* Frameworks */, ); sourceTree = ""; }; 338579CD221AD646006861C1 /* Products */ = { isa = PBXGroup; children = ( 8FA8740625AC5515003CA69F /* ConsentsDemo.app */, ); name = Products; sourceTree = ""; }; 338579CE221AD646006861C1 /* Sources */ = { isa = PBXGroup; children = ( 338579CF221AD646006861C1 /* AppDelegate.swift */, 338579D1221AD646006861C1 /* MainViewController.swift */, 338579D3221AD646006861C1 /* Main.storyboard */, 338579D6221AD647006861C1 /* Assets.xcassets */, 338579D8221AD647006861C1 /* LaunchScreen.storyboard */, 338579DB221AD647006861C1 /* Info.plist */, 8FA8740825AC5747003CA69F /* Localizable.strings */, ); path = Sources; sourceTree = ""; }; 8FAA366825A8618A00945E91 /* Frameworks */ = { isa = PBXGroup; children = ( 8FAA366925A8618A00945E91 /* SmartlookConsentSDK.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8FA873F525AC5515003CA69F /* ConsentsDemo */ = { isa = PBXNativeTarget; buildConfigurationList = 8FA8740325AC5515003CA69F /* Build configuration list for PBXNativeTarget "ConsentsDemo" */; buildPhases = ( 8FA873F625AC5515003CA69F /* Sources */, 8FA873F825AC5515003CA69F /* Frameworks */, 8FA873FA25AC5515003CA69F /* Resources */, 8FA8740025AC5515003CA69F /* SwiftLint */, 8FA8740125AC5515003CA69F /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ConsentsDemo; productName = ConsentSDKDemo; productReference = 8FA8740625AC5515003CA69F /* ConsentsDemo.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 338579C4221AD646006861C1 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1010; LastUpgradeCheck = 1220; ORGANIZATIONNAME = Smartlook; }; buildConfigurationList = 338579C7221AD646006861C1 /* Build configuration list for PBXProject "ConsentsDemo" */; compatibilityVersion = "Xcode 10.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 338579C3221AD646006861C1; productRefGroup = 338579CD221AD646006861C1 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 8FA873F525AC5515003CA69F /* ConsentsDemo */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8FA873FA25AC5515003CA69F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8FA8740A25AC5747003CA69F /* Localizable.strings in Resources */, 8FA873FC25AC5515003CA69F /* Settings.bundle in Resources */, 8FA873FD25AC5515003CA69F /* LaunchScreen.storyboard in Resources */, 8FA873FE25AC5515003CA69F /* Assets.xcassets in Resources */, 8FA873FF25AC5515003CA69F /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 8FA8740025AC5515003CA69F /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = SwiftLint; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8FA873F625AC5515003CA69F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 8FA8740E25AC58EB003CA69F /* MainViewController.swift in Sources */, 8FA873F725AC5515003CA69F /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 338579D3221AD646006861C1 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 338579D4221AD646006861C1 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 338579D8221AD647006861C1 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 338579D9221AD647006861C1 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; 8FA8740825AC5747003CA69F /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( 8FA8740925AC5747003CA69F /* Base */, ); name = Localizable.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 338579DC221AD647006861C1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 338579DD221AD647006861C1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 8FA8740425AC5515003CA69F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 57MHDLMMHS; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.ConsentSDKDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 8FA8740525AC5515003CA69F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 57MHDLMMHS; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.ConsentSDKDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 338579C7221AD646006861C1 /* Build configuration list for PBXProject "ConsentsDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 338579DC221AD647006861C1 /* Debug */, 338579DD221AD647006861C1 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 8FA8740325AC5515003CA69F /* Build configuration list for PBXNativeTarget "ConsentsDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 8FA8740425AC5515003CA69F /* Debug */, 8FA8740525AC5515003CA69F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 338579C4221AD646006861C1 /* Project object */; } ================================================ FILE: Demos/ConsentsDemo/ConsentsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Demos/ConsentsDemo/ConsentsDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Demos/ConsentsDemo/Sources/AppDelegate.swift ================================================ // // AppDelegate.swift // ConsentSDKDemo // // Created by Pavel Kroh on 18/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit import SmartlookConsentSDK @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? // MARK: - Consent check /** This method is to be invoked any time the app starts to check if the consents are provided namely in: - `application:didFinishWithLaunchingOptions` for the fresh start - `application:WillEnterForeground` for app woken up from background (where possibly the consent setting could be changed in system settings) */ func checkConsentStates() { var consentsSettingsDefaults = SmartlookConsentSDK.ConsentsSettings() consentsSettingsDefaults.append((.privacy, .provided)) consentsSettingsDefaults.append((.analytics, .notProvided)) // adding a custom consent // consentsSettingsDefaults.append(("gdpr", .notProvided)) SmartlookConsentSDK.check(with: consentsSettingsDefaults) { if SmartlookConsentSDK.consentState(for: .analytics) == .provided { // start analytics tools // Smartlook.start(withKey: "1a2b3c4e5f60") } } } // MARK: - Application lifecycle func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { checkConsentStates() return true } func applicationWillEnterForeground(_ application: UIApplication) { checkConsentStates() } } ================================================ FILE: Demos/ConsentsDemo/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Demos/ConsentsDemo/Sources/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Demos/ConsentsDemo/Sources/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Demos/ConsentsDemo/Sources/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Demos/ConsentsDemo/Sources/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName Consents Demo CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Demos/ConsentsDemo/Sources/MainViewController.swift ================================================ // // ViewController.swift // ConsentSDKDemo // // Created by Pavel Kroh on 18/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit import SmartlookConsentSDK class MainViewController: UIViewController { // MARK: - UI Outlets @IBOutlet weak var consentPanelWasShown: UIView! @IBOutlet weak var privacyConsentIndicator: UIView! @IBOutlet weak var analyticsConsentIndicator: UIView! // MARK: - View lifecycle override func viewDidLoad() { super.viewDidLoad() privacyConsentIndicator.layer.cornerRadius = 3 analyticsConsentIndicator.layer.cornerRadius = 3 // This is not necessary if all the logic is handled in the `SmartlookConsentSDK.check` // or `SmartlookConsentSDK.show` callbacks may be usefully eg., // if some UI depends on the consents state like here. let notificationName = SmartlookConsentSDK.consentsTouchedNotification _ = NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: nil) { (_) in self.updateConsentIndicators() } if #available(iOS 13.0, *) { view.backgroundColor = UIColor.systemBackground } else { view.backgroundColor = UIColor.white } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.updateConsentIndicators() } // MARK: - Consent state visualization let consentColors: [SmartlookConsentSDK.ConsentState: UIColor] = [ .unknown: UIColor.lightGray, .provided: UIColor.green, .notProvided: UIColor.red ] func updateConsentIndicators() { consentPanelWasShown.backgroundColor = SmartlookConsentSDK.wasShown ? UIColor.green : UIColor.red privacyConsentIndicator.backgroundColor = consentColors[SmartlookConsentSDK.consentState(for: .privacy)] analyticsConsentIndicator.backgroundColor = consentColors[SmartlookConsentSDK.consentState(for: .analytics)] } // MARK: - UI Actions @IBAction func reviewConsents(_ sender: Any) { // let user review or check the consents SmartlookConsentSDK.show { self.updateConsentIndicators() if SmartlookConsentSDK.consentState(for: .analytics) != .provided { // stop analytics tools } } } @IBAction func showAppSettingsAction(_ sender: Any) { if let settingsUrl = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(settingsUrl) { UIApplication.shared.open(settingsUrl, completionHandler: nil) } } } ================================================ FILE: Demos/ConsentsObjC/ConsentsObjC.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 337E624F22173CAB00EBAB60 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 337E624E22173CAB00EBAB60 /* AppDelegate.m */; }; 337E625222173CAB00EBAB60 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 337E625122173CAB00EBAB60 /* ViewController.m */; }; 337E625522173CAB00EBAB60 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 337E625322173CAB00EBAB60 /* Main.storyboard */; }; 337E625722173CAC00EBAB60 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 337E625622173CAC00EBAB60 /* Assets.xcassets */; }; 337E625A22173CAC00EBAB60 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 337E625822173CAC00EBAB60 /* LaunchScreen.storyboard */; }; 337E625D22173CAC00EBAB60 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 337E625C22173CAC00EBAB60 /* main.m */; }; 8FA8740D25AC5801003CA69F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8FA8740B25AC5800003CA69F /* Localizable.strings */; }; 8FAA367025A8620B00945E91 /* SmartlookConsentSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FAA366F25A8620B00945E91 /* SmartlookConsentSDK.framework */; }; 8FAA367125A8620B00945E91 /* SmartlookConsentSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8FAA366F25A8620B00945E91 /* SmartlookConsentSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8FD4736225AC4EF0004ECA0A /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8FD4736125AC4EF0004ECA0A /* Settings.bundle */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 338579E8221AE468006861C1 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 8FAA367125A8620B00945E91 /* SmartlookConsentSDK.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 337E624A22173CAB00EBAB60 /* ConsentsObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConsentsObjC.app; sourceTree = BUILT_PRODUCTS_DIR; }; 337E624D22173CAB00EBAB60 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 337E624E22173CAB00EBAB60 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 337E625022173CAB00EBAB60 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 337E625122173CAB00EBAB60 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 337E625422173CAB00EBAB60 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 337E625622173CAC00EBAB60 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 337E625922173CAC00EBAB60 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 337E625B22173CAC00EBAB60 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 337E625C22173CAC00EBAB60 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 8FA8740C25AC5800003CA69F /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = ../../Shared/Base.lproj/Localizable.strings; sourceTree = ""; }; 8FAA366F25A8620B00945E91 /* SmartlookConsentSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SmartlookConsentSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8FD4736125AC4EF0004ECA0A /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = ../Shared/Settings.bundle; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 337E624722173CAB00EBAB60 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8FAA367025A8620B00945E91 /* SmartlookConsentSDK.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 337E624122173CAB00EBAB60 = { isa = PBXGroup; children = ( 8FD4736125AC4EF0004ECA0A /* Settings.bundle */, 337E624C22173CAB00EBAB60 /* Sources */, 337E624B22173CAB00EBAB60 /* Products */, 8FAA366E25A8620B00945E91 /* Frameworks */, ); sourceTree = ""; }; 337E624B22173CAB00EBAB60 /* Products */ = { isa = PBXGroup; children = ( 337E624A22173CAB00EBAB60 /* ConsentsObjC.app */, ); name = Products; sourceTree = ""; }; 337E624C22173CAB00EBAB60 /* Sources */ = { isa = PBXGroup; children = ( 337E624D22173CAB00EBAB60 /* AppDelegate.h */, 337E624E22173CAB00EBAB60 /* AppDelegate.m */, 337E625022173CAB00EBAB60 /* ViewController.h */, 337E625122173CAB00EBAB60 /* ViewController.m */, 337E625322173CAB00EBAB60 /* Main.storyboard */, 337E625622173CAC00EBAB60 /* Assets.xcassets */, 337E625822173CAC00EBAB60 /* LaunchScreen.storyboard */, 337E625B22173CAC00EBAB60 /* Info.plist */, 337E625C22173CAC00EBAB60 /* main.m */, 8FA8740B25AC5800003CA69F /* Localizable.strings */, ); path = Sources; sourceTree = ""; }; 8FAA366E25A8620B00945E91 /* Frameworks */ = { isa = PBXGroup; children = ( 8FAA366F25A8620B00945E91 /* SmartlookConsentSDK.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 337E624922173CAB00EBAB60 /* ConsentsObjC */ = { isa = PBXNativeTarget; buildConfigurationList = 337E626022173CAC00EBAB60 /* Build configuration list for PBXNativeTarget "ConsentsObjC" */; buildPhases = ( 337E624622173CAB00EBAB60 /* Sources */, 337E624722173CAB00EBAB60 /* Frameworks */, 337E624822173CAB00EBAB60 /* Resources */, 338579E8221AE468006861C1 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ConsentsObjC; productName = ConsentSDKDemoObjC; productReference = 337E624A22173CAB00EBAB60 /* ConsentsObjC.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 337E624222173CAB00EBAB60 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1230; ORGANIZATIONNAME = Smartlook; TargetAttributes = { 337E624922173CAB00EBAB60 = { CreatedOnToolsVersion = 10.1; LastSwiftMigration = 1010; }; }; }; buildConfigurationList = 337E624522173CAB00EBAB60 /* Build configuration list for PBXProject "ConsentsObjC" */; compatibilityVersion = "Xcode 10.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 337E624122173CAB00EBAB60; productRefGroup = 337E624B22173CAB00EBAB60 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 337E624922173CAB00EBAB60 /* ConsentsObjC */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 337E624822173CAB00EBAB60 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8FA8740D25AC5801003CA69F /* Localizable.strings in Resources */, 337E625A22173CAC00EBAB60 /* LaunchScreen.storyboard in Resources */, 8FD4736225AC4EF0004ECA0A /* Settings.bundle in Resources */, 337E625722173CAC00EBAB60 /* Assets.xcassets in Resources */, 337E625522173CAB00EBAB60 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 337E624622173CAB00EBAB60 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 337E625222173CAB00EBAB60 /* ViewController.m in Sources */, 337E625D22173CAC00EBAB60 /* main.m in Sources */, 337E624F22173CAB00EBAB60 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 337E625322173CAB00EBAB60 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 337E625422173CAB00EBAB60 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 337E625822173CAC00EBAB60 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 337E625922173CAC00EBAB60 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; 8FA8740B25AC5800003CA69F /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( 8FA8740C25AC5800003CA69F /* Base */, ); name = Localizable.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 337E625E22173CAC00EBAB60 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 337E625F22173CAC00EBAB60 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; 337E626122173CAC00EBAB60 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 57MHDLMMHS; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.ConsentSDKDemoObjC; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ConsentSDKDemoObjC-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 337E626222173CAC00EBAB60 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 57MHDLMMHS; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.ConsentSDKDemoObjC; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ConsentSDKDemoObjC-Bridging-Header.h"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 337E624522173CAB00EBAB60 /* Build configuration list for PBXProject "ConsentsObjC" */ = { isa = XCConfigurationList; buildConfigurations = ( 337E625E22173CAC00EBAB60 /* Debug */, 337E625F22173CAC00EBAB60 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 337E626022173CAC00EBAB60 /* Build configuration list for PBXNativeTarget "ConsentsObjC" */ = { isa = XCConfigurationList; buildConfigurations = ( 337E626122173CAC00EBAB60 /* Debug */, 337E626222173CAC00EBAB60 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 337E624222173CAB00EBAB60 /* Project object */; } ================================================ FILE: Demos/ConsentsObjC/Sources/AppDelegate.h ================================================ // // AppDelegate.h // ConsentSDKDemoObjC // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Demos/ConsentsObjC/Sources/AppDelegate.m ================================================ // // AppDelegate.m // ConsentSDKDemoObjC // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // #import "AppDelegate.h" #import @implementation AppDelegate // MARK: - Consent check /** This method is to be invoked any time the app starts to check if the consents are provided namely in: - `application:didFinishWithLaunchingOptions` for the fresh start - `application:WillEnterForeground` for app woken up from background (where possibly the consent setting could be changed in system settings) */ - (void)checkConsentStates { NSMutableArray *consentsSettingsDefaults = [NSMutableArray new]; [consentsSettingsDefaults addObjectsFromArray:@[SLCConsentPrivacy, @(SLCConsentStateProvided)]]; [consentsSettingsDefaults addObjectsFromArray:@[SLCConsentAnalytics, @(SLCConsentStateNotProvided)]]; [SmartlookConsentSDK checkWith:consentsSettingsDefaults callback:^{ if ([SmartlookConsentSDK consentStateFor:SLCConsentAnalytics] == SLCConsentStateProvided) { // start analytics tools }; }]; } // MARK: - Application lifecycle - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self checkConsentStates]; return YES; } - (void)applicationWillEnterForeground:(UIApplication *)application { [self checkConsentStates]; } @end ================================================ FILE: Demos/ConsentsObjC/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Demos/ConsentsObjC/Sources/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Demos/ConsentsObjC/Sources/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Demos/ConsentsObjC/Sources/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Demos/ConsentsObjC/Sources/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName Consents (ObjC) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Demos/ConsentsObjC/Sources/ViewController.h ================================================ // // ViewController.h // ConsentSDKDemoObjC // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // #import @interface ViewController : UIViewController @end ================================================ FILE: Demos/ConsentsObjC/Sources/ViewController.m ================================================ // // ViewController.m // ConsentSDKDemoObjC // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // #import "ViewController.h" #import @interface ViewController (); @property (weak, nonatomic) IBOutlet UIView *consentsPanelWasShownIndicator; @property (weak, nonatomic) IBOutlet UIView *privacyConsentIndicator; @property (weak, nonatomic) IBOutlet UIView *analyticsConsentIndicator; @end @implementation ViewController // MARK: - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; _privacyConsentIndicator.layer.cornerRadius = 3; _analyticsConsentIndicator.layer.cornerRadius = 3; // This is not necessary if all the logic is handled in the `SmartlookConsentSDK.check` // or `SmartlookConsentSDK.show` callbacks may be usefully eg., // if some UI depends on the consents state like here. [[NSNotificationCenter defaultCenter] addObserverForName:SLCConsentsTouchedNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { [self updateConsentIndicators]; }]; if (@available(iOS 13.0, *)) { self.view.backgroundColor = UIColor.systemBackgroundColor; } else { self.view.backgroundColor = UIColor.whiteColor; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self updateConsentIndicators]; } // MARK: - Consent state visualization - (UIColor *)colorForState:(SLCConsentState)state { switch (state) { case SLCConsentStateUnknown: return [UIColor lightGrayColor]; break; case SLCConsentStateNotProvided: return [UIColor redColor]; break; case SLCConsentStateProvided: return [UIColor greenColor]; break; } } -(void)updateConsentIndicators { self.consentsPanelWasShownIndicator.backgroundColor = SmartlookConsentSDK.wasShown ? [UIColor greenColor] : [UIColor grayColor]; self.privacyConsentIndicator.backgroundColor = [self colorForState:[SmartlookConsentSDK consentStateFor:SLCConsentPrivacy]]; self.analyticsConsentIndicator.backgroundColor = [self colorForState:[SmartlookConsentSDK consentStateFor:SLCConsentAnalytics]]; } // MARK: - UI Actions - (IBAction)buttonAction:(id)sender { // let user review or check the consents [SmartlookConsentSDK showWithCallback:^{ [self updateConsentIndicators]; if ([SmartlookConsentSDK consentStateFor:SLCConsentAnalytics] != SLCConsentStateProvided) { // stop analytics tools }; }]; } - (IBAction)showAppSettingsAction:(id)sender { NSURL *settingsUrl = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:settingsUrl]) { [[UIApplication sharedApplication] openURL:settingsUrl options:@{} completionHandler:nil]; } } @end ================================================ FILE: Demos/ConsentsObjC/Sources/main.m ================================================ // // main.m // ConsentSDKDemoObjC // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: Demos/ConsentsSwiftUI/ConsentsSwiftUI.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 8F24310D25AC5FCD00403182 /* ConsentStatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F24310C25AC5FCD00403182 /* ConsentStatesView.swift */; }; 8FA8741225AC5AE2003CA69F /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8FA8741125AC5AE2003CA69F /* Settings.bundle */; }; 8FA8741725AC5B04003CA69F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8FA8741525AC5B04003CA69F /* Localizable.strings */; }; 8FD01CC925AC92EE00FD6871 /* SmartlookConsentSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FD01CC825AC92EE00FD6871 /* SmartlookConsentSDK.framework */; }; 8FD01CCA25AC92EE00FD6871 /* SmartlookConsentSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8FD01CC825AC92EE00FD6871 /* SmartlookConsentSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8FE5C8BD25A88E460073DEBD /* ConsentsDemoSwiftUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE5C8BC25A88E460073DEBD /* ConsentsDemoSwiftUIApp.swift */; }; 8FE5C8BF25A88E460073DEBD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE5C8BE25A88E460073DEBD /* ContentView.swift */; }; 8FE5C8C125A88E480073DEBD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8FE5C8C025A88E480073DEBD /* Assets.xcassets */; }; 8FE5C8C425A88E480073DEBD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8FE5C8C325A88E480073DEBD /* Preview Assets.xcassets */; }; 8FE5C8D725A898C30073DEBD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE5C8D625A898C30073DEBD /* AppDelegate.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 8FA8741C25AC5B64003CA69F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 8FD01CCA25AC92EE00FD6871 /* SmartlookConsentSDK.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 8F24310C25AC5FCD00403182 /* ConsentStatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentStatesView.swift; sourceTree = ""; }; 8FA8741125AC5AE2003CA69F /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = ../Shared/Settings.bundle; sourceTree = ""; }; 8FA8741625AC5B04003CA69F /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = ../../Shared/Base.lproj/Localizable.strings; sourceTree = ""; }; 8FD01CC825AC92EE00FD6871 /* SmartlookConsentSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SmartlookConsentSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8FE5C8B925A88E460073DEBD /* ConsentsSwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConsentsSwiftUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8FE5C8BC25A88E460073DEBD /* ConsentsDemoSwiftUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentsDemoSwiftUIApp.swift; sourceTree = ""; }; 8FE5C8BE25A88E460073DEBD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 8FE5C8C025A88E480073DEBD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 8FE5C8C325A88E480073DEBD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 8FE5C8C525A88E480073DEBD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8FE5C8D625A898C30073DEBD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8FE5C8B625A88E460073DEBD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8FD01CC925AC92EE00FD6871 /* SmartlookConsentSDK.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 8FE5C8B025A88E460073DEBD = { isa = PBXGroup; children = ( 8FA8741125AC5AE2003CA69F /* Settings.bundle */, 8FE5C8BB25A88E460073DEBD /* Sources */, 8FE5C8BA25A88E460073DEBD /* Products */, 8FE5C8CF25A8938F0073DEBD /* Frameworks */, ); sourceTree = ""; }; 8FE5C8BA25A88E460073DEBD /* Products */ = { isa = PBXGroup; children = ( 8FE5C8B925A88E460073DEBD /* ConsentsSwiftUI.app */, ); name = Products; sourceTree = ""; }; 8FE5C8BB25A88E460073DEBD /* Sources */ = { isa = PBXGroup; children = ( 8FE5C8D625A898C30073DEBD /* AppDelegate.swift */, 8FE5C8BC25A88E460073DEBD /* ConsentsDemoSwiftUIApp.swift */, 8F24310C25AC5FCD00403182 /* ConsentStatesView.swift */, 8FE5C8BE25A88E460073DEBD /* ContentView.swift */, 8FE5C8C025A88E480073DEBD /* Assets.xcassets */, 8FE5C8C525A88E480073DEBD /* Info.plist */, 8FA8741525AC5B04003CA69F /* Localizable.strings */, 8FE5C8C225A88E480073DEBD /* Preview Content */, ); path = Sources; sourceTree = ""; }; 8FE5C8C225A88E480073DEBD /* Preview Content */ = { isa = PBXGroup; children = ( 8FE5C8C325A88E480073DEBD /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; 8FE5C8CF25A8938F0073DEBD /* Frameworks */ = { isa = PBXGroup; children = ( 8FD01CC825AC92EE00FD6871 /* SmartlookConsentSDK.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8FE5C8B825A88E460073DEBD /* ConsentsSwiftUI */ = { isa = PBXNativeTarget; buildConfigurationList = 8FE5C8C825A88E480073DEBD /* Build configuration list for PBXNativeTarget "ConsentsSwiftUI" */; buildPhases = ( 8FE5C8B525A88E460073DEBD /* Sources */, 8FE5C8B625A88E460073DEBD /* Frameworks */, 8FE5C8B725A88E460073DEBD /* Resources */, 8FE5C8D525A895560073DEBD /* SwiftLint */, 8FA8741C25AC5B64003CA69F /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ConsentsSwiftUI; productName = "Consents Demo SwiftUI"; productReference = 8FE5C8B925A88E460073DEBD /* ConsentsSwiftUI.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 8FE5C8B125A88E460073DEBD /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1230; LastUpgradeCheck = 1230; TargetAttributes = { 8FE5C8B825A88E460073DEBD = { CreatedOnToolsVersion = 12.3; }; }; }; buildConfigurationList = 8FE5C8B425A88E460073DEBD /* Build configuration list for PBXProject "ConsentsSwiftUI" */; compatibilityVersion = "Xcode 10.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 8FE5C8B025A88E460073DEBD; productRefGroup = 8FE5C8BA25A88E460073DEBD /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 8FE5C8B825A88E460073DEBD /* ConsentsSwiftUI */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8FE5C8B725A88E460073DEBD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8FA8741225AC5AE2003CA69F /* Settings.bundle in Resources */, 8FA8741725AC5B04003CA69F /* Localizable.strings in Resources */, 8FE5C8C425A88E480073DEBD /* Preview Assets.xcassets in Resources */, 8FE5C8C125A88E480073DEBD /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 8FE5C8D525A895560073DEBD /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = SwiftLint; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8FE5C8B525A88E460073DEBD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 8FE5C8D725A898C30073DEBD /* AppDelegate.swift in Sources */, 8FE5C8BF25A88E460073DEBD /* ContentView.swift in Sources */, 8FE5C8BD25A88E460073DEBD /* ConsentsDemoSwiftUIApp.swift in Sources */, 8F24310D25AC5FCD00403182 /* ConsentStatesView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 8FA8741525AC5B04003CA69F /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( 8FA8741625AC5B04003CA69F /* Base */, ); name = Localizable.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 8FE5C8C625A88E480073DEBD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 8FE5C8C725A88E480073DEBD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 8FE5C8C925A88E480073DEBD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\""; DEVELOPMENT_TEAM = L67NW37249; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.ConsentSDKDemoSwiftUI; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 8FE5C8CA25A88E480073DEBD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\""; DEVELOPMENT_TEAM = L67NW37249; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.ConsentSDKDemoSwiftUI; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 8FE5C8B425A88E460073DEBD /* Build configuration list for PBXProject "ConsentsSwiftUI" */ = { isa = XCConfigurationList; buildConfigurations = ( 8FE5C8C625A88E480073DEBD /* Debug */, 8FE5C8C725A88E480073DEBD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 8FE5C8C825A88E480073DEBD /* Build configuration list for PBXNativeTarget "ConsentsSwiftUI" */ = { isa = XCConfigurationList; buildConfigurations = ( 8FE5C8C925A88E480073DEBD /* Debug */, 8FE5C8CA25A88E480073DEBD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 8FE5C8B125A88E460073DEBD /* Project object */; } ================================================ FILE: Demos/ConsentsSwiftUI/ConsentsSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Demos/ConsentsSwiftUI/ConsentsSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Demos/ConsentsSwiftUI/Sources/AppDelegate.swift ================================================ // // AppDelegate.swift // Consents Demo SwiftUI // // Created by Václav Halík on 08.01.2021. // import UIKit import SmartlookConsentSDK class AppDelegate: NSObject, UIApplicationDelegate { // MARK: - Consent check /** This method is to be invoked any time the app starts to check if the consents are provided namely in: - `application:didFinishWithLaunchingOptions` for the fresh start - `application:WillEnterForeground` for app woken up from background (where possibly the consent setting could be changed in system settings) */ func checkConsentStates() { var consentsSettingsDefaults = SmartlookConsentSDK.ConsentsSettings() consentsSettingsDefaults.append((.privacy, .provided)) consentsSettingsDefaults.append((.analytics, .notProvided)) // adding a custom consent // consentsSettingsDefaults.append(("gdpr", .notProvided)) SmartlookConsentSDK.check(with: consentsSettingsDefaults) { if SmartlookConsentSDK.consentState(for: .analytics) == .provided { // start analytics tools // Smartlook.start(withKey: "1a2b3c4e5f60") } } } // MARK: - Application lifecycle func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { checkConsentStates() return true } func applicationWillEnterForeground(_ application: UIApplication) { checkConsentStates() } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/ConsentStatesView.swift ================================================ // // ConsentStatesView.swift // ConsentsSwiftUI // // Created by Václav Halík on 11.01.2021. // import SwiftUI import SmartlookConsentSDK struct ConsentStatesView: View { @State private var updateFlag: Bool = false // States colors private let consentColors: [SmartlookConsentSDK.ConsentState: Color] = [ .unknown: Color(.systemGray2), .provided: Color(.systemGreen), .notProvided: Color(.systemRed) ] // Consent colors private var panelWasShownColor: Color { SmartlookConsentSDK.wasShown ? .green : .red } private var privacyConsentColor: Color? { consentColors[SmartlookConsentSDK.consentState(for: .privacy)] } private var analyticsConsentColor: Color? { consentColors[SmartlookConsentSDK.consentState(for: .analytics)] } var body: some View { VStack(spacing: 6) { ConsentStateRowView(text: "Consents panel has been shown", color: panelWasShownColor) ConsentStateRowView(text: "Privacy consent", color: privacyConsentColor) ConsentStateRowView(text: "Analytics consent", color: analyticsConsentColor) // Force to update view if updateFlag { EmptyView() } } // This is not necessary if all the logic is handled in the `SmartlookConsentSDK.check` // or `SmartlookConsentSDK.show` callbacks may be usefully eg., // if some UI depends on the consents state like here. .onReceive(NotificationCenter.default.publisher( for: SmartlookConsentSDK.consentsTouchedNotification )) { _ in updateFlag.toggle() } } } struct ConsentStateRowView: View { var text: String var color: Color? var body: some View { HStack { Text(text) Spacer() Rectangle() .frame(width: 20, height: 20) .foregroundColor(color) .cornerRadius(4) } } } struct ConsentStatesView_Previews: PreviewProvider { static var previews: some View { ConsentStatesView() } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/ConsentsDemoSwiftUIApp.swift ================================================ // // ConsentsDemoSwiftUIApp.swift // Consents Demo SwiftUI // // Created by Václav Halík on 08.01.2021. // import SwiftUI @main struct ConsentsSwiftUIApp: App { // swiftlint:disable weak_delegate @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { ContentView() } } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/ContentView.swift ================================================ // // ContentView.swift // Consents Demo SwiftUI // // Created by Václav Halík on 08.01.2021. // import SwiftUI import SmartlookConsentSDK struct ContentView: View { var body: some View { VStack(alignment: .leading, spacing: 32) { HStack { Spacer() Text("Smartlook Consent Demo") .font(.title) Spacer() } ConsentStatesView() .frame(maxWidth: 350) Button("Review consents...") { reviewConsents() } Button("Show app settings...") { showAppSettingsAction() } Spacer() } .padding() } // MARK: - UI Actions func reviewConsents() { // let user review or check the consents SmartlookConsentSDK.show { if SmartlookConsentSDK.consentState(for: .analytics) != .provided { // stop analytics tools } } } func showAppSettingsAction() { if let settingsUrl = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(settingsUrl) { UIApplication.shared.open(settingsUrl, completionHandler: nil) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } ================================================ FILE: Demos/ConsentsSwiftUI/Sources/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName Consent (SwiftUI) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UIApplicationSupportsIndirectInputEvents UILaunchScreen UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Demos/ConsentsSwiftUI/Sources/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: Demos/Shared/Base.lproj/Localizable.strings ================================================ /* Localizable.strings ConsentSDKDemo Created by Pavel Kroh on 15/02/2019. Copyright © 2019 Smartlook. All rights reserved. */ // Title and into text "smartlook-consent-sdk-title" = "Consents for $APP"; //$APP is replaced by the app display name and the name is highlighted then "smartlook-consent-sdk-text" = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam sapien elit, consequat eget, tristique non, venenatis quis, ante.\n\nCurabitur vitae diam non enim vestibulum interdum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos."; //\n\nCurabitur sagittis hendrerit ante. Donec iaculis gravida nulla.\n\nClass aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. // Texts for privacy consent "smartlook-consent-sdk-privacy-consent" = "Privacy consent proin mattis lacinia justo. Duis ante orci, molestie vitae vehicula venenatis, tincidunt ac pede."; //Maecenas ipsum velit, consectetuer eu lobortis ut, dictum at dui. "smartlook-consent-sdk-privacy-consent-url" = "https://www.smartlook.com"; // Texts for analytics consent "smartlook-consent-sdk-analytics-consent" = "Analytics consent aliquam erat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Morbi scelerisque luctus velit. Nunc auctor. "; //"consent-sdk-analytics-consent-url" = "https://www.smartlook.com"; // The button "smartlook-consent-sdk-button" = "Confirm & continue"; ================================================ FILE: Demos/Shared/Settings.bundle/Root.plist ================================================ StringsTable Root PreferenceSpecifiers Type PSGroupSpecifier Title Group Type PSToggleSwitchSpecifier Title privacy-consent Key smartlook-consent-sdk-privacy-consent DefaultValue Type PSToggleSwitchSpecifier Title analytics-consent Key smartlook-consent-sdk-analytics-consent DefaultValue ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Smartlook 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. ================================================ FILE: Package.swift ================================================ // swift-tools-version:5.3 import PackageDescription let package = Package( name: "SmartlookConsentSDK", platforms: [ .iOS(.v10) ], products: [ .library( name: "SmartlookConsentSDK", targets: ["SmartlookConsentSDK"]) ], targets: [ .target( name: "SmartlookConsentSDK", path: "Sources", exclude: ["Info.plist"]) ] ) ================================================ FILE: README.md ================================================ # SmartlookConsentSDK for iOS Getting explicit user consent with gathering analytics data in an app, or with processing the user’s personal data, is an important part of establishing user trust and seamless user experience. It is also an obligation of an app developer stated in [App Store Guidelines](https://developer.apple.com/app-store/review/guidelines/) (2.5.14 and 5.1.2), necessary to fulfil to get your app approved for distribution. Per the guidelines, if an app uses a 3rd party tool for analytics, it is the sole responsibility of the app developer, not of the 3rd party tool provider, to verify that the analytics tool does not register events in the app without user explicit consent. Although implementing a simple dialog to obtain user consents and store them for further reference seems like a straightforward process — digging into it reveals (as it is usual with “simple tasks”) that many programming and design details should be respected. Since implementing the consent goes beyond core functionality and the intended design of the app, it is likely that this consent necessity will bring further annoyances, impact time and disrupt existing developing processes. So why not use or reuse some ready-made SDK? The SmartlookConsentSDK: - provides a configurable control panel where user can select their privacy options - stores the selected user preferences for the app - enables all texts to be fully localized - enables linking to privacy policies which may be provided by an external web page and presenting them without leaving the app SmartlookConsentSDK works well with both Swift and Objective-C apps.     ![iPad Screenshot](https://github.com/smartlook/ios-consent-sdk/raw/master/readme-media/SmartlookConsentSDKDemo2.gif)       ![iPhone SettingsScreenshot](https://github.com/smartlook/ios-consent-sdk/raw/master/readme-media/ConsentSDK-Settings.png) ## Code examples ### Simple example The most straightforward use that utilises default settings: ```swift SmartlookConsentSDK.check() { if SmartlookConsentSDK.consentState(for: .analytics) == .provided { // Start analytics tools } } ``` This method first checks if the user already provided/rejected consent for both `privacy` and `analytics`. If not, the privacy control panel is shown with build-in defaults (consent is provided for both). When user agrees, the callback is called to let you handle the user preferences. In the case user previously went though these settings, the control panel is not show and the callback is called straight away. ### Complex example In this example, only consent for analytics is sought for with the consent provided by default. ```swift var consentsSettingsDefaults = SmartlookConsentSDK.ConsentsSettings() // consentsSettingsDefaults.append((.privacy, .notProvided)) consentsSettingsDefaults.append((.analytics, .provided)) SmartlookConsentSDK.check(with: consentsSettingsDefaults) { if SmartlookConsentSDK.consentState(for: .analytics) == .provided { // Start analytics tools } } ``` ## Installation ### Swift Package Manager Integration into the application using [Swift Package Manager](https://swift.org/package-manager/) is fully supported. ### Direct framework embedding Framwork is ready for direct embedding in your app, e.g. the way it is embedded into the demo apps here. For detailed descriptions see this Apple Developer Portal document [Embedding Frameworks In An App](https://developer.apple.com/library/archive/technotes/tn2435/_index.html) ### Cocoapods (deprecated) Add cocoapod `SmartlookConsentSDK` into your Podfile. ## Adding SmartlookConsentSDK to app code Generally, you want `SmartlookConsentSDK.check()` be called at the very beginning of the app life-cycle. Where exactly, it depends on your app architecture. If you don't want integrate this SDK into the standard app Settings, then `viewDidLoad()` of the root view controller is enough. If you want more comprehensive solution, then see our demo apps `AppDelegate` and `ViewController` for an inpiration. ## Objective-C The SDK is fully compatible with `Objective-C`. For code examples see the respective demo app. ## API ### Consent Consent is identified by a string constant. For convenience, SDK provides a `String` alias with two predefined constants, `.privacy` a `.analytics` together with the respective text placeholders in demo app `Localizable.strings`. However, any string works, and the name convention used in `Localizable.strings` is obvious. ### ConsentState Is a standard enumaration and indicates whether user seen and provided consent to a policy. - `.unknown` state indicates that the user did not reviewed the policy - `.notProvided` state indicates that the user explicitely refused consent to the policy - `.provided` state indicates that the user explicitely provided consent to the policy ```swift @objc(SLCConsentState) public enum ConsentState: Int { case unknown = -2 case notProvided = -1 case provided = 1 } ``` ### SmartlookConsentSDK.check() Is the key method of the SDK. It comes in two versions: ```swift @objc public static func check(callback: @escaping RequestIdCallback) ``` a straigth one w/out consents configuration that can be used when both `.privacy` and `.analytics` policies consents are required with `.provided` as the default value. ```swift public static func check(with consentsSettings: ConsentsSettings, callback: @escaping RequestIdCallback) ``` a version with configuration that allows fine-tuning required consents (adding, removing, chaning order of or their default values) ### SmartlookConsentSDK.show() Two variants much like `check()`, it **always** opens the Control Panel for the user to review their current privacy settings. ### Content Touched Notification Whenever user closes the panel opened by a call to `check()` or `show()`, `SmartlookConsentSDK.consentsTouchedNotification` notification is broadcased (regardless whether the consents were changed or not). This provides an alternative way to the `callback` block to let the app know there might be a change in consents. In ObjC, the notification identifier is `SLCConsentsTouchedNotification`. ## Localisation The texts shown in the control panel are configured using the standard `Localizable.strings` mechanism. `Localizable.strings` are also used to provide an optional URL of a detailed policy information (thus the link is localised as well). The keys used in the `Localizable.strings` are listed in the table below, or you can simply reuse [the file in our demo app](https://github.com/smartlook/ios-consent-sdk/raw/master/Consents%20Demo/ConsentsDemo/Base.lproj/Localizable.strings) . Localization follows a name conventions. If a new consent type is added (on top of the predefined convenience types `.privacy` a `.analytics`), the respective keys must be added to localized files following the pattern ``` "smartlook-consent-sdk-*consent-key*-consent" = "My special consent..."; "smartlook-consent-sdk-*consent-key*-consent-url" = "https://www.my-company.com/consent-policy-details?lang=de"; //optional ``` where `*consent-key*` is simply the text string constant used to identify the policy. ## iOS Settings The Consents SDK can be straigthforwardly integrated into the iOS Settings app so the user can also review their consents there. To do this, some manual work is needed even if the SDK is integrated using Cocoapods. If you are not familiar with adding system settings panel into your app, have a look at [Implementing an iOS Settings Bundle](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/UserDefaults/Preferences/Preferences.html) or just copy the `Settings.bundle` from one of our Demo apps to start. The key for SDK integration here is using the same names for your settings items as you use for localised strings, i.e., in order to add the most basic consent settings to iOS settings: 1. Add `Settings.bundle` if it is not there 2. Into it, add a `Toggle Switch` for each consent. 3. To connect it with a consent, set the toggle identifier to the same string you use for it in `Localizable.strings`, e.g., `smartlook-consent-sdk-analytics-consent` 4. Do not forget that `Settings.bundle` localisation is separated from the app localisation (the respective `.strings` file is inside the `Settings.bundle`, in the demo apps it is named `Root.strings`) ================================================ FILE: SmartlookConsentSDK.podspec ================================================ # coding: utf-8 # # Be sure to run `pod lib lint ABCPod.podspec' to ensure this is a # valid spec before submitting. # # Any lines starting with a # are optional, but their use is encouraged # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| s.name = 'SmartlookConsentSDK' s.version = '1.6.0' s.summary = 'This SDK provides a configurable control panel where user selects her privacy options.' # This description is used to generate tags and improve search results. s.description = <<-DESC This SDK: - provides a configurable control panel where user selects her privacy options - stores the selected user preferences to be used in your app - works both with Swift and Objective-C apps - all texts are fully localizable - privacy policies may be provided by an external web page that is presented w/out leaving the app (see localisation.) Obtaining explicit user consent with gathering analytics data in an app, or with processing user’s personal data is important part of establishing user trust and seamless user experience. It is also an obligation of an app developer stated in App Store Guidelines (2.5.14 and 5.1.2), necessary to fulfil in order to get your app approved for distribution. If your app e.g. uses a 3rd party tool for analytics, it is the sole responsibility of you as the app developer, not of the 3rd party tool provider, that the analytics tool does not start registering events in the app without user explicit consent. Although implementing some dialog to obtain user consents and store them for further reference seems pretty straightforward, digging into it reveals (as usual with “simple tasks”) many programming and design details that must be implemented, which are not the core functionality of your app. DESC s.homepage = 'https://github.com/smartlook/ios-consent-sdk/' s.screenshots = ['https://sdk.smartlook.com/assets/SmartlookConsentSDKDemo2.gif', 'https://github.com/smartlook/ios-consent-sdk/raw/master/readme-media/ConsentSDK-Settings.png'] s.license = { :type => 'MIT', :file => 'LICENSE' } s.authors = { 'Pavel Kroh' => 'pavelkroh@smartlook.com', 'Václav Halík' => 'vaclav.halik@smartlook.com' } s.source = { :git => 'https://github.com/smartlook/ios-consent-sdk.git', :tag => s.version.to_s } s.documentation_url = 'https://github.com/smartlook/ios-consent-sdk/' s.ios.deployment_target = '10.0' s.swift_versions = ['5.1', '5.2', '5.3'] s.source_files = 'Sources/**/*.{h,m,c,swift}' s.resource_bundles = { 'SmartlookConsentSDK' => ['Sources/**/*.{storyboard,xib}'] } end ================================================ FILE: SmartlookConsentSDK.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 3313512D222E80FB004BE5AE /* SmartlookConsentSDK+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3313512C222E80FB004BE5AE /* SmartlookConsentSDK+Notification.swift */; }; 33C27E44221C07560026F090 /* SmartlookConsentSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C27E42221C07560026F090 /* SmartlookConsentSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33C27E53221C07D30026F090 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E4A221C07D20026F090 /* HeaderCell.swift */; }; 33C27E54221C07D30026F090 /* TopBorderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E4B221C07D30026F090 /* TopBorderCell.swift */; }; 33C27E55221C07D30026F090 /* ButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E4C221C07D30026F090 /* ButtonCell.swift */; }; 33C27E56221C07D30026F090 /* ControlPanel.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 33C27E4D221C07D30026F090 /* ControlPanel.storyboard */; }; 33C27E57221C07D30026F090 /* SmartlookConsentSDK+Consent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E4E221C07D30026F090 /* SmartlookConsentSDK+Consent.swift */; }; 33C27E58221C07D30026F090 /* ControlPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E4F221C07D30026F090 /* ControlPanelViewController.swift */; }; 33C27E59221C07D30026F090 /* SmartlookConsentSDK+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E50221C07D30026F090 /* SmartlookConsentSDK+ObjC.swift */; }; 33C27E5A221C07D30026F090 /* ConsentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E51221C07D30026F090 /* ConsentCell.swift */; }; 33C27E5B221C07D30026F090 /* SmartlookConsentSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33C27E52221C07D30026F090 /* SmartlookConsentSDK.swift */; }; 8F0CFF782576A085002CBC8B /* ConsentCellDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F0CFF772576A085002CBC8B /* ConsentCellDelegateProtocol.swift */; }; 8F114349257A34F000F41179 /* ButtonCellDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F114348257A34F000F41179 /* ButtonCellDelegateProtocol.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 3313512C222E80FB004BE5AE /* SmartlookConsentSDK+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SmartlookConsentSDK+Notification.swift"; sourceTree = ""; }; 33C27E3F221C07560026F090 /* SmartlookConsentSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SmartlookConsentSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 33C27E42221C07560026F090 /* SmartlookConsentSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SmartlookConsentSDK.h; sourceTree = ""; }; 33C27E43221C07560026F090 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33C27E4A221C07D20026F090 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = ""; }; 33C27E4B221C07D30026F090 /* TopBorderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopBorderCell.swift; sourceTree = ""; }; 33C27E4C221C07D30026F090 /* ButtonCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = ""; }; 33C27E4D221C07D30026F090 /* ControlPanel.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ControlPanel.storyboard; sourceTree = ""; }; 33C27E4E221C07D30026F090 /* SmartlookConsentSDK+Consent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SmartlookConsentSDK+Consent.swift"; sourceTree = ""; }; 33C27E4F221C07D30026F090 /* ControlPanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlPanelViewController.swift; sourceTree = ""; }; 33C27E50221C07D30026F090 /* SmartlookConsentSDK+ObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SmartlookConsentSDK+ObjC.swift"; sourceTree = ""; }; 33C27E51221C07D30026F090 /* ConsentCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentCell.swift; sourceTree = ""; }; 33C27E52221C07D30026F090 /* SmartlookConsentSDK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmartlookConsentSDK.swift; sourceTree = ""; }; 8F0CFF772576A085002CBC8B /* ConsentCellDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentCellDelegateProtocol.swift; sourceTree = ""; }; 8F114348257A34F000F41179 /* ButtonCellDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCellDelegateProtocol.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 33C27E3C221C07560026F090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 33C27E35221C07560026F090 = { isa = PBXGroup; children = ( 33C27E41221C07560026F090 /* Sources */, 33C27E40221C07560026F090 /* Products */, ); sourceTree = ""; }; 33C27E40221C07560026F090 /* Products */ = { isa = PBXGroup; children = ( 33C27E3F221C07560026F090 /* SmartlookConsentSDK.framework */, ); name = Products; sourceTree = ""; }; 33C27E41221C07560026F090 /* Sources */ = { isa = PBXGroup; children = ( 8F0CFF7E2576A55E002CBC8B /* UI */, 33C27E42221C07560026F090 /* SmartlookConsentSDK.h */, 33C27E52221C07D30026F090 /* SmartlookConsentSDK.swift */, 33C27E4E221C07D30026F090 /* SmartlookConsentSDK+Consent.swift */, 33C27E50221C07D30026F090 /* SmartlookConsentSDK+ObjC.swift */, 3313512C222E80FB004BE5AE /* SmartlookConsentSDK+Notification.swift */, 33C27E43221C07560026F090 /* Info.plist */, ); path = Sources; sourceTree = ""; }; 8F0CFF7E2576A55E002CBC8B /* UI */ = { isa = PBXGroup; children = ( 8FEA48B92576923A002B07EA /* Cells */, 33C27E4F221C07D30026F090 /* ControlPanelViewController.swift */, 33C27E4D221C07D30026F090 /* ControlPanel.storyboard */, ); path = UI; sourceTree = ""; }; 8FEA48B92576923A002B07EA /* Cells */ = { isa = PBXGroup; children = ( 33C27E4C221C07D30026F090 /* ButtonCell.swift */, 8F114348257A34F000F41179 /* ButtonCellDelegateProtocol.swift */, 33C27E51221C07D30026F090 /* ConsentCell.swift */, 8F0CFF772576A085002CBC8B /* ConsentCellDelegateProtocol.swift */, 33C27E4A221C07D20026F090 /* HeaderCell.swift */, 33C27E4B221C07D30026F090 /* TopBorderCell.swift */, ); path = Cells; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 33C27E3A221C07560026F090 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 33C27E44221C07560026F090 /* SmartlookConsentSDK.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 33C27E3E221C07560026F090 /* SmartlookConsentSDK */ = { isa = PBXNativeTarget; buildConfigurationList = 33C27E47221C07560026F090 /* Build configuration list for PBXNativeTarget "SmartlookConsentSDK" */; buildPhases = ( 3332D680237040CD00A1C2F0 /* Build Number Increase */, 33C27E3A221C07560026F090 /* Headers */, 33C27E3B221C07560026F090 /* Sources */, 33C27E3C221C07560026F090 /* Frameworks */, 33C27E3D221C07560026F090 /* Resources */, 8FEA48B525767DCD002B07EA /* SwiftLint */, ); buildRules = ( ); dependencies = ( ); name = SmartlookConsentSDK; productName = SmartlookConsentSDK; productReference = 33C27E3F221C07560026F090 /* SmartlookConsentSDK.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 33C27E36221C07560026F090 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1220; ORGANIZATIONNAME = Smartlook; TargetAttributes = { 33C27E3E221C07560026F090 = { CreatedOnToolsVersion = 10.1; LastSwiftMigration = 1220; }; }; }; buildConfigurationList = 33C27E39221C07560026F090 /* Build configuration list for PBXProject "SmartlookConsentSDK" */; compatibilityVersion = "Xcode 10.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 33C27E35221C07560026F090; productRefGroup = 33C27E40221C07560026F090 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 33C27E3E221C07560026F090 /* SmartlookConsentSDK */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 33C27E3D221C07560026F090 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 33C27E56221C07D30026F090 /* ControlPanel.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 3332D680237040CD00A1C2F0 /* Build Number Increase */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = "Build Number Increase"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "buildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$INFOPLIST_FILE\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n"; }; 8FEA48B525767DCD002B07EA /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = SwiftLint; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 33C27E3B221C07560026F090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 33C27E53221C07D30026F090 /* HeaderCell.swift in Sources */, 33C27E58221C07D30026F090 /* ControlPanelViewController.swift in Sources */, 33C27E59221C07D30026F090 /* SmartlookConsentSDK+ObjC.swift in Sources */, 33C27E57221C07D30026F090 /* SmartlookConsentSDK+Consent.swift in Sources */, 33C27E5B221C07D30026F090 /* SmartlookConsentSDK.swift in Sources */, 33C27E5A221C07D30026F090 /* ConsentCell.swift in Sources */, 33C27E54221C07D30026F090 /* TopBorderCell.swift in Sources */, 8F0CFF782576A085002CBC8B /* ConsentCellDelegateProtocol.swift in Sources */, 33C27E55221C07D30026F090 /* ButtonCell.swift in Sources */, 3313512D222E80FB004BE5AE /* SmartlookConsentSDK+Notification.swift in Sources */, 8F114349257A34F000F41179 /* ButtonCellDelegateProtocol.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 33C27E45221C07560026F090 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 33C27E46221C07560026F090 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 33C27E48221C07560026F090 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 57MHDLMMHS; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MARKETING_VERSION = 1.6.0; PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.SmartlookConsentSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 33C27E49221C07560026F090 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 57MHDLMMHS; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MARKETING_VERSION = 1.6.0; PRODUCT_BUNDLE_IDENTIFIER = com.smartlook.SmartlookConsentSDK; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 33C27E39221C07560026F090 /* Build configuration list for PBXProject "SmartlookConsentSDK" */ = { isa = XCConfigurationList; buildConfigurations = ( 33C27E45221C07560026F090 /* Debug */, 33C27E46221C07560026F090 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 33C27E47221C07560026F090 /* Build configuration list for PBXNativeTarget "SmartlookConsentSDK" */ = { isa = XCConfigurationList; buildConfigurations = ( 33C27E48221C07560026F090 /* Debug */, 33C27E49221C07560026F090 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 33C27E36221C07560026F090 /* Project object */; } ================================================ FILE: SmartlookConsentSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: SmartlookConsentSDK.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: SmartlookConsentSDK.xcodeproj/xcshareddata/xcschemes/SmartlookConsentSDK.xcscheme ================================================ ================================================ FILE: SmartlookConsentSDK.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: SmartlookConsentSDK.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Sources/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion 533 ================================================ FILE: Sources/SmartlookConsentSDK+Consent.swift ================================================ // // SmartlookConsentSDK+Consent.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 18/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import Foundation extension SmartlookConsentSDK { public typealias Consent = String } extension SmartlookConsentSDK.Consent { public static let privacy: SmartlookConsentSDK.Consent = "privacy" public static let analytics: SmartlookConsentSDK.Consent = "analytics" } extension SmartlookConsentSDK { /** Indicates whether user seen and provided consent to a policy. `.unknown` state indicates that the user did not reviewed the policy `.notProvided` state indicates that the user explicitly refused consent to the policy `.provided` state indicates that the user explicitly provided consent to the policy */ @objc(SLCConsentState) public enum ConsentState: Int { case unknown = -2 case notProvided = -1 case provided = 1 } static func key(for consent: Consent) -> String { return "\(SmartlookConsentSDK.keyPrefix)-\(consent)-consent" } static func set(state: ConsentState, for consent: Consent) { switch state { case .unknown: UserDefaults.standard.removeObject(forKey: key(for: consent)) case .provided: UserDefaults.standard.set(true, forKey: key(for: consent)) case .notProvided: UserDefaults.standard.set(false, forKey: key(for: consent)) } } /** The current consent state. */ @objc public static func consentState(for consent: Consent) -> ConsentState { guard let value = UserDefaults.standard.object(forKey: key(for: consent)) as? Bool else { return .unknown } return value ? .provided: .notProvided } static func label(for consent: Consent) -> String { return NSLocalizedString(key(for: consent), comment: "") } static func detailUrl(for consent: Consent) -> URL? { let urlKey = "\(key(for: consent))-url" let consentUrlString = NSLocalizedString(urlKey, comment: "") guard consentUrlString != urlKey else { return nil } return URL(string: consentUrlString) } } ================================================ FILE: Sources/SmartlookConsentSDK+Notification.swift ================================================ // // SmartlookConsentSDK+Notification.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 05/03/2019. // Copyright © 2019 Smartlook. All rights reserved. // import Foundation extension SmartlookConsentSDK { private static var touchedName = "com.smartlook.SmartlookConsentSDK.notification.consentsTouched" /** This notification is sent whenever user touches the consents, regardless there is an actual change in the consents or not. */ public static let consentsTouchedNotification = Notification.Name(rawValue: touchedName) } ================================================ FILE: Sources/SmartlookConsentSDK+ObjC.swift ================================================ // // SmartlookConsentSDK+ObjC.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 18/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import Foundation /** This array must be filled alternatively by values of `NSString` (preferably a constant defined in `SLCConsent` class) and the respective default value for `SLCConsentState`: [NSString, @(SLCConsentState), NSString, @(SLCConsentState)...] */ public typealias SLCConsentsSettings = [Any] extension SmartlookConsentSDK { static func settings(from objcSettings: SLCConsentsSettings) -> ConsentsSettings { var swiftConsents = ConsentsSettings() for index in stride(from: 0, to: objcSettings.count - 1, by: 2) { if let consent = objcSettings[index] as? Consent, let rawValue = objcSettings[index + 1] as? Int, let state = ConsentState(rawValue: rawValue) { swiftConsents.append((consent, state)) } } return swiftConsents } @objc public static func show(with consentsSettings: SLCConsentsSettings, callback: @escaping RequestIdCallback) { show(with: settings(from: consentsSettings), callback: callback) } @objc public static func check(with consentsSettings: SLCConsentsSettings, callback: @escaping RequestIdCallback) { check(with: settings(from: consentsSettings), callback: callback) } } ================================================ FILE: Sources/SmartlookConsentSDK.h ================================================ // // SmartlookConsentSDK.h // SmartlookConsentSDK // // Created by Pavel Kroh on 19/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // #import #define SLCConsentPrivacy @"privacy" #define SLCConsentAnalytics @"analytics" #define SLCConsentsTouchedNotification @"com.smartlook.SmartlookConsentSDK.notification.consentsTouched" ================================================ FILE: Sources/SmartlookConsentSDK.swift ================================================ // // SmartlookConsentSDK.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 12/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import Foundation import UIKit @objc public class SmartlookConsentSDK: NSObject, SLCViewControllerDelegate { private static let shared = SmartlookConsentSDK() private static let defaultConsentsSettings: ConsentsSettings = [(.privacy, .provided), (.analytics, .provided)] static let keyPrefix = "smartlook-consent-sdk" static let bundleName = "SmartlookConsentSDK" // MARK: - Private private var originalKeyWindow: UIWindow? private var keyWindow: UIWindow? private var callback: RequestIdCallback? // MARK: - Presenting Control Panel public typealias RequestIdCallback = () -> Void /** An array used to configure what consents with which default state appear in the control panel. If user already changed set its own state to a particular consent, her choice is respected and the default state is not used. */ public typealias ConsentsSettings = [(consent: SmartlookConsentSDK.Consent, defaultState: SmartlookConsentSDK.ConsentState)] // MARK: - Show control panel /** Indicates whether the control panel was already shown at least once. */ @objc public static var wasShown: Bool { return UserDefaults.standard.bool(forKey: "\(keyPrefix)-shown") } /** This method always opens the Control Panel for user to review the current consents states and calls the callback once the user confirm her choice in with the button there. */ @objc public static func show(callback: @escaping RequestIdCallback) { show(with: defaultConsentsSettings, callback: callback) } /** This method always opens the Control Panel for user to review the current consents states and calls the callback once the user confirm her choice in with the button there. */ public static func show(with consentsSettings: ConsentsSettings, callback: @escaping RequestIdCallback) { guard shared.keyWindow == nil else { return } var originalWindow: UIWindow? for window in UIApplication.shared.windows where window.isKeyWindow { originalWindow = window break } if let originalWindow = originalWindow { shared.originalKeyWindow = originalWindow shared.callback = callback if #available(iOS 13.0, *) { if let windowScene = originalWindow.windowScene { shared.keyWindow = UIWindow(windowScene: windowScene) } } else { shared.keyWindow = UIWindow() } if let viewController = shared.viewController { shared.keyWindow?.windowLevel += 1 shared.keyWindow?.accessibilityViewIsModal = true shared.keyWindow?.rootViewController = UIViewController() shared.keyWindow?.backgroundColor = UIColor.clear viewController.consents = consentsSettings DispatchQueue.main.async { shared.keyWindow?.makeKeyAndVisible() shared.keyWindow?.rootViewController?.present(viewController, animated: true) { UserDefaults.standard.set(true, forKey: "\(keyPrefix)-shown") } } } } else { DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { show(with: consentsSettings, callback: callback) } } } // MARK: - Check and show control panel /** This method checks if the user reviewed the default (i.e., `privacy` and `analytics` policies). If not, it opens the Control Panel and calls the callback once the user confirm her choice in with the button there. If the consents were already reviewed by the user, callback is called immediately. */ @objc public static func check(callback: @escaping RequestIdCallback) { check(with: defaultConsentsSettings, callback: callback) } /** This method checks if the user reviewed the privacy policies enumerated in `ConsentsSettings` array. If not, it opens the Control Panel and calls the callback once the user confirm her choice in with the button there. If the consents were already reviewed by the user, callback is called immediately. */ public static func check(with consentsSettings: ConsentsSettings, callback: @escaping RequestIdCallback) { var consentsHaveBeenSet = true consentsSettings.forEach { (key: SmartlookConsentSDK.Consent, _: SmartlookConsentSDK.ConsentState) in consentsHaveBeenSet = consentsHaveBeenSet && consentState(for: key) != .unknown } guard consentsHaveBeenSet else { show(with: consentsSettings, callback: callback) return } NotificationCenter.default.post(name: SmartlookConsentSDK.consentsTouchedNotification, object: nil) callback() } // MARK: - Control panel instantiate private var controlPanel: ControlPanelViewController? private func instantiateControlPanel() -> ControlPanelViewController? { #if SWIFT_PACKAGE // Framework is used as Swift Package let storyboard = UIStoryboard(name: "ControlPanel", bundle: Bundle.module) return storyboard.instantiateInitialViewController() as? ControlPanelViewController #else // Framework is imported via legacy methods let thisBundle = Bundle(for: type(of: self)) if thisBundle.bundleIdentifier == "com.smartlook.SmartlookConsentSDK" { let storyboard = UIStoryboard(name: "ControlPanel", bundle: thisBundle) return storyboard.instantiateInitialViewController() as? ControlPanelViewController } else if let cocoapodResourcesBundleUrl = thisBundle.url(forResource: Self.bundleName, withExtension: "bundle"), let cocoapodResourcesBundle = Bundle(url: cocoapodResourcesBundleUrl) { let storyboard = UIStoryboard(name: "ControlPanel", bundle: cocoapodResourcesBundle) return storyboard.instantiateInitialViewController() as? ControlPanelViewController } return nil #endif } // MARK: - View Controller Implementation var viewController: ControlPanelViewController? { get { if controlPanel != nil { return controlPanel } controlPanel = instantiateControlPanel() guard let viewController = controlPanel else { return nil } // For tablet modes presents control panel as form viewController.delegate = self viewController.modalTransitionStyle = .coverVertical // This ensures, together with the viewController.supportedInterfaceOrientations = .portrait // that the controller is never in landscape. viewController.modalPresentationStyle = .formSheet // On phone presents control panel as full screen let phoneTraitCollection = UITraitCollection(userInterfaceIdiom: .phone) if let currentRootViewController = originalKeyWindow?.rootViewController, currentRootViewController.traitCollection.containsTraits(in: phoneTraitCollection) { viewController.modalPresentationStyle = .fullScreen } if #available(iOS 13.0, *) { // disable 'swipe to dismiss' viewController.isModalInPresentation = true } return viewController } set { controlPanel = newValue } } func viewControllerRequestClose(_ viewController: ControlPanelViewController) { DispatchQueue.main.async { viewController.dismiss(animated: true) { [weak self] in self?.originalKeyWindow?.makeKeyAndVisible() self?.originalKeyWindow = nil self?.keyWindow = nil self?.viewController = nil NotificationCenter.default.post(name: SmartlookConsentSDK.consentsTouchedNotification, object: nil) self?.callback?() self?.callback = nil } } } } ================================================ FILE: Sources/UI/Cells/ButtonCell.swift ================================================ // // ButtonCell.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit class ButtonCell: TopBorderCell { // MARK: - UI Outlets @IBOutlet weak var button: UIButton! // MARK: - Public public weak var delegate: ButtonCellDelegate? // MARK: - UI Actions @IBAction func detailButtonAction(_ sender: Any) { if let button = sender as? UIButton { delegate?.buttonCellPressed(button: button) } } // MARK: - View lifecycle override func awakeFromNib() { super.awakeFromNib() button.layer.cornerRadius = 6 button.setTitleColor(.white, for: .normal) let buttonTitle = NSLocalizedString("\(SmartlookConsentSDK.keyPrefix)-button", comment: "") button.setTitle(buttonTitle, for: .normal) } override func layoutSubviews() { super.layoutSubviews() button.backgroundColor = tintColor } } ================================================ FILE: Sources/UI/Cells/ButtonCellDelegateProtocol.swift ================================================ // // ButtonCellDelegateProtocol.swift // SmartlookConsentSDK // // Created by Václav Halík on 04.12.2020. // Copyright © 2020 Smartlook. All rights reserved. // import UIKit protocol ButtonCellDelegate: AnyObject { func buttonCellPressed(button: UIButton) } ================================================ FILE: Sources/UI/Cells/ConsentCell.swift ================================================ // // ConsentCell.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit class ConsentCell: TopBorderCell { // MARK: - UI Outlets @IBOutlet weak var consentSwitch: UISwitch! @IBOutlet weak var label: UILabel! @IBOutlet weak var detailView: UIView! // MARK: - Private private var consentUrl: URL? // MARK: - Public public weak var delegate: ConsentCellDelegate? private(set) var consent: SmartlookConsentSDK.Consent? func setupCell(_ consent: SmartlookConsentSDK.Consent, _ defaultState: SmartlookConsentSDK.ConsentState) { self.consent = consent if SmartlookConsentSDK.consentState(for: consent) == .unknown { // default default state is .provided SmartlookConsentSDK.set(state: defaultState == .unknown ? .provided : defaultState, for: consent) } consentSwitch.isOn = SmartlookConsentSDK.consentState(for: consent) == .provided label.text = SmartlookConsentSDK.label(for: consent) consentUrl = SmartlookConsentSDK.detailUrl(for: consent) detailView.isHidden = consentUrl == nil } // MARK: - UI Actions // the state is changed immediately @IBAction func switchAction(_ sender: Any) { if let consent = consent { SmartlookConsentSDK.set(state: consentSwitch.isOn ? .provided : .notProvided, for: consent) consentSwitch.isOn = SmartlookConsentSDK.consentState(for: consent) != .notProvided } } @IBAction func detailButtonAction(_ sender: Any) { delegate?.consentCellDetailButtonPressed(cell: self) } // MARK: - View lifecycle override func prepareForReuse() { consentUrl = nil } } ================================================ FILE: Sources/UI/Cells/ConsentCellDelegateProtocol.swift ================================================ // // ConsentCellDelegateProtocol.swift // SmartlookConsentSDK // // Created by Václav Halík on 01.12.2020. // Copyright © 2020 Smartlook. All rights reserved. // protocol ConsentCellDelegate: AnyObject { func consentCellDetailButtonPressed(cell: ConsentCell) } ================================================ FILE: Sources/UI/Cells/HeaderCell.swift ================================================ // // HeaderCell.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit class HeaderCell: UITableViewCell { // MARK: - UI Outlets @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var infoLabel: UILabel! // MARK: - View lifecycle override func awakeFromNib() { super.awakeFromNib() fillContent() } override func layoutSubviews() { super.layoutSubviews() fillContent() } // MARK: - Content private func fillContent() { let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? "" var titleText = NSLocalizedString("\(SmartlookConsentSDK.keyPrefix)-title", comment: "") titleText = titleText.replacingOccurrences(of: "$APP", with: appName) let titleStyles = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .title1)] let attributedString = NSMutableAttributedString(string: titleText, attributes: titleStyles) if let appNameRange = titleText.range(of: appName), !appNameRange.isEmpty { let appNameColor = tintColor ?? UIColor(red: 0, green: 0.48, blue: 1, alpha: 1) let appNameAttributes = [NSAttributedString.Key.foregroundColor: appNameColor] attributedString.setAttributes(appNameAttributes, range: NSRange(appNameRange, in: titleText)) } titleLabel.attributedText = attributedString infoLabel.text = NSLocalizedString("\(SmartlookConsentSDK.keyPrefix)-text", comment: "") } } ================================================ FILE: Sources/UI/Cells/TopBorderCell.swift ================================================ // // TopBorderCell.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 15/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit class TopBorderCell: UITableViewCell { // MARK: - View lifecycle override func awakeFromNib() { super.awakeFromNib() let borderView = UIView() borderView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(borderView) var separatorColor: UIColor = .lightGray if #available(iOS 13.0, *) { separatorColor = .separator } borderView.backgroundColor = separatorColor borderView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true borderView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true borderView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true borderView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true } } ================================================ FILE: Sources/UI/ControlPanel.storyboard ================================================ ================================================ FILE: Sources/UI/ControlPanelViewController.swift ================================================ // // ControlPanelViewController.swift // SmartlookConsentSDK // // Created by Pavel Kroh on 13/02/2019. // Copyright © 2019 Smartlook. All rights reserved. // import UIKit import SafariServices protocol SLCViewControllerDelegate: AnyObject { func viewControllerRequestClose(_ viewController: ControlPanelViewController) } class ControlPanelViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // MARK: - UI Outlets @IBOutlet weak var tableView: UITableView! // MARK: - Public public weak var delegate: SLCViewControllerDelegate? public var consents = SmartlookConsentSDK.ConsentsSettings() // MARK: - View lifecycle override func viewDidLoad() { super.viewDidLoad() if #available(iOS 13.0, *) { view.backgroundColor = UIColor.systemBackground } } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } // MARK: - UITableViewDataSource methods func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return consents.count + 2 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch indexPath.item { case 0: return tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) case consents.count + 1: if let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonCell", for: indexPath) as? ButtonCell { cell.delegate = self return cell } default: if let cell = tableView.dequeueReusableCell(withIdentifier: "ConsentCell", for: indexPath) as? ConsentCell { cell.delegate = self let consentDef = consents[indexPath.item - 1] cell.setupCell(consentDef.consent, consentDef.defaultState) return cell } } return UITableViewCell() } // MARK: - UITableViewDelegate methods func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { return indexPath.item == consents.count + 1 } } extension ControlPanelViewController: ConsentCellDelegate { // MARK: - ConsentCellDelegate methods func consentCellDetailButtonPressed(cell: ConsentCell) { guard let consent = cell.consent, let url = SmartlookConsentSDK.detailUrl(for: consent) else { return } var safariController: SFSafariViewController? if #available(iOS 11, *) { let config = SFSafariViewController.Configuration() config.barCollapsingEnabled = false safariController = SFSafariViewController(url: url, configuration: config) safariController?.dismissButtonStyle = .done } else { safariController = SFSafariViewController(url: url) } safariController?.modalPresentationStyle = .formSheet safariController?.modalTransitionStyle = .crossDissolve safariController?.preferredControlTintColor = self.view.tintColor if let safariController = safariController { present(safariController, animated: true, completion: nil) } } } extension ControlPanelViewController: ButtonCellDelegate { // MARK: - ButtonCellDelegate methods func buttonCellPressed(button: UIButton) { delegate?.viewControllerRequestClose(self) } }