├── Consent String SDK Swift.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Consent String SDK Swift ├── ConsentString.swift ├── ConsentStringError.swift ├── ConsentStringProtocol.swift ├── Consent_String_SDK_Swift.h ├── Data.swift ├── Info.plist └── String.swift ├── Consent String SDK SwiftTests ├── Consent_String_SDK_SwiftTests.swift └── Info.plist ├── LICENSE └── README.md /Consent String SDK Swift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F142B52A2093A1C5001ABE60 /* Consent_String_SDK_Swift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F142B5202093A1C5001ABE60 /* Consent_String_SDK_Swift.framework */; }; 11 | F142B52F2093A1C5001ABE60 /* Consent_String_SDK_SwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B52E2093A1C5001ABE60 /* Consent_String_SDK_SwiftTests.swift */; }; 12 | F142B5312093A1C5001ABE60 /* Consent_String_SDK_Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = F142B5232093A1C5001ABE60 /* Consent_String_SDK_Swift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | F142B53F2093A207001ABE60 /* ConsentString.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53A2093A206001ABE60 /* ConsentString.swift */; }; 14 | F142B5402093A207001ABE60 /* ConsentString.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53A2093A206001ABE60 /* ConsentString.swift */; }; 15 | F142B5412093A207001ABE60 /* ConsentStringError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53B2093A206001ABE60 /* ConsentStringError.swift */; }; 16 | F142B5422093A207001ABE60 /* ConsentStringError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53B2093A206001ABE60 /* ConsentStringError.swift */; }; 17 | F142B5432093A207001ABE60 /* ConsentStringProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53C2093A206001ABE60 /* ConsentStringProtocol.swift */; }; 18 | F142B5442093A207001ABE60 /* ConsentStringProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53C2093A206001ABE60 /* ConsentStringProtocol.swift */; }; 19 | F142B5452093A207001ABE60 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53D2093A207001ABE60 /* String.swift */; }; 20 | F142B5462093A207001ABE60 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53D2093A207001ABE60 /* String.swift */; }; 21 | F142B5472093A207001ABE60 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53E2093A207001ABE60 /* Data.swift */; }; 22 | F142B5482093A207001ABE60 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = F142B53E2093A207001ABE60 /* Data.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | F142B52B2093A1C5001ABE60 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = F142B5172093A1C5001ABE60 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = F142B51F2093A1C5001ABE60; 31 | remoteInfo = "Consent String SDK Swift"; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | F142B5202093A1C5001ABE60 /* Consent_String_SDK_Swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Consent_String_SDK_Swift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | F142B5232093A1C5001ABE60 /* Consent_String_SDK_Swift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Consent_String_SDK_Swift.h; sourceTree = ""; }; 38 | F142B5242093A1C5001ABE60 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | F142B5292093A1C5001ABE60 /* Consent String SDK SwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Consent String SDK SwiftTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | F142B52E2093A1C5001ABE60 /* Consent_String_SDK_SwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consent_String_SDK_SwiftTests.swift; sourceTree = ""; }; 41 | F142B5302093A1C5001ABE60 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | F142B53A2093A206001ABE60 /* ConsentString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentString.swift; sourceTree = ""; }; 43 | F142B53B2093A206001ABE60 /* ConsentStringError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentStringError.swift; sourceTree = ""; }; 44 | F142B53C2093A206001ABE60 /* ConsentStringProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentStringProtocol.swift; sourceTree = ""; }; 45 | F142B53D2093A207001ABE60 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 46 | F142B53E2093A207001ABE60 /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | F142B51C2093A1C5001ABE60 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | F142B5262093A1C5001ABE60 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | F142B52A2093A1C5001ABE60 /* Consent_String_SDK_Swift.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | F142B5162093A1C5001ABE60 = { 69 | isa = PBXGroup; 70 | children = ( 71 | F142B5222093A1C5001ABE60 /* Consent String SDK Swift */, 72 | F142B52D2093A1C5001ABE60 /* Consent String SDK SwiftTests */, 73 | F142B5212093A1C5001ABE60 /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | F142B5212093A1C5001ABE60 /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | F142B5202093A1C5001ABE60 /* Consent_String_SDK_Swift.framework */, 81 | F142B5292093A1C5001ABE60 /* Consent String SDK SwiftTests.xctest */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | F142B5222093A1C5001ABE60 /* Consent String SDK Swift */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | F142B53C2093A206001ABE60 /* ConsentStringProtocol.swift */, 90 | F142B53A2093A206001ABE60 /* ConsentString.swift */, 91 | F142B53B2093A206001ABE60 /* ConsentStringError.swift */, 92 | F142B53E2093A207001ABE60 /* Data.swift */, 93 | F142B53D2093A207001ABE60 /* String.swift */, 94 | F142B5232093A1C5001ABE60 /* Consent_String_SDK_Swift.h */, 95 | F142B5242093A1C5001ABE60 /* Info.plist */, 96 | ); 97 | path = "Consent String SDK Swift"; 98 | sourceTree = ""; 99 | }; 100 | F142B52D2093A1C5001ABE60 /* Consent String SDK SwiftTests */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | F142B52E2093A1C5001ABE60 /* Consent_String_SDK_SwiftTests.swift */, 104 | F142B5302093A1C5001ABE60 /* Info.plist */, 105 | ); 106 | path = "Consent String SDK SwiftTests"; 107 | sourceTree = ""; 108 | }; 109 | /* End PBXGroup section */ 110 | 111 | /* Begin PBXHeadersBuildPhase section */ 112 | F142B51D2093A1C5001ABE60 /* Headers */ = { 113 | isa = PBXHeadersBuildPhase; 114 | buildActionMask = 2147483647; 115 | files = ( 116 | F142B5312093A1C5001ABE60 /* Consent_String_SDK_Swift.h in Headers */, 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | /* End PBXHeadersBuildPhase section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | F142B51F2093A1C5001ABE60 /* Consent String SDK Swift */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = F142B5342093A1C5001ABE60 /* Build configuration list for PBXNativeTarget "Consent String SDK Swift" */; 126 | buildPhases = ( 127 | F142B51B2093A1C5001ABE60 /* Sources */, 128 | F142B51C2093A1C5001ABE60 /* Frameworks */, 129 | F142B51D2093A1C5001ABE60 /* Headers */, 130 | F142B51E2093A1C5001ABE60 /* Resources */, 131 | ); 132 | buildRules = ( 133 | ); 134 | dependencies = ( 135 | ); 136 | name = "Consent String SDK Swift"; 137 | productName = "Consent String SDK Swift"; 138 | productReference = F142B5202093A1C5001ABE60 /* Consent_String_SDK_Swift.framework */; 139 | productType = "com.apple.product-type.framework"; 140 | }; 141 | F142B5282093A1C5001ABE60 /* Consent String SDK SwiftTests */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = F142B5372093A1C5001ABE60 /* Build configuration list for PBXNativeTarget "Consent String SDK SwiftTests" */; 144 | buildPhases = ( 145 | F142B5252093A1C5001ABE60 /* Sources */, 146 | F142B5262093A1C5001ABE60 /* Frameworks */, 147 | F142B5272093A1C5001ABE60 /* Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | F142B52C2093A1C5001ABE60 /* PBXTargetDependency */, 153 | ); 154 | name = "Consent String SDK SwiftTests"; 155 | productName = "Consent String SDK SwiftTests"; 156 | productReference = F142B5292093A1C5001ABE60 /* Consent String SDK SwiftTests.xctest */; 157 | productType = "com.apple.product-type.bundle.unit-test"; 158 | }; 159 | /* End PBXNativeTarget section */ 160 | 161 | /* Begin PBXProject section */ 162 | F142B5172093A1C5001ABE60 /* Project object */ = { 163 | isa = PBXProject; 164 | attributes = { 165 | LastSwiftUpdateCheck = 0930; 166 | LastUpgradeCheck = 0930; 167 | ORGANIZATIONNAME = "Interactive Advertising Bureau"; 168 | TargetAttributes = { 169 | F142B51F2093A1C5001ABE60 = { 170 | CreatedOnToolsVersion = 9.3; 171 | LastSwiftMigration = 0930; 172 | }; 173 | F142B5282093A1C5001ABE60 = { 174 | CreatedOnToolsVersion = 9.3; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = F142B51A2093A1C5001ABE60 /* Build configuration list for PBXProject "Consent String SDK Swift" */; 179 | compatibilityVersion = "Xcode 9.3"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | ); 185 | mainGroup = F142B5162093A1C5001ABE60; 186 | productRefGroup = F142B5212093A1C5001ABE60 /* Products */; 187 | projectDirPath = ""; 188 | projectRoot = ""; 189 | targets = ( 190 | F142B51F2093A1C5001ABE60 /* Consent String SDK Swift */, 191 | F142B5282093A1C5001ABE60 /* Consent String SDK SwiftTests */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | F142B51E2093A1C5001ABE60 /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | F142B5272093A1C5001ABE60 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | F142B51B2093A1C5001ABE60 /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | F142B5432093A207001ABE60 /* ConsentStringProtocol.swift in Sources */, 219 | F142B5472093A207001ABE60 /* Data.swift in Sources */, 220 | F142B5412093A207001ABE60 /* ConsentStringError.swift in Sources */, 221 | F142B5452093A207001ABE60 /* String.swift in Sources */, 222 | F142B53F2093A207001ABE60 /* ConsentString.swift in Sources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | F142B5252093A1C5001ABE60 /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | F142B52F2093A1C5001ABE60 /* Consent_String_SDK_SwiftTests.swift in Sources */, 231 | F142B5482093A207001ABE60 /* Data.swift in Sources */, 232 | F142B5442093A207001ABE60 /* ConsentStringProtocol.swift in Sources */, 233 | F142B5462093A207001ABE60 /* String.swift in Sources */, 234 | F142B5422093A207001ABE60 /* ConsentStringError.swift in Sources */, 235 | F142B5402093A207001ABE60 /* ConsentString.swift in Sources */, 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXSourcesBuildPhase section */ 240 | 241 | /* Begin PBXTargetDependency section */ 242 | F142B52C2093A1C5001ABE60 /* PBXTargetDependency */ = { 243 | isa = PBXTargetDependency; 244 | target = F142B51F2093A1C5001ABE60 /* Consent String SDK Swift */; 245 | targetProxy = F142B52B2093A1C5001ABE60 /* PBXContainerItemProxy */; 246 | }; 247 | /* End PBXTargetDependency section */ 248 | 249 | /* Begin XCBuildConfiguration section */ 250 | F142B5322093A1C5001ABE60 /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ALWAYS_SEARCH_USER_PATHS = NO; 254 | CLANG_ANALYZER_NONNULL = YES; 255 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 256 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 257 | CLANG_CXX_LIBRARY = "libc++"; 258 | CLANG_ENABLE_MODULES = YES; 259 | CLANG_ENABLE_OBJC_ARC = YES; 260 | CLANG_ENABLE_OBJC_WEAK = YES; 261 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 262 | CLANG_WARN_BOOL_CONVERSION = YES; 263 | CLANG_WARN_COMMA = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 268 | CLANG_WARN_EMPTY_BODY = YES; 269 | CLANG_WARN_ENUM_CONVERSION = YES; 270 | CLANG_WARN_INFINITE_RECURSION = YES; 271 | CLANG_WARN_INT_CONVERSION = YES; 272 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 273 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 274 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 275 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 276 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 277 | CLANG_WARN_STRICT_PROTOTYPES = YES; 278 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 279 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | CODE_SIGN_IDENTITY = "iPhone Developer"; 283 | COPY_PHASE_STRIP = NO; 284 | CURRENT_PROJECT_VERSION = 1; 285 | DEBUG_INFORMATION_FORMAT = dwarf; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | ENABLE_TESTABILITY = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu11; 289 | GCC_DYNAMIC_NO_PIC = NO; 290 | GCC_NO_COMMON_BLOCKS = YES; 291 | GCC_OPTIMIZATION_LEVEL = 0; 292 | GCC_PREPROCESSOR_DEFINITIONS = ( 293 | "DEBUG=1", 294 | "$(inherited)", 295 | ); 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 303 | MTL_ENABLE_DEBUG_INFO = YES; 304 | ONLY_ACTIVE_ARCH = YES; 305 | SDKROOT = iphoneos; 306 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 307 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 308 | VERSIONING_SYSTEM = "apple-generic"; 309 | VERSION_INFO_PREFIX = ""; 310 | }; 311 | name = Debug; 312 | }; 313 | F142B5332093A1C5001ABE60 /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ALWAYS_SEARCH_USER_PATHS = NO; 317 | CLANG_ANALYZER_NONNULL = YES; 318 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 320 | CLANG_CXX_LIBRARY = "libc++"; 321 | CLANG_ENABLE_MODULES = YES; 322 | CLANG_ENABLE_OBJC_ARC = YES; 323 | CLANG_ENABLE_OBJC_WEAK = YES; 324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_COMMA = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INFINITE_RECURSION = YES; 334 | CLANG_WARN_INT_CONVERSION = YES; 335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 336 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 337 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 339 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 340 | CLANG_WARN_STRICT_PROTOTYPES = YES; 341 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 342 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | CODE_SIGN_IDENTITY = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | CURRENT_PROJECT_VERSION = 1; 348 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 349 | ENABLE_NS_ASSERTIONS = NO; 350 | ENABLE_STRICT_OBJC_MSGSEND = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu11; 352 | GCC_NO_COMMON_BLOCKS = YES; 353 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 354 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 355 | GCC_WARN_UNDECLARED_SELECTOR = YES; 356 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 357 | GCC_WARN_UNUSED_FUNCTION = YES; 358 | GCC_WARN_UNUSED_VARIABLE = YES; 359 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 360 | MTL_ENABLE_DEBUG_INFO = NO; 361 | SDKROOT = iphoneos; 362 | SWIFT_COMPILATION_MODE = wholemodule; 363 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 364 | VALIDATE_PRODUCT = YES; 365 | VERSIONING_SYSTEM = "apple-generic"; 366 | VERSION_INFO_PREFIX = ""; 367 | }; 368 | name = Release; 369 | }; 370 | F142B5352093A1C5001ABE60 /* Debug */ = { 371 | isa = XCBuildConfiguration; 372 | buildSettings = { 373 | CLANG_ENABLE_MODULES = YES; 374 | CODE_SIGN_IDENTITY = ""; 375 | CODE_SIGN_STYLE = Automatic; 376 | DEFINES_MODULE = YES; 377 | DEVELOPMENT_TEAM = MQN7FXPCA7; 378 | DYLIB_COMPATIBILITY_VERSION = 1; 379 | DYLIB_CURRENT_VERSION = 1; 380 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 381 | INFOPLIST_FILE = "Consent String SDK Swift/Info.plist"; 382 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 383 | LD_RUNPATH_SEARCH_PATHS = ( 384 | "$(inherited)", 385 | "@executable_path/Frameworks", 386 | "@loader_path/Frameworks", 387 | ); 388 | PRODUCT_BUNDLE_IDENTIFIER = "com.iab.Consent-String-SDK-Swift"; 389 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 390 | SKIP_INSTALL = YES; 391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 392 | SWIFT_VERSION = 4.0; 393 | TARGETED_DEVICE_FAMILY = "1,2"; 394 | }; 395 | name = Debug; 396 | }; 397 | F142B5362093A1C5001ABE60 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | CLANG_ENABLE_MODULES = YES; 401 | CODE_SIGN_IDENTITY = ""; 402 | CODE_SIGN_STYLE = Automatic; 403 | DEFINES_MODULE = YES; 404 | DEVELOPMENT_TEAM = MQN7FXPCA7; 405 | DYLIB_COMPATIBILITY_VERSION = 1; 406 | DYLIB_CURRENT_VERSION = 1; 407 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 408 | INFOPLIST_FILE = "Consent String SDK Swift/Info.plist"; 409 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 410 | LD_RUNPATH_SEARCH_PATHS = ( 411 | "$(inherited)", 412 | "@executable_path/Frameworks", 413 | "@loader_path/Frameworks", 414 | ); 415 | PRODUCT_BUNDLE_IDENTIFIER = "com.iab.Consent-String-SDK-Swift"; 416 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 417 | SKIP_INSTALL = YES; 418 | SWIFT_VERSION = 4.0; 419 | TARGETED_DEVICE_FAMILY = "1,2"; 420 | }; 421 | name = Release; 422 | }; 423 | F142B5382093A1C5001ABE60 /* Debug */ = { 424 | isa = XCBuildConfiguration; 425 | buildSettings = { 426 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 427 | CODE_SIGN_STYLE = Automatic; 428 | DEVELOPMENT_TEAM = MQN7FXPCA7; 429 | INFOPLIST_FILE = "Consent String SDK SwiftTests/Info.plist"; 430 | LD_RUNPATH_SEARCH_PATHS = ( 431 | "$(inherited)", 432 | "@executable_path/Frameworks", 433 | "@loader_path/Frameworks", 434 | ); 435 | PRODUCT_BUNDLE_IDENTIFIER = "com.iab.Consent-String-SDK-SwiftTests"; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | SWIFT_VERSION = 4.0; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | F142B5392093A1C5001ABE60 /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 446 | CODE_SIGN_STYLE = Automatic; 447 | DEVELOPMENT_TEAM = MQN7FXPCA7; 448 | INFOPLIST_FILE = "Consent String SDK SwiftTests/Info.plist"; 449 | LD_RUNPATH_SEARCH_PATHS = ( 450 | "$(inherited)", 451 | "@executable_path/Frameworks", 452 | "@loader_path/Frameworks", 453 | ); 454 | PRODUCT_BUNDLE_IDENTIFIER = "com.iab.Consent-String-SDK-SwiftTests"; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_VERSION = 4.0; 457 | TARGETED_DEVICE_FAMILY = "1,2"; 458 | }; 459 | name = Release; 460 | }; 461 | /* End XCBuildConfiguration section */ 462 | 463 | /* Begin XCConfigurationList section */ 464 | F142B51A2093A1C5001ABE60 /* Build configuration list for PBXProject "Consent String SDK Swift" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | F142B5322093A1C5001ABE60 /* Debug */, 468 | F142B5332093A1C5001ABE60 /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | defaultConfigurationName = Release; 472 | }; 473 | F142B5342093A1C5001ABE60 /* Build configuration list for PBXNativeTarget "Consent String SDK Swift" */ = { 474 | isa = XCConfigurationList; 475 | buildConfigurations = ( 476 | F142B5352093A1C5001ABE60 /* Debug */, 477 | F142B5362093A1C5001ABE60 /* Release */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | F142B5372093A1C5001ABE60 /* Build configuration list for PBXNativeTarget "Consent String SDK SwiftTests" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | F142B5382093A1C5001ABE60 /* Debug */, 486 | F142B5392093A1C5001ABE60 /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | /* End XCConfigurationList section */ 492 | }; 493 | rootObject = F142B5172093A1C5001ABE60 /* Project object */; 494 | } 495 | -------------------------------------------------------------------------------- /Consent String SDK Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Consent String SDK Swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Consent String SDK Swift/ConsentString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentString.swift 3 | // gdprConsentStringSwift 4 | // 5 | // Created by Daniel Kanaan on 4/17/18. 6 | // Copyright © 2018 Daniel Kanaan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ConsentString:ConsentStringProtocol { 12 | 13 | /** 14 | The current Consent String. Setting will allow replacement of the curr 15 | */ 16 | public var consentString: String { 17 | 18 | //error correction in didSet resets old value if base64decoding fails 19 | didSet { 20 | guard let dataValue = Data(base64Encoded: consentString.base64Padded) else { 21 | print("New Consent String Value is not base64 decodable. Throwing away changes.") 22 | consentString = oldValue 23 | return 24 | } 25 | consentData = dataValue 26 | } 27 | 28 | } 29 | 30 | var consentData:Data 31 | 32 | /** 33 | Creates new instance of a ConsentString object 34 | 35 | - parameter consentString: web-safe base64 encoded consent string 36 | */ 37 | public required init(consentString: String) throws { 38 | self.consentString = consentString 39 | guard let dataValue = Data(base64Encoded: self.consentString.base64Padded) else { 40 | throw ConsentStringError.base64DecodingFailed 41 | } 42 | consentData = dataValue 43 | } 44 | 45 | 46 | public var cmpId: Int { 47 | return Int(consentData.intValue(fromBit: 78, toBit: 89)) 48 | } 49 | 50 | public var consentScreen: Int { 51 | return Int(consentData.intValue(fromBit: 102, toBit: 107)) 52 | } 53 | 54 | public var consentLanguage: String { 55 | var data = consentData.data(fromBit: 108, toBit: 119) 56 | data.insert(0, at: 0) 57 | let string = data.base64EncodedString() 58 | return String(string[string.index(string.startIndex, offsetBy: 2)...]) 59 | } 60 | 61 | let purposesStart:Int64 = 132 62 | let maxPurposes:Int64 = 24 63 | 64 | public var purposesAllowed: [Int8] { 65 | var resultsArray = [Int8]() 66 | for purposeId in 1...maxPurposes { 67 | let purposeBit = purposesStart - 1 + Int64(purposeId) 68 | let value = Int(consentData.intValue(fromBit: purposeBit, toBit: purposeBit)) 69 | if value > 0 { 70 | resultsArray.append(Int8(purposeId)) 71 | } 72 | } 73 | return resultsArray 74 | } 75 | 76 | 77 | public func purposeAllowed(forPurposeId purposeId: Int8) -> Bool { 78 | if purposeId > 24 || purposeId < 1 { 79 | return false 80 | } 81 | let purposeBit = purposesStart - 1 + Int64(purposeId) 82 | let value = Int(consentData.intValue(fromBit: purposeBit, toBit: purposeBit)) 83 | if value > 0 { 84 | return true 85 | } 86 | return false 87 | } 88 | 89 | //Used to determine whether we need to check for a vendor ID at all if it's greater than this value 90 | private var maxVendorId : Int { 91 | get { 92 | return Int(consentData.intValue(fromBit: 156, toBit: 171)) 93 | } 94 | } 95 | 96 | private var isBitField:Bool { 97 | get { 98 | let value = consentData.intValue(fromBit: 172, toBit: 172) 99 | return value == 0 100 | } 101 | } 102 | 103 | private var isRange:Bool { 104 | get { 105 | return !isBitField 106 | } 107 | } 108 | 109 | private let bitFieldVendorStart:Int64 = 173 110 | private let rangeDefaultConsent:Int64 = 173 111 | 112 | public func isVendorAllowed(vendorId: Int) -> Bool { 113 | if vendorId > maxVendorId { 114 | return false 115 | } 116 | if isBitField { 117 | let vendorBitField = bitFieldVendorStart + Int64(vendorId) - 1 118 | //not enough bits 119 | guard vendorBitField < consentData.count * 8 else { 120 | return false 121 | } 122 | let value = consentData.intValue(fromBit: vendorBitField, toBit: vendorBitField) 123 | if value == 0 { 124 | return false 125 | } else { 126 | return true 127 | } 128 | } else { 129 | let consentDataMaxBit = consentData.count * 8 - 1 //1 byte, last bit is 7, for 2 bytes, last is 15 etc... 130 | let defaultConsent = consentData.intValue(fromBit: rangeDefaultConsent, toBit: rangeDefaultConsent) 131 | let numEntries = Int(consentData.intValue(fromBit: 174, toBit: 185)) 132 | var rangeStart = Int64(186) 133 | for _ in 0.. Bool 20 | func isVendorAllowed(vendorId:Int) -> Bool 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Consent String SDK Swift/Consent_String_SDK_Swift.h: -------------------------------------------------------------------------------- 1 | // 2 | // Consent_String_SDK_Swift.h 3 | // Consent String SDK Swift 4 | // 5 | // Created by Daniel Kanaan on 4/27/18. 6 | // Copyright © 2018 Interactive Advertising Bureau. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Consent_String_SDK_Swift. 12 | FOUNDATION_EXPORT double Consent_String_SDK_SwiftVersionNumber; 13 | 14 | //! Project version string for Consent_String_SDK_Swift. 15 | FOUNDATION_EXPORT const unsigned char Consent_String_SDK_SwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Consent String SDK Swift/Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data.swift 3 | // gdprConsentStringSwift 4 | // 5 | // Created by Daniel Kanaan on 4/19/18. 6 | // Copyright © 2018 Daniel Kanaan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Data { 12 | 13 | /** 14 | This does most of the SDK heavy lifting. It will return the bits reshuffled into new bytes terminating at toBit. Bits will be left padded with zeros to fill first byte 15 | 16 | For example in a bit string "00000100 10000100" (two bytes), requesting the integer value from bit 0 to bit 7 will return "00000100". Requesting the bytes from bit 1 to bit 8 would return "00001000". Etc... 17 | 18 | Things to note. 19 | 20 | 1. Results are padded from the left with zeros to fill the first byte. 21 | 2. Requests for a terminating bit after the final bit of actual data will trim the request to last bit. The last bit will be the least significant bit of the result with zeros padded on the left to 64 bit. This means that a bit string "0000 0001" or 1 if requested for bit 0 to bit 100 will return 1. 22 | 3. Max length from fromBit to toBit inclusive is 64. Anything more will be terminated at the 64th bit. 23 | 24 | - parameter fromBit: Int64 value of start bit (inclusive) 25 | - parameter toBit: Int64 value of final bit (inclusive) 26 | */ 27 | func bytes(fromBit startBit:Int64, toBit endBit:Int64) -> [UInt8] { 28 | let byteCount = count 29 | let lastBit = Int64(byteCount * 8 - 1) 30 | var byteArray = [UInt8]() 31 | if startBit > lastBit { 32 | return byteArray 33 | } 34 | var realEndBit = endBit 35 | //limit to length of data 36 | if endBit > lastBit { 37 | realEndBit = lastBit 38 | } 39 | //limit to 64 bits 40 | if endBit - startBit > 63 { 41 | realEndBit = startBit + 63 42 | } 43 | if startBit <= endBit, let startByte = byte(forBit: startBit), let endByte = byte(forBit: realEndBit) { 44 | if startByte == endByte { 45 | //avoid complexity by removing the case where startBit and endBit are on the same byte 46 | let leftShift = startBit % 8 47 | let rightShift = 8 - (realEndBit % 8) - 1 48 | byteArray.append((self[startByte] << leftShift) >> (rightShift + leftShift)) 49 | } else { 50 | let rightShift = 8 - (realEndBit % 8) - 1 51 | let leftShift = 8 - rightShift 52 | let finalLeftShift = startBit % 8 53 | var currentByte = endByte 54 | //addendum is required for when first byte pull is less than next bite push 55 | let addendum = finalLeftShift > leftShift ? 1 : 0 56 | while currentByte > startByte + addendum { 57 | let beggining = self[currentByte - 1] << leftShift 58 | let ending = self[currentByte] >> rightShift 59 | byteArray.insert(beggining | ending, at:0) 60 | currentByte -= 1 61 | } 62 | let finalRightShift = finalLeftShift + rightShift 63 | 64 | if addendum == 0 { 65 | //means that there's some bits on the first byte that need to be added 66 | byteArray.insert((self[currentByte] << finalLeftShift) >> finalRightShift , at: 0) 67 | } else { 68 | //means that there's some bits on the second byte and some from the first byte totaling less than a byte 69 | let rightBits = (self[currentByte] >> rightShift) 70 | let leftBits = (self[currentByte - 1] << finalLeftShift) >> (finalLeftShift - (8 - rightShift)) 71 | byteArray.insert(leftBits | rightBits , at: 0) 72 | } 73 | } 74 | } 75 | return byteArray 76 | } 77 | 78 | /** 79 | This returns bytes of data terminating with the bit at "endBit" and starting at startBit with a maximum byte length of 8. 80 | 81 | For example in a bit string "00000100 10000100" (two bytes), requesting the data from bit 0 to bit 7 will return "00000100". Requesting the bytes from bit 1 to bit 8 would return "00001000". Etc... 82 | 83 | Things to note. 84 | 85 | 1. Results are padded from the left with zeros to fill the first byte. 86 | 2. Requests for a terminating bit after the final bit of actual data will trim the request to last bit. The last bit will be the least significant bit of the result with zeros padded on the left to 64 bit. This means that a bit string "0000 0001" or 1 if requested for bit 0 to bit 100 will return 1. 87 | 3. Max length from fromBit to toBit inclusive is 64. Anything more will be terminated at the 64th bit. 88 | 4. Requesting bits that start after the end of the data will return empty data 89 | 90 | - parameter fromBit: Int64 value of start bit (inclusive) 91 | - parameter toBit: Int64 value of final bit (inclusive) 92 | */ 93 | func data(fromBit startBit:Int64, toBit endBit:Int64) -> Data { 94 | let byteArray = bytes(fromBit: startBit, toBit: endBit) 95 | return Data(bytes: byteArray) 96 | } 97 | 98 | /** 99 | This returns the bigEndian IntegerValue of the bits terminating with the bit at "endBit" and starting at startBit. 100 | 101 | For example in a bit string "00000100 10000100" (two bytes), requesting the data from bit 0 to bit 7 will return 8 or "00000100". Requesting the value from bit 1 to bit 8 would return 16 or "00001000". Etc... 102 | 103 | Things to note. 104 | 105 | 1. Results are padded from the left with zeros to fill the first byte. 106 | 2. Requests for a terminating bit after the final bit of actual data will trim the request to last bit. The last bit will be the least significant bit of the result with zeros padded on the left to 64 bit. This means that a bit string "0000 0001" or 1 if requested for bit 0 to bit 100 will return 1. 107 | 3. Max length from fromBit to toBit inclusive is 64. Anything more will be terminated at the 64th bit. 108 | 109 | - parameter fromBit: Int64 value of start bit (inclusive) 110 | - parameter toBit: Int64 value of final bit (inclusive) 111 | */ 112 | func intValue(fromBit startBit:Int64, toBit endBit:Int64) -> Int64 { 113 | var dataValue = data(fromBit: startBit, toBit: endBit) 114 | while dataValue.count < 8 { 115 | dataValue.insert(0, at: 0) 116 | } 117 | let value = UInt64(bigEndian: dataValue.withUnsafeBytes { $0.pointee }) 118 | return Int64(value) 119 | } 120 | 121 | } 122 | 123 | extension Data { 124 | 125 | 126 | /* 127 | Returns byte number for bit with error correction for length 128 | */ 129 | func byte(forBit bit:Int64) -> Int? { 130 | let lastBit = count * 8 - 1 131 | if bit > lastBit { 132 | return nil 133 | } 134 | if bit < 0 { 135 | return nil 136 | } 137 | return Int(bit / 8) 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /Consent String SDK Swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Consent String SDK Swift/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // gdprConsentStringSwift 4 | // 5 | // Created by Daniel Kanaan on 4/17/18. 6 | // Copyright © 2018 Daniel Kanaan. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | 13 | var base64Padded:String { 14 | get { 15 | return self.padding(toLength: ((self.count + 3) / 4) * 4,withPad: "=",startingAt: 0) 16 | } 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Consent String SDK SwiftTests/Consent_String_SDK_SwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Consent_String_SDK_SwiftTests.swift 3 | // Consent String SDK SwiftTests 4 | // 5 | // Created by Daniel Kanaan on 4/27/18. 6 | // Copyright © 2018 Interactive Advertising Bureau. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Consent_String_SDK_Swift 11 | 12 | class Consent_String_SDK_SwiftTests: XCTestCase { 13 | 14 | let base64 = ["BOMexSfOMexSfAAABAENAA////ABSABgACAAIA", 15 | "BOMexSfOMexSfAAABAENAA////ABSABgACBAAA", 16 | "BOMexSfOMexSfAAABAENABAAEAABSABgACBAAA", 17 | "BOMexSfOMexSfAAABAENABAAEAABRVVVAA", 18 | "BOMexSfOMexSfAAABAENABAAEAABQAIAAA", 19 | "BOMexSfOMexSfAAABAENABAAEAABQAIAgA", 20 | "BOM03lPOM03lPAAABAENAAAAAAABR//g" 21 | ] 22 | let binaries = [ 23 | "000001001110001100011110110001010010011111001110001100011110110001010010011111000000000000000000000001000000000100001101000000000000111111111111111111111111000000000001010010000000000001100000000000000010000000000000001", 24 | "000001001110001100011110110001010010011111001110001100011110110001010010011111000000000000000000000001000000000100001101000000000000111111111111111111111111000000000001010010000000000001100000000000000010000001000000000", 25 | "000001001110001100011110110001010010011111001110001100011110110001010010011111000000000000000000000001000000000100001101000000000001000000000000000100000000000000000001010010000000000001100000000000000010000001000000000", 26 | "0000010011100011000111101100010100100111110011100011000111101100010100100111110000000000000000000000010000000001000011010000000000010000000000000001000000000000000000010100010101010101010101010", 27 | "0000010011100011000111101100010100100111110011100011000111101100010100100111110000000000000000000000010000000001000011010000000000010000000000000001000000000000000000010100000000000010000000000", 28 | "0000010011100011000111101100010100100111110011100011000111101100010100100111110000000000000000000000010000000001000011010000000000010000000000000001000000000000000000010100000000000010000000001", 29 | "0000010011100011001101001101111001010011110011100011001101001101111001010011110000000000000000000000010000000001000011010000000000000000000000000000000000000000000000010100011111111111111" 30 | ] 31 | 32 | override func setUp() { 33 | super.setUp() 34 | // Put setup code here. This method is called before the invocation of each test method in the class. 35 | } 36 | 37 | override func tearDown() { 38 | // Put teardown code here. This method is called after the invocation of each test method in the class. 39 | super.tearDown() 40 | } 41 | 42 | 43 | 44 | func testPerformance() { 45 | self.measure { 46 | // Put the code you want to measure the time of here. 47 | let data = Data(base64Encoded: "BOMYO7eOMYO7eAABAENAAAAAAAAoAAA".base64Padded)! 48 | XCTAssert(data.intValue(fromBit: 6, toBit: 41) == 15240064734) 49 | } 50 | } 51 | 52 | func testPadding() { 53 | var string = "BOMXuBxOMXuBxAABAENAAAAAAAAoAAA" 54 | XCTAssert(string.base64Padded == "BOMXuBxOMXuBxAABAENAAAAAAAAoAAA=", string.base64Padded) 55 | string = "BOMXuBxOMXuBxAABAENAAAAAAAAoAAB" 56 | XCTAssert(string.base64Padded == "BOMXuBxOMXuBxAABAENAAAAAAAAoAAB=", string.base64Padded) 57 | string = "BOMXuBxOMXuBxAABAENAAAAAAAAoA" 58 | XCTAssert(string.base64Padded == "BOMXuBxOMXuBxAABAENAAAAAAAAoA===", string.base64Padded) 59 | string = "BOMXuBxOMXuBxAABAENAAAAAAAAo" 60 | XCTAssert(string.base64Padded == "BOMXuBxOMXuBxAABAENAAAAAAAAo", string.base64Padded) 61 | string = "BOMXuBxOMXuBxAABAENAAAAAAAAoAABoo" 62 | XCTAssert(string.base64Padded == "BOMXuBxOMXuBxAABAENAAAAAAAAoAABoo===", string.base64Padded) 63 | } 64 | 65 | func testBase64Decoding() { 66 | 67 | for (index,string) in base64.enumerated() { 68 | let data = Data(base64Encoded: string.base64Padded) 69 | XCTAssert(binary(string: binaryStringRepresenting(data: data!), isEqualToBinaryString: binaries[index])) 70 | } 71 | } 72 | 73 | func testBase64Encoding() { 74 | let notEqual:[(String,String)] = 75 | [("BOMXuBxOMXuBxAABAENAAAAAAAAoAAAA", 76 | "BOMXuBxOMXuBxAABAENAAAAAAAAoAABA"), 77 | ("BOMXuBxOMXuBxAABAENAAAAAAAAoA", 78 | "BOMXuBxOMXuBxAABAENAAAAAAAAoAA"), 79 | ("BOMXuBxOMXuBxAABAENAAAAAAAAo", 80 | "BOMXuBxOMXuBxAABAENAAAAAAAAoA"), 81 | ("AA==", 82 | "A="), 83 | ("AB=", 84 | "ABA"), 85 | ("AABAA", 86 | "AABA"), 87 | ("===", 88 | "A") 89 | ] 90 | 91 | for pair in notEqual { 92 | let data1 = Data(base64Encoded: pair.0.base64Padded) 93 | let data2 = Data(base64Encoded: pair.1.base64Padded) 94 | XCTAssert(data1 != data2,"\(pair.0) == \(pair.1)") 95 | } 96 | 97 | let equal:[(String,String)] = [ 98 | ("BOMXuBxOMXuBxAABAENAAAAAAAAoA=", 99 | "BOMXuBxOMXuBxAABAENAAAAAAAAoA"), 100 | ("BOMXuBxOMXuBxAABAENAAAAAAAAoA==", 101 | "BOMXuBxOMXuBxAABAENAAAAAAAAoA"), 102 | ("BOMXuBxOMXuBxAABAENAAAAAAAAoA===", 103 | "BOMXuBxOMXuBxAABAENAAAAAAAAoA") 104 | ] 105 | for pair in equal { 106 | let data1 = Data(base64Encoded: pair.0.base64Padded) 107 | let data2 = Data(base64Encoded: pair.1.base64Padded) 108 | XCTAssert(data1 == data2, "\(pair.0) != \(pair.1)") 109 | } 110 | } 111 | 112 | func testInit() { 113 | 114 | for (index,string) in base64.enumerated() { 115 | 116 | let consentString = try?ConsentString(consentString: string) 117 | let representation = binaryStringRepresenting(data: consentString!.consentData) 118 | XCTAssert(binary(string: representation, isEqualToBinaryString: binaries[index]), "Actual value : \(representation)") 119 | 120 | } 121 | 122 | } 123 | 124 | func testDataExtensions () { 125 | var data = Data(base64Encoded: "BOMYO7eOMYO7eAABAENAAAAAAAAoAAA".base64Padded)! 126 | var byteLength = 23 127 | var lastBit = Int64(byteLength * 8 - 1) 128 | XCTAssert(data.byte(forBit: 0) == 0) 129 | XCTAssert(data.byte(forBit: 1) == 0) 130 | XCTAssert(data.byte(forBit: 6) == 0) 131 | XCTAssert(data.byte(forBit: 8) == 1) 132 | XCTAssert(data.byte(forBit: 10) == 1) 133 | XCTAssert(data.byte(forBit: 15) == 1) 134 | XCTAssert(data.byte(forBit: 16) == 2) 135 | XCTAssert(data.byte(forBit: lastBit) == byteLength - 1) 136 | XCTAssert(data.byte(forBit: lastBit + 1) == nil) 137 | XCTAssert(data.byte(forBit: lastBit - 8) == byteLength - 2) 138 | 139 | XCTAssert(data.intValue(fromBit: 0, toBit: 5) == 1) 140 | XCTAssert(data.intValue(fromBit: 0, toBit: 7) == 4) 141 | XCTAssert(data.intValue(fromBit: 6, toBit: 41) == 15240064734) 142 | XCTAssert(data.intValue(fromBit: 42, toBit: 77) == 15240064734) 143 | XCTAssert(data.intValue(fromBit: 78, toBit: 89) == 0) 144 | XCTAssert(data.intValue(fromBit: 90, toBit: 95) == 1) 145 | XCTAssert(data.intValue(fromBit: 96, toBit: 101) == 0) 146 | XCTAssert(data.intValue(fromBit: 102, toBit: 113) == 269) 147 | XCTAssert(data.intValue(fromBit: 114, toBit: 125) == 0) 148 | XCTAssert(data.intValue(fromBit: 13*8+1, toBit: 13*8+1) == 1) 149 | 150 | data = Data(base64Encoded: "AAAB")! 151 | byteLength = 3 152 | lastBit = Int64(byteLength * 8 - 1) 153 | for i in 0..<(byteLength*3-1) { 154 | XCTAssert(data.intValue(fromBit: Int64(i), toBit: Int64(i)) == 0) 155 | } 156 | XCTAssert(data.intValue(fromBit: Int64(byteLength*8-1), toBit: Int64(byteLength*8-1)) == 1) 157 | XCTAssert(data.intValue(fromBit: 0, toBit: 23) == 1) 158 | XCTAssert(data.intValue(fromBit: 100000, toBit: 1000000) == 0) 159 | 160 | data = Data(base64Encoded: "AAEA")! 161 | byteLength = 3 162 | lastBit = Int64(byteLength * 8 - 1) 163 | XCTAssert(data.intValue(fromBit: 15, toBit: 15) == 1) 164 | XCTAssert(data.intValue(fromBit: 0, toBit: 15) == 1) 165 | XCTAssert(data.intValue(fromBit: 16, toBit: 23) == 0) 166 | 167 | data = Data(base64Encoded: "AQAA")! 168 | byteLength = 3 169 | lastBit = Int64(byteLength * 8 - 1) 170 | XCTAssert(data.intValue(fromBit: 15, toBit: 15) == 0) 171 | XCTAssert(data.intValue(fromBit: 16, toBit: 23) == 0) 172 | XCTAssert(data.intValue(fromBit: 0, toBit: 7) == 1) 173 | XCTAssert(data.intValue(fromBit: 0, toBit: 8) == 2) 174 | XCTAssert(data.intValue(fromBit: 0, toBit: 9) == 4) 175 | XCTAssert(data.intValue(fromBit: 0, toBit: 10) == 8) 176 | XCTAssert(data.intValue(fromBit: 0, toBit: 11) == 16) 177 | XCTAssert(data.intValue(fromBit: 0, toBit: 12) == 32) 178 | 179 | //Bad Data 180 | data = Data(base64Encoded: "AAAAAAAB")! 181 | byteLength = 6 182 | lastBit = Int64(byteLength * 8 - 1) 183 | 184 | XCTAssert(data.intValue(fromBit: 0, toBit: lastBit) == 1) 185 | XCTAssert(data.intValue(fromBit: 0, toBit: 63) == 1) 186 | 187 | 188 | data = Data(base64Encoded: "AAAAAAABAAAA")! 189 | byteLength = data.count 190 | lastBit = Int64(byteLength * 8 - 1) 191 | 192 | XCTAssert(data.intValue(fromBit: 0, toBit: lastBit) == 65536, "\(data.intValue(fromBit: 0, toBit: lastBit))") 193 | XCTAssert(data.intValue(fromBit: 0, toBit: 63) == 65536) 194 | XCTAssert(data.intValue(fromBit: 0, toBit: 100) == 65536) 195 | 196 | 197 | data = Data(base64Encoded: "BOMexSfOMexSfAAABAENAAAAAAAAoAA".base64Padded)! 198 | byteLength = data.count 199 | lastBit = Int64(byteLength * 8 - 1) 200 | XCTAssert(data.intValue(fromBit: 0, toBit: 5) == 1) 201 | XCTAssert(data.intValue(fromBit: 6, toBit: 41) == 15241778335) 202 | XCTAssert(data.intValue(fromBit: 42, toBit: 77) == 15241778335) 203 | XCTAssert(data.intValue(fromBit: 78, toBit: 89) == 0) 204 | XCTAssert(data.intValue(fromBit: 90, toBit: 101) == 1) 205 | XCTAssert(data.intValue(fromBit: 102, toBit: 107) == 0) 206 | XCTAssert(data.intValue(fromBit: 108, toBit: 119) == 269) 207 | XCTAssert(data.intValue(fromBit: 120, toBit: 131) == 0) 208 | XCTAssert(data.intValue(fromBit: 132, toBit: 155) == 0) 209 | XCTAssert(data.intValue(fromBit: 156, toBit: 171) == 10) 210 | XCTAssert(data.intValue(fromBit: 172, toBit: 172) == 0) 211 | XCTAssert(data.intValue(fromBit: 173, toBit: lastBit) == 0) 212 | } 213 | 214 | func testConsentStringLanguage () { 215 | let consentString = try!ConsentString(consentString: "BOMexSfOMexSfAAABAENAAAAAAAAoAA") 216 | XCTAssert(consentString.consentLanguage == "EN", consentString.consentLanguage) 217 | } 218 | 219 | func testPurposesAllowed () { 220 | let consentStringArray = ["BOMexSfOMexSfAAABAENAA////AAoAA", 221 | "BOMexSfOMexSfAAABAENAAf///AAoAA", 222 | "BOMexSfOMexSfAAABAENAAP///AAoAA", 223 | "BOMexSfOMexSfAAABAENAAH///AAoAA", 224 | "BOMexSfOMexSfAAABAENAAD///AAoAA", 225 | "BOMexSfOMexSfAAABAENAAB///AAoAA", 226 | "BOMexSfOMexSfAAABAENAAA///AAoAA", 227 | "BOMexSfOMexSfAAABAENAAAf//AAoAA", 228 | "BOMexSfOMexSfAAABAENAAAP//AAoAA", 229 | "BOMexSfOMexSfAAABAENAAAH//AAoAA", 230 | "BOMexSfOMexSfAAABAENAAAD//AAoAA", 231 | "BOMexSfOMexSfAAABAENAAAB//AAoAA", 232 | "BOMexSfOMexSfAAABAENAAAA//AAoAA", 233 | "BOMexSfOMexSfAAABAENAAAAf/AAoAA", 234 | "BOMexSfOMexSfAAABAENAAAAP/AAoAA", 235 | "BOMexSfOMexSfAAABAENAAAAH/AAoAA", 236 | "BOMexSfOMexSfAAABAENAAAAD/AAoAA", 237 | "BOMexSfOMexSfAAABAENAAAAB/AAoAA", 238 | "BOMexSfOMexSfAAABAENAAAAA/AAoAA", 239 | "BOMexSfOMexSfAAABAENAAAAAfAAoAA", 240 | "BOMexSfOMexSfAAABAENAAAAAPAAoAA", 241 | "BOMexSfOMexSfAAABAENAAAAAHAAoAA", 242 | "BOMexSfOMexSfAAABAENAAAAADAAoAA", 243 | "BOMexSfOMexSfAAABAENAAAAABAAoAA", 244 | "BOMexSfOMexSfAAABAENAAAAAAAAoAA"] 245 | var consentString:ConsentString 246 | var purposesAllowed:[Int8] 247 | for (index,string) in consentStringArray.enumerated() { 248 | consentString = try!ConsentString(consentString: string) 249 | purposesAllowed = consentString.purposesAllowed 250 | if index < 24 { 251 | for i in index+1...24 { 252 | XCTAssert(purposesAllowed.contains(Int8(i))) 253 | } 254 | } 255 | for i in 0.. String { 331 | return data.reduce("") { (acc, byte) -> String in 332 | let stringRep = String(byte, radix: 2) 333 | let pad = 8 - stringRep.count 334 | let padString = "".padding(toLength: pad, withPad: "0", startingAt: 0) 335 | return acc + padString + stringRep 336 | } 337 | } 338 | 339 | func binary(string:String, isEqualToBinaryString string2:String) -> Bool { 340 | if abs(string.count - string2.count) > 7 { 341 | return false 342 | } 343 | var index = 0 344 | var max = string.count 345 | if string.count > string2.count { 346 | max = string2.count 347 | } 348 | while index < max { 349 | if string[string.index(string.startIndex, offsetBy: index)] != string2[string2.index(string2.startIndex, offsetBy: index)] { 350 | return false 351 | } 352 | index += 1 353 | } 354 | if string.count > string2.count { 355 | while index < string.count { 356 | if string[string.index(string.startIndex, offsetBy: index)] != "0" { 357 | return false 358 | } 359 | index += 1 360 | } 361 | } else { 362 | while index < string2.count { 363 | if string2[string2.index(string2.startIndex, offsetBy: index)] != "0" { 364 | return false 365 | } 366 | index += 1 367 | } 368 | } 369 | return true 370 | } 371 | 372 | } 373 | -------------------------------------------------------------------------------- /Consent String SDK SwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Transparency and Consent Framework: Consent-String-SDK-Swift 2 | 3 | Encode and decode web-safe base64 consent information with the IAB EU's GDPR Transparency and Consent Framework. 4 | 5 | This library is a Swift reference implementation for dealing with consent strings in the IAB EU's GDPR Transparency and Consent Framework. 6 | It should be used by anyone who receives or sends consent information like vendors that receive consent data from a partner, or consent management platforms that need to encode/decode the global cookie. 7 | 8 | The IAB specification for the consent string format is available on the [IAB Github](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md) (section "Vendor Consent Cookie Format"). 9 | 10 | **This library fully supports the version v1.1 of the specification. It can encode and decode consent strings with version bit 1.** 11 | 12 | #### IAB Europe Transparency and Consent Framework 13 | 14 | In November 2017, IAB Europe and a cross-section of the publishing and advertising industry, announced a new Transparency & Consent Framework to help publishers, advertisers and technology companies comply with key elements of GDPR. The Framework will give the publishing and advertising industries a common language with which to communicate consumer consent for the delivery of relevant online advertising and content. 15 | 16 | Framework Technical specifications available at: https://raw.githubusercontent.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework. 17 | 18 | 19 | ## Consent String SDK (Swift) 20 | - [Installation](#installation) 21 | - [Usage](#usage) 22 | - [Use cases](#use-cases) 23 | - [Additional Documentation](#documentation) 24 | 25 | 26 | 27 | 28 | 29 | 30 | ## About 31 | 32 | #### About IAB Tech Lab 33 | 34 | The IAB Technology Laboratory (?Tech Lab?) is a non-profit research and development consortium that produces and provides standards, software, and services to drive growth of an effective and sustainable global digital media ecosystem. Comprised of digital publishers and ad technology firms, as well as marketers, agencies, and other companies with interests in the interactive marketing arena, IAB Tech Lab aims to enable brand and media growth via a transparent, safe, effective supply chain, simpler and more consistent measurement, and better advertising experiences for consumers, with a focus on mobile and ?TV?/digital video channel enablement. The IAB Tech Lab portfolio includes the DigiTrust real-time standardized identity service designed to improve the digital experience for consumers, publishers, advertisers, and third-party platforms. Board members include AppNexus, ExtremeReach, Google, GroupM, Hearst Digital Media, Integral Ad Science, Index Exchange, LinkedIn, MediaMath, Microsoft, Moat, Pandora, PubMatic, Quantcast, Telaria, The Trade Desk, and Yahoo! Japan. Established in 2014, the IAB Tech Lab is headquartered in New York City with an office in San Francisco and representation in Seattle and London. 35 | 36 | Learn more about IAB Tech Lab here: [https://www.iabtechlab.com/](https://www.iabtechlab.com/) 37 | 38 | #### About IAB Europe 39 | 40 | IAB Europe is the voice of digital business and the leading European-level industry association for the interactive advertising ecosystem. Its mission is to promote the development of this innovative sector by shaping the regulatory environment, investing in research and education, and developing and facilitating the uptake of business standards. 41 | 42 | Learn more about IAB Europe here: [https://www.iabeurope.eu/](https://www.iabeurope.eu/) 43 | 44 | 45 | #### Contributors and Technical Governance 46 | 47 | GDPR Technical Working Group members provide contributions to this repository. Participants in the GDPR Technical Working group must be members of IAB Tech Lab. Technical Governance for the project is provided by the IAB Tech Lab GDPR Commit Group. 48 | --------------------------------------------------------------------------------