├── demo-ios ├── Gdpr │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.swift │ ├── InMemoryDataSource.swift │ ├── SimpleViewController.swift │ ├── NavigationViewController.swift │ ├── Info.plist │ ├── TableViewController.swift │ ├── AppDelegate.swift │ └── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard └── Gdpr.xcodeproj │ ├── xcuserdata │ ├── cristi.xcuserdatad │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ └── cristianbaluta.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ ├── cristi.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── cristianbaluta.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── project.pbxproj ├── src ├── ConsentKitCellProtocol.swift ├── ConsentKitCell.swift ├── ConsentKitUserDefaultsDataSource.swift ├── CloudKitViewControllerHeader.swift ├── ConsentKitServices.swift ├── ConsentKitTableViewDataSource.swift ├── ConsentKit.swift ├── ConsentKitViewController.swift └── ConsentKitCell.xib ├── Consent.podspec ├── LICENSE └── README.md /demo-ios/Gdpr/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/xcuserdata/cristi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/project.xcworkspace/xcuserdata/cristi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristibaluta/ConsentKit/HEAD/demo-ios/Gdpr.xcodeproj/project.xcworkspace/xcuserdata/cristi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/project.xcworkspace/xcuserdata/cristianbaluta.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristibaluta/ConsentKit/HEAD/demo-ios/Gdpr.xcodeproj/project.xcworkspace/xcuserdata/cristianbaluta.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 21/05/2018. 6 | // Copyright © 2018 Cristian Baluta. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UITabBarController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ConsentKitCellProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKitCellProtocol.swift 3 | // 4 | // Created by Cristian Baluta on 28/05/2018. 5 | // Copyright © 2018 Imagin soft. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ConsentKitCellProtocol { 11 | var title: String {get set} 12 | var subtitle: String {get set} 13 | var value: Bool {get set} 14 | var valueDidChange: ((Bool) -> Void)? {get set} 15 | } 16 | -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/xcuserdata/cristi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Gdpr.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/xcuserdata/cristianbaluta.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Gdpr.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Consent.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'ConsentKit' 3 | s.version = '0.9' 4 | s.summary = 'Keep track of GDPR consents in your app.' 5 | s.description = <<-DESC 6 | Keep track of GDPR consents in your app. 7 | DESC 8 | s.module_name = "ConsentKit" 9 | s.homepage = 'https://github.com/ralcr/Consent' 10 | s.license = 'MIT' 11 | s.authors = { 'Cristian Baluta' => 'cristi.baluta@gmail.com' } 12 | s.ios.deployment_target = '10.0' 13 | s.source = { :git => 'https://github.com/ralcr/Consent', :tag => s.version } 14 | s.source_files = 'src/*.{h,swift}' 15 | end 16 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/InMemoryDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InMemorydataSource.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 23/05/2018. 6 | // Copyright © 2018 Imagin soft. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class InMemoryDataSource: ConsentKitDataSource { 12 | 13 | private var dict = [String: Bool?]() 14 | 15 | func isAccepted (_ item: ConsentKitItem) -> Bool { 16 | return dict[item.rawValue] == true 17 | } 18 | 19 | func isReviewed (_ item: ConsentKitItem) -> Bool { 20 | return dict[item.rawValue] != nil 21 | } 22 | 23 | func setAccepted (_ value: Bool, for item: ConsentKitItem) { 24 | dict[item.rawValue] = value 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/SimpleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleViewController.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 22/05/2018. 6 | // Copyright © 2018 Imagin soft. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SimpleViewController: UIViewController { 12 | 13 | @IBOutlet var label: UILabel! 14 | 15 | override func viewDidAppear(_ animated: Bool) { 16 | super.viewDidAppear(animated) 17 | 18 | if gdpr.needsReviewing([Services.icloud, Services.analytics, ConsentKitServices.location]) { 19 | label.text = "Gdpr needs reviewing!" 20 | } else { 21 | label.text = "All gdpr items already reviewed" 22 | } 23 | } 24 | 25 | @IBAction func handleGdpr() { 26 | 27 | let vc = ConsentKitViewController() 28 | vc.items = [Services.icloud, Services.analytics, ConsentKitServices.location] 29 | self.present(vc, animated: true) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Cristian Baluta 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 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/NavigationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationViewController.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 23/05/2018. 6 | // Copyright © 2018 Imagin soft. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NavigationViewController: UIViewController { 12 | 13 | @IBOutlet var label: UILabel! 14 | 15 | override func viewDidAppear(_ animated: Bool) { 16 | super.viewDidAppear(animated) 17 | 18 | if gdpr.needsReviewing([Services.icloud, Services.analytics, ConsentKitServices.location]) { 19 | label.text = "Gdpr needs reviewing!" 20 | } else { 21 | label.text = "All gdpr items already reviewed" 22 | } 23 | } 24 | 25 | @IBAction func handleGdpr() { 26 | 27 | let vc = ConsentKitViewController() 28 | vc.items = [Services.icloud, Services.analytics, ConsentKitServices.location] 29 | vc.didFinishReview = { 30 | self.navigationController?.popViewController(animated: true) 31 | } 32 | self.navigationController?.pushViewController(vc, animated: true) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ConsentKitCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKitCell.swift 3 | // 4 | // Created by Cristian Baluta on 21/05/2018. 5 | // Copyright © 2018 Cristian Baluta. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class ConsentKitCell: UITableViewCell, ConsentKitCellProtocol { 11 | 12 | @IBOutlet private var titleLabel: UILabel! 13 | @IBOutlet private var subtitleLabel: UILabel! 14 | @IBOutlet private var switchButton: UISwitch! 15 | 16 | var title: String = "" { 17 | didSet { 18 | titleLabel.text = title 19 | } 20 | } 21 | var subtitle: String = "" { 22 | didSet { 23 | subtitleLabel.text = subtitle 24 | } 25 | } 26 | var value: Bool = false { 27 | didSet { 28 | switchButton.isOn = value 29 | } 30 | } 31 | var valueDidChange: ((Bool) -> Void)? 32 | 33 | override func awakeFromNib() { 34 | super.awakeFromNib() 35 | switchButton.onTintColor = UIColor.orange 36 | } 37 | 38 | static func instantiateFromXib() -> ConsentKitCell { 39 | let arrNib: Array = Bundle.main.loadNibNamed("ConsentKitCell", owner: self, options: nil)! 40 | return arrNib.first as! ConsentKitCell 41 | } 42 | 43 | @IBAction func switchChanged(_ sender: UISwitch) { 44 | valueDidChange?(sender.isOn) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ConsentKitUserDefaultsDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKitUserDefaultsDataSource.swift 3 | // 4 | // Created by Cristian Baluta on 23/05/2018. 5 | // Copyright © 2018 Imagin soft. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | class ConsentKitUserDefaultsDataSource { 11 | 12 | fileprivate let prefix = "ConsentKit-" 13 | fileprivate let userDefaults = UserDefaults.standard 14 | 15 | 16 | fileprivate func get (_ key: String) -> Any? { 17 | return userDefaults.object(forKey: prefix + key) 18 | } 19 | 20 | fileprivate func set (_ value: Bool?, forKey key: String) { 21 | if let v = value { 22 | userDefaults.set(v, forKey: prefix + key) 23 | } else { 24 | userDefaults.removeObject(forKey: prefix + key) 25 | } 26 | userDefaults.synchronize() 27 | } 28 | } 29 | 30 | extension ConsentKitUserDefaultsDataSource: ConsentKitDataSource { 31 | 32 | func isAccepted (_ item: ConsentKitItem) -> Bool { 33 | return (get(item.rawValue) as? Bool) == true 34 | } 35 | 36 | func isReviewed (_ item: ConsentKitItem) -> Bool { 37 | return get(item.rawValue) != nil 38 | } 39 | 40 | func setAccepted (_ value: Bool, for item: ConsentKitItem) { 41 | set(value, forKey: item.rawValue) 42 | } 43 | 44 | func reset (_ item: ConsentKitItem) { 45 | set(nil, forKey: item.rawValue) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/CloudKitViewControllerHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CloudKitViewControllerHeader.swift 3 | // 4 | // Created by Cristian Baluta on 21/05/2018. 5 | // Copyright © 2018 Cristian Baluta. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class CloudKitViewControllerHeader: UIView { 11 | 12 | var didDone: (() -> Void)? 13 | private let padding = CGFloat(16) 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | 18 | self.backgroundColor = UIColor(white: 0.9, alpha: 1.0) 19 | 20 | let label = UILabel() 21 | label.text = "Review services!" 22 | label.font = UIFont.boldSystemFont(ofSize: 16) 23 | label.sizeToFit() 24 | label.center = CGPoint(x: label.frame.size.width/2 + padding, y: 30) 25 | self.addSubview(label) 26 | 27 | let button = UIButton() 28 | button.setTitle("Done", for: .normal) 29 | button.setTitleColor(UIColor.orange, for: .normal) 30 | button.addTarget(self, action: #selector(handleButton), for: .touchUpInside) 31 | button.sizeToFit() 32 | button.center = CGPoint(x: frame.size.width - button.frame.size.width/2 - padding, y: 30) 33 | self.addSubview(button) 34 | } 35 | 36 | required init?(coder aDecoder: NSCoder) { 37 | fatalError("init(coder:) has not been implemented") 38 | } 39 | 40 | @objc func handleButton() { 41 | didDone?() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ConsentKitServices.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKitServices.swift 3 | // 4 | // Created by Cristian Baluta on 23/05/2018. 5 | // Copyright © 2018 Imagin soft. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ConsentKitServices: String, ConsentKitItem { 11 | 12 | case icloud = "ck-icloud" 13 | case analytics = "ck-analytics" 14 | case location = "ck-location" 15 | 16 | func title() -> String { 17 | switch self { 18 | case .icloud: return "iCloud" 19 | case .analytics: return "Google analytics" 20 | case .location: return "Location" 21 | } 22 | } 23 | 24 | func description() -> String { 25 | switch self { 26 | case .icloud: return "Store data to the Apple's iCloud. This will allow the data to by synced across all your devices." 27 | case .analytics: return "Help developer understand how the app is used by sharing usage with Google analytics." 28 | case .location: return "Store location on server" 29 | } 30 | } 31 | 32 | func alertMessage() -> String? { 33 | switch self { 34 | case .icloud: return "I accept that this app will store my data in the Apple's iCloud for the purpose of syncing it across multiple devices and backup" 35 | case .analytics: return "I accept that this app will store anonymous usage data in Google analytics for the purpose of improving the app" 36 | case .location: return "I accept to store my location on the server" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 04/06/2018. 6 | // Copyright © 2018 Imagin soft. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TableViewController: UITableViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Uncomment the following line to preserve selection between presentations 17 | // self.clearsSelectionOnViewWillAppear = false 18 | } 19 | 20 | // MARK: - Table view data source 21 | 22 | override func numberOfSections(in tableView: UITableView) -> Int { 23 | return 3 24 | } 25 | 26 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 27 | // #warning Incomplete implementation, return the number of rows 28 | return section == 1 ? 1 : 2 29 | } 30 | 31 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 32 | 33 | if indexPath.section == 1 { 34 | let cell = UITableViewCell(style: .default, reuseIdentifier: nil) 35 | return cell 36 | } else { 37 | let cell = UITableViewCell(style: .default, reuseIdentifier: nil) 38 | cell.textLabel?.text = "IndePath \(indexPath)" 39 | return cell 40 | } 41 | } 42 | 43 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 44 | switch section { 45 | case 0: 46 | return "Section 1" 47 | case 1: 48 | return "GDPR section" 49 | default: 50 | return "Section 3" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 21/05/2018. 6 | // Copyright © 2018 Cristian Baluta. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum Services: String, ConsentKitItem { 12 | 13 | case icloud = "iCloud" 14 | case analytics = "analytics" 15 | 16 | func title() -> String { 17 | switch self { 18 | case .icloud: return "iCloud" 19 | case .analytics: return "Google analytics" 20 | } 21 | } 22 | 23 | func description() -> String { 24 | switch self { 25 | case .icloud: return "Store data to the Apple's iCloud. This will allow the data to by synced across all your devices." 26 | case .analytics: return "Help developer understand how the app is used by sharing usage with Google analytics." 27 | } 28 | } 29 | 30 | func alertMessage() -> String? { 31 | switch self { 32 | case .icloud: return nil 33 | case .analytics: return nil 34 | } 35 | } 36 | } 37 | 38 | let gdpr = ConsentKit() 39 | //let gdpr = ConsentKit(dataSource: InMemoryDataSource()) 40 | 41 | @UIApplicationMain 42 | class AppDelegate: UIResponder, UIApplicationDelegate { 43 | 44 | var window: UIWindow? 45 | 46 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 47 | // Override point for customization after application launch. 48 | // For testing purposes we reset all values from previous run 49 | gdpr.reset(Services.icloud) 50 | gdpr.reset(Services.analytics) 51 | gdpr.reset(ConsentKitServices.location) 52 | 53 | return true 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConsentKit 2 | Note: This library only helps you collect consents for the services in your app, actually disabling those services is developer's job. 3 | 4 | ![Screenshot](https://image.ibb.co/iuASQy/Screen_Shot_2018_05_28_at_23_07_10.png) 5 | 6 | ## Usage 7 | ### Define the services in your app 8 | Note: Some generic services can be found in ConsentKitServices 9 | 10 | ```swift 11 | enum Services: String, ConsentKitItem { 12 | 13 | case icloud = "iCloud" 14 | case analytics = "analytics" 15 | 16 | func title() -> String { 17 | switch self { 18 | case .icloud: return "iCloud" 19 | case .analytics: return "Google analytics" 20 | } 21 | } 22 | func description() -> String { 23 | switch self { 24 | case .icloud: return "Wether to accept iCloud or not" 25 | case .analytics: return "Google analytics" 26 | } 27 | } 28 | func alertMessage() -> String? { 29 | switch self { 30 | case .icloud: return nil 31 | case .analytics: return "I accept this app to store anonymous analytics in Google Analytics!" 32 | } 33 | } 34 | } 35 | ``` 36 | 37 | ### Instantiate the lib, preferably once in the AppDelegate, but can be anywhere and as many times as you like. 38 | 39 | ```swift 40 | let gdpr = ConsentKit() 41 | ``` 42 | 43 | ### Check if you have missing consents 44 | If yes, add the default ConsentKitViewController to handle all the switches for you. 45 | 46 | ```swift 47 | if gdpr.needsReviewing([Services.icloud, Services.analytics]) { 48 | 49 | let vc = ConsentKitViewController() 50 | vc.items = [Services.icloud, Services.analytics, ConsentKitServices.location] 51 | self.present(vc, animated: true) 52 | } 53 | ``` 54 | 55 | ### Custom storage 56 | By default ConsentKit keeps values in UserDefaults, but you can change that with the gdpr.dataSource property. Just assign or pass through constructor a class implementing the ConsentKitDataSource 57 | 58 | ```swift 59 | let gdpr = ConsentKit(dataSource: InMemoryDataSource())// InMemoryDataSource implements ConsentKitDataSource 60 | 61 | protocol ConsentKitDataSource { 62 | func isAccepted (_ item: ConsentKitItem) -> Bool 63 | func isReviewed (_ item: ConsentKitItem) -> Bool 64 | func setAccepted (_ value: Bool, for item: ConsentKitItem) 65 | func reset (_ item: ConsentKitItem)// Optional 66 | } 67 | ``` 68 | 69 | ## Contribution 70 | Help me create a complete list of services that the developers can use. 71 | Thanks. 72 | -------------------------------------------------------------------------------- /src/ConsentKitTableViewDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKitTableViewDataSource.swift 3 | // Gdpr 4 | // 5 | // Created by Cristian Baluta on 05/06/2018. 6 | // Copyright © 2018 Imagin soft. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ConsentKitTableViewDataSource: NSObject, UITableViewDelegate, UITableViewDataSource { 12 | 13 | var items: [ConsentKitItem] = [] 14 | 15 | func numberOfSections(in tableView: UITableView) -> Int { 16 | return 1 17 | } 18 | 19 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 20 | return items.count 21 | } 22 | 23 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 24 | 25 | let item = items[indexPath.row] 26 | var cell: ConsentKitCellProtocol = ConsentKitCell.instantiateFromXib() 27 | cell.title = item.title() 28 | cell.subtitle = item.description() 29 | cell.value = gdpr.isAccepted(item) 30 | cell.valueDidChange = { isOn in 31 | self.item(item, didChangeValue: isOn, in: cell) 32 | } 33 | 34 | return cell as! ConsentKitCell 35 | } 36 | 37 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 38 | tableView.deselectRow(at: indexPath, animated: true) 39 | } 40 | 41 | func item(_ item: ConsentKitItem, didChangeValue value: Bool, in cell: ConsentKitCellProtocol) { 42 | 43 | if value { 44 | guard let message = item.alertMessage() else { 45 | self.gdpr.setAccepted(true, for: item) 46 | self.didAccept?(item) 47 | return 48 | } 49 | let alert = UIAlertController(title: item.title(), message: message, preferredStyle: .alert) 50 | alert.addAction( 51 | UIAlertAction(title: "Accept", style: .default, handler: { _ in 52 | self.gdpr.setAccepted(true, for: item) 53 | self.didAccept?(item) 54 | }) 55 | ) 56 | alert.addAction( 57 | UIAlertAction(title: "Decline", style: .cancel, handler: { _ in 58 | var cell = cell 59 | cell.value = false 60 | self.gdpr.setAccepted(false, for: item) 61 | self.didReject?(item) 62 | }) 63 | ) 64 | self.present(alert, animated: true, completion: nil) 65 | } else { 66 | self.gdpr.setAccepted(false, for: item) 67 | didReject?(item) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/ConsentKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKit.swift 3 | // 4 | // Created by Cristian Baluta on 21/05/2018. 5 | // Copyright © 2018 Imagin soft. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ConsentKitItem { 11 | var rawValue: String {get} 12 | /// Title used in the cell and alert 13 | func title() -> String 14 | /// Description used in the cell 15 | func description() -> String 16 | /// Message displayed in an alert when the switch is turned on. 17 | func alertMessage() -> String? 18 | } 19 | 20 | protocol ConsentKitDataSource { 21 | /// Returns if the item has a value of true 22 | func isAccepted (_ item: ConsentKitItem) -> Bool 23 | /// Returns if the item's value is different than nil, which means it was reviewed 24 | func isReviewed (_ item: ConsentKitItem) -> Bool 25 | /// Set a value for the item 26 | func setAccepted (_ value: Bool, for item: ConsentKitItem) 27 | /// Resets the value of an item, which means that isReviewed will return false after that 28 | func reset (_ item: ConsentKitItem) 29 | } 30 | 31 | extension ConsentKitDataSource { 32 | func reset (_ item: ConsentKitItem) { 33 | // This is a empty implementation to allow this method to be optional 34 | } 35 | } 36 | 37 | class ConsentKit { 38 | 39 | /// Assign a custom dataSource. By default UserDefaults is used and each key is prefixed with "ConsentKit-" 40 | var dataSource: ConsentKitDataSource = ConsentKitUserDefaultsDataSource() 41 | 42 | convenience init (dataSource: ConsentKitDataSource) { 43 | self.init() 44 | self.dataSource = dataSource 45 | } 46 | 47 | /// Returns if there's any value that was not reviewed 48 | /// Used to check if the ConsentKitViewController should be presented or not 49 | /// This will also be useful to present the controller if any new service added later in the app needs reviewing 50 | func needsReviewing(_ items: [ConsentKitItem]) -> Bool { 51 | for item in items { 52 | if !isReviewed(item) { 53 | return true 54 | } 55 | } 56 | return false 57 | } 58 | } 59 | 60 | extension ConsentKit: ConsentKitDataSource { 61 | 62 | func isAccepted (_ item: ConsentKitItem) -> Bool { 63 | return dataSource.isAccepted(item) 64 | } 65 | 66 | func isReviewed (_ item: ConsentKitItem) -> Bool { 67 | return dataSource.isReviewed(item) 68 | } 69 | 70 | func setAccepted (_ value: Bool, for item: ConsentKitItem) { 71 | dataSource.setAccepted(value, for: item) 72 | } 73 | 74 | func reset (_ item: ConsentKitItem) { 75 | dataSource.reset(item) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/ConsentKitViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsentKitViewController.swift 3 | // 4 | // Created by Cristian Baluta on 21/05/2018. 5 | // Copyright © 2018 Cristian Baluta. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | class ConsentKitViewController: UITableViewController { 11 | 12 | var didAccept: ((ConsentKitItem) -> Void)? 13 | var didReject: ((ConsentKitItem) -> Void)? 14 | var didFinishReview: (() -> Void)? 15 | var items: [ConsentKitItem] = [] { 16 | didSet { 17 | tableView.reloadData() 18 | } 19 | } 20 | fileprivate let gdpr = ConsentKit() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | tableView.tableFooterView = UIView() 26 | } 27 | 28 | override func viewWillAppear(_ animated: Bool) { 29 | super.viewWillAppear(animated) 30 | 31 | if self.navigationController == nil { 32 | // Add a custom header only if the VC is not pushed into a navigationController 33 | let header = CloudKitViewControllerHeader(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 80)) 34 | header.didDone = { 35 | self.handleDone() 36 | self.dismiss(animated: true, completion: nil) 37 | } 38 | tableView.tableHeaderView = header 39 | } else { 40 | self.title = "Review services!" 41 | self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(handleDone)) 42 | } 43 | } 44 | 45 | // MARK: - Table view data source 46 | 47 | override func numberOfSections(in tableView: UITableView) -> Int { 48 | return 1 49 | } 50 | 51 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 52 | return items.count 53 | } 54 | 55 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 56 | 57 | let item = items[indexPath.row] 58 | var cell: ConsentKitCellProtocol = ConsentKitCell.instantiateFromXib() 59 | cell.title = item.title() 60 | cell.subtitle = item.description() 61 | cell.value = gdpr.isAccepted(item) 62 | cell.valueDidChange = { isOn in 63 | self.item(item, didChangeValue: isOn, in: cell) 64 | } 65 | 66 | return cell as! ConsentKitCell 67 | } 68 | 69 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 70 | tableView.deselectRow(at: indexPath, animated: true) 71 | } 72 | 73 | // MARK: - Switch changed 74 | 75 | func item(_ item: ConsentKitItem, didChangeValue value: Bool, in cell: ConsentKitCellProtocol) { 76 | 77 | if value { 78 | guard let message = item.alertMessage() else { 79 | self.gdpr.setAccepted(true, for: item) 80 | self.didAccept?(item) 81 | return 82 | } 83 | let alert = UIAlertController(title: item.title(), message: message, preferredStyle: .alert) 84 | alert.addAction( 85 | UIAlertAction(title: "Accept", style: .default, handler: { _ in 86 | self.gdpr.setAccepted(true, for: item) 87 | self.didAccept?(item) 88 | }) 89 | ) 90 | alert.addAction( 91 | UIAlertAction(title: "Decline", style: .cancel, handler: { _ in 92 | var cell = cell 93 | cell.value = false 94 | self.gdpr.setAccepted(false, for: item) 95 | self.didReject?(item) 96 | }) 97 | ) 98 | self.present(alert, animated: true, completion: nil) 99 | } else { 100 | self.gdpr.setAccepted(false, for: item) 101 | didReject?(item) 102 | } 103 | } 104 | 105 | @objc func handleDone() { 106 | // Set to false the untouched switches, to prevent gdpr being called again 107 | for item in items { 108 | if !gdpr.isReviewed(item) { 109 | gdpr.setAccepted(false, for: item) 110 | } 111 | } 112 | didFinishReview?() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/ConsentKitCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /demo-ios/Gdpr/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 116 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /demo-ios/Gdpr.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 283DB05C20BADECD00A8BC14 /* ConsentKitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 283DB05B20BADECD00A8BC14 /* ConsentKitCell.xib */; }; 11 | 283DB05E20BC77D200A8BC14 /* ConsentKitCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283DB05D20BC77D200A8BC14 /* ConsentKitCellProtocol.swift */; }; 12 | 2845157D20B3528B009F3EE1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845157C20B3528B009F3EE1 /* AppDelegate.swift */; }; 13 | 2845157F20B3528B009F3EE1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845157E20B3528B009F3EE1 /* ViewController.swift */; }; 14 | 2845158220B3528B009F3EE1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2845158020B3528B009F3EE1 /* Main.storyboard */; }; 15 | 2845158420B3528F009F3EE1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2845158320B3528F009F3EE1 /* Assets.xcassets */; }; 16 | 2845158720B3528F009F3EE1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2845158520B3528F009F3EE1 /* LaunchScreen.storyboard */; }; 17 | 2845159320B352F4009F3EE1 /* CloudKitViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845158F20B352F4009F3EE1 /* CloudKitViewControllerHeader.swift */; }; 18 | 2845159420B352F4009F3EE1 /* ConsentKitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159020B352F4009F3EE1 /* ConsentKitCell.swift */; }; 19 | 2845159520B352F4009F3EE1 /* ConsentKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159120B352F4009F3EE1 /* ConsentKit.swift */; }; 20 | 2845159620B352F4009F3EE1 /* ConsentKitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159220B352F4009F3EE1 /* ConsentKitViewController.swift */; }; 21 | 2845159820B4BB95009F3EE1 /* SimpleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159720B4BB95009F3EE1 /* SimpleViewController.swift */; }; 22 | 2845159A20B4BDBC009F3EE1 /* NavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159920B4BDBC009F3EE1 /* NavigationViewController.swift */; }; 23 | 2845159C20B5EA1C009F3EE1 /* InMemoryDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159B20B5EA1C009F3EE1 /* InMemoryDataSource.swift */; }; 24 | 2845159E20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159D20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift */; }; 25 | 284515A020B5F738009F3EE1 /* ConsentKitServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159F20B5F738009F3EE1 /* ConsentKitServices.swift */; }; 26 | 5684412C20C5A4DB00D9A471 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5684412B20C5A4DB00D9A471 /* TableViewController.swift */; }; 27 | 5684413220C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5684413120C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 283DB05B20BADECD00A8BC14 /* ConsentKitCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConsentKitCell.xib; sourceTree = ""; }; 32 | 283DB05D20BC77D200A8BC14 /* ConsentKitCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitCellProtocol.swift; sourceTree = ""; }; 33 | 2845157920B3528B009F3EE1 /* Gdpr.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gdpr.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 2845157C20B3528B009F3EE1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 2845157E20B3528B009F3EE1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | 2845158120B3528B009F3EE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | 2845158320B3528F009F3EE1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | 2845158620B3528F009F3EE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 39 | 2845158820B3528F009F3EE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 2845158F20B352F4009F3EE1 /* CloudKitViewControllerHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitViewControllerHeader.swift; sourceTree = ""; }; 41 | 2845159020B352F4009F3EE1 /* ConsentKitCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentKitCell.swift; sourceTree = ""; }; 42 | 2845159120B352F4009F3EE1 /* ConsentKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentKit.swift; sourceTree = ""; }; 43 | 2845159220B352F4009F3EE1 /* ConsentKitViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentKitViewController.swift; sourceTree = ""; }; 44 | 2845159720B4BB95009F3EE1 /* SimpleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleViewController.swift; sourceTree = ""; }; 45 | 2845159920B4BDBC009F3EE1 /* NavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewController.swift; sourceTree = ""; }; 46 | 2845159B20B5EA1C009F3EE1 /* InMemoryDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryDataSource.swift; sourceTree = ""; }; 47 | 2845159D20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitUserDefaultsDataSource.swift; sourceTree = ""; }; 48 | 2845159F20B5F738009F3EE1 /* ConsentKitServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitServices.swift; sourceTree = ""; }; 49 | 5684412B20C5A4DB00D9A471 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 50 | 5684413120C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitTableViewDataSource.swift; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 2845157620B3528B009F3EE1 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 2845157020B3528B009F3EE1 = { 65 | isa = PBXGroup; 66 | children = ( 67 | 2845157B20B3528B009F3EE1 /* Gdpr */, 68 | 2845157A20B3528B009F3EE1 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 2845157A20B3528B009F3EE1 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 2845157920B3528B009F3EE1 /* Gdpr.app */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 2845157B20B3528B009F3EE1 /* Gdpr */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 2845157C20B3528B009F3EE1 /* AppDelegate.swift */, 84 | 2845157E20B3528B009F3EE1 /* ViewController.swift */, 85 | 2845159720B4BB95009F3EE1 /* SimpleViewController.swift */, 86 | 2845159920B4BDBC009F3EE1 /* NavigationViewController.swift */, 87 | 5684412B20C5A4DB00D9A471 /* TableViewController.swift */, 88 | 2845159B20B5EA1C009F3EE1 /* InMemoryDataSource.swift */, 89 | 2845158020B3528B009F3EE1 /* Main.storyboard */, 90 | 2845158320B3528F009F3EE1 /* Assets.xcassets */, 91 | 2845158520B3528F009F3EE1 /* LaunchScreen.storyboard */, 92 | 2845158820B3528F009F3EE1 /* Info.plist */, 93 | 2845158E20B352F4009F3EE1 /* src */, 94 | ); 95 | path = Gdpr; 96 | sourceTree = ""; 97 | }; 98 | 2845158E20B352F4009F3EE1 /* src */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 2845159120B352F4009F3EE1 /* ConsentKit.swift */, 102 | 2845159F20B5F738009F3EE1 /* ConsentKitServices.swift */, 103 | 2845159D20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift */, 104 | 2845159220B352F4009F3EE1 /* ConsentKitViewController.swift */, 105 | 5684413120C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift */, 106 | 283DB05D20BC77D200A8BC14 /* ConsentKitCellProtocol.swift */, 107 | 2845159020B352F4009F3EE1 /* ConsentKitCell.swift */, 108 | 283DB05B20BADECD00A8BC14 /* ConsentKitCell.xib */, 109 | 2845158F20B352F4009F3EE1 /* CloudKitViewControllerHeader.swift */, 110 | ); 111 | name = src; 112 | path = ../../src; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 2845157820B3528B009F3EE1 /* Gdpr */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 2845158B20B3528F009F3EE1 /* Build configuration list for PBXNativeTarget "Gdpr" */; 121 | buildPhases = ( 122 | 2845157520B3528B009F3EE1 /* Sources */, 123 | 2845157620B3528B009F3EE1 /* Frameworks */, 124 | 2845157720B3528B009F3EE1 /* Resources */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = Gdpr; 131 | productName = Gdpr; 132 | productReference = 2845157920B3528B009F3EE1 /* Gdpr.app */; 133 | productType = "com.apple.product-type.application"; 134 | }; 135 | /* End PBXNativeTarget section */ 136 | 137 | /* Begin PBXProject section */ 138 | 2845157120B3528B009F3EE1 /* Project object */ = { 139 | isa = PBXProject; 140 | attributes = { 141 | LastSwiftUpdateCheck = 0930; 142 | LastUpgradeCheck = 0930; 143 | ORGANIZATIONNAME = "Imagin soft"; 144 | TargetAttributes = { 145 | 2845157820B3528B009F3EE1 = { 146 | CreatedOnToolsVersion = 9.3; 147 | }; 148 | }; 149 | }; 150 | buildConfigurationList = 2845157420B3528B009F3EE1 /* Build configuration list for PBXProject "Gdpr" */; 151 | compatibilityVersion = "Xcode 9.3"; 152 | developmentRegion = en; 153 | hasScannedForEncodings = 0; 154 | knownRegions = ( 155 | en, 156 | Base, 157 | ); 158 | mainGroup = 2845157020B3528B009F3EE1; 159 | productRefGroup = 2845157A20B3528B009F3EE1 /* Products */; 160 | projectDirPath = ""; 161 | projectRoot = ""; 162 | targets = ( 163 | 2845157820B3528B009F3EE1 /* Gdpr */, 164 | ); 165 | }; 166 | /* End PBXProject section */ 167 | 168 | /* Begin PBXResourcesBuildPhase section */ 169 | 2845157720B3528B009F3EE1 /* Resources */ = { 170 | isa = PBXResourcesBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | 2845158720B3528F009F3EE1 /* LaunchScreen.storyboard in Resources */, 174 | 283DB05C20BADECD00A8BC14 /* ConsentKitCell.xib in Resources */, 175 | 2845158420B3528F009F3EE1 /* Assets.xcassets in Resources */, 176 | 2845158220B3528B009F3EE1 /* Main.storyboard in Resources */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | /* End PBXResourcesBuildPhase section */ 181 | 182 | /* Begin PBXSourcesBuildPhase section */ 183 | 2845157520B3528B009F3EE1 /* Sources */ = { 184 | isa = PBXSourcesBuildPhase; 185 | buildActionMask = 2147483647; 186 | files = ( 187 | 2845159A20B4BDBC009F3EE1 /* NavigationViewController.swift in Sources */, 188 | 2845159E20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift in Sources */, 189 | 5684412C20C5A4DB00D9A471 /* TableViewController.swift in Sources */, 190 | 284515A020B5F738009F3EE1 /* ConsentKitServices.swift in Sources */, 191 | 2845157F20B3528B009F3EE1 /* ViewController.swift in Sources */, 192 | 2845159C20B5EA1C009F3EE1 /* InMemoryDataSource.swift in Sources */, 193 | 2845159420B352F4009F3EE1 /* ConsentKitCell.swift in Sources */, 194 | 2845159620B352F4009F3EE1 /* ConsentKitViewController.swift in Sources */, 195 | 5684413220C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift in Sources */, 196 | 283DB05E20BC77D200A8BC14 /* ConsentKitCellProtocol.swift in Sources */, 197 | 2845157D20B3528B009F3EE1 /* AppDelegate.swift in Sources */, 198 | 2845159320B352F4009F3EE1 /* CloudKitViewControllerHeader.swift in Sources */, 199 | 2845159820B4BB95009F3EE1 /* SimpleViewController.swift in Sources */, 200 | 2845159520B352F4009F3EE1 /* ConsentKit.swift in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin PBXVariantGroup section */ 207 | 2845158020B3528B009F3EE1 /* Main.storyboard */ = { 208 | isa = PBXVariantGroup; 209 | children = ( 210 | 2845158120B3528B009F3EE1 /* Base */, 211 | ); 212 | name = Main.storyboard; 213 | sourceTree = ""; 214 | }; 215 | 2845158520B3528F009F3EE1 /* LaunchScreen.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 2845158620B3528F009F3EE1 /* Base */, 219 | ); 220 | name = LaunchScreen.storyboard; 221 | sourceTree = ""; 222 | }; 223 | /* End PBXVariantGroup section */ 224 | 225 | /* Begin XCBuildConfiguration section */ 226 | 2845158920B3528F009F3EE1 /* Debug */ = { 227 | isa = XCBuildConfiguration; 228 | buildSettings = { 229 | ALWAYS_SEARCH_USER_PATHS = NO; 230 | CLANG_ANALYZER_NONNULL = YES; 231 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 233 | CLANG_CXX_LIBRARY = "libc++"; 234 | CLANG_ENABLE_MODULES = YES; 235 | CLANG_ENABLE_OBJC_ARC = YES; 236 | CLANG_ENABLE_OBJC_WEAK = YES; 237 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_COMMA = YES; 240 | CLANG_WARN_CONSTANT_CONVERSION = YES; 241 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 242 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 243 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 244 | CLANG_WARN_EMPTY_BODY = YES; 245 | CLANG_WARN_ENUM_CONVERSION = YES; 246 | CLANG_WARN_INFINITE_RECURSION = YES; 247 | CLANG_WARN_INT_CONVERSION = YES; 248 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 249 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 250 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 252 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 253 | CLANG_WARN_STRICT_PROTOTYPES = YES; 254 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 255 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 256 | CLANG_WARN_UNREACHABLE_CODE = YES; 257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 258 | CODE_SIGN_IDENTITY = "iPhone Developer"; 259 | COPY_PHASE_STRIP = NO; 260 | DEBUG_INFORMATION_FORMAT = dwarf; 261 | ENABLE_STRICT_OBJC_MSGSEND = YES; 262 | ENABLE_TESTABILITY = YES; 263 | GCC_C_LANGUAGE_STANDARD = gnu11; 264 | GCC_DYNAMIC_NO_PIC = NO; 265 | GCC_NO_COMMON_BLOCKS = YES; 266 | GCC_OPTIMIZATION_LEVEL = 0; 267 | GCC_PREPROCESSOR_DEFINITIONS = ( 268 | "DEBUG=1", 269 | "$(inherited)", 270 | ); 271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 278 | MTL_ENABLE_DEBUG_INFO = YES; 279 | ONLY_ACTIVE_ARCH = YES; 280 | SDKROOT = iphoneos; 281 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 282 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 283 | }; 284 | name = Debug; 285 | }; 286 | 2845158A20B3528F009F3EE1 /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_ANALYZER_NONNULL = YES; 291 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_ENABLE_MODULES = YES; 295 | CLANG_ENABLE_OBJC_ARC = YES; 296 | CLANG_ENABLE_OBJC_WEAK = YES; 297 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_COMMA = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INFINITE_RECURSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 310 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 311 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 312 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 313 | CLANG_WARN_STRICT_PROTOTYPES = YES; 314 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 315 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | CODE_SIGN_IDENTITY = "iPhone Developer"; 319 | COPY_PHASE_STRIP = NO; 320 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 321 | ENABLE_NS_ASSERTIONS = NO; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu11; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 332 | MTL_ENABLE_DEBUG_INFO = NO; 333 | SDKROOT = iphoneos; 334 | SWIFT_COMPILATION_MODE = wholemodule; 335 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 336 | VALIDATE_PRODUCT = YES; 337 | }; 338 | name = Release; 339 | }; 340 | 2845158C20B3528F009F3EE1 /* Debug */ = { 341 | isa = XCBuildConfiguration; 342 | buildSettings = { 343 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 344 | CODE_SIGN_STYLE = Automatic; 345 | DEVELOPMENT_TEAM = 5NHDC5EV44; 346 | INFOPLIST_FILE = Gdpr/Info.plist; 347 | LD_RUNPATH_SEARCH_PATHS = ( 348 | "$(inherited)", 349 | "@executable_path/Frameworks", 350 | ); 351 | PRODUCT_BUNDLE_IDENTIFIER = ro.imagin.Gdpr; 352 | PRODUCT_NAME = "$(TARGET_NAME)"; 353 | SWIFT_VERSION = 4.0; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | }; 356 | name = Debug; 357 | }; 358 | 2845158D20B3528F009F3EE1 /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 362 | CODE_SIGN_STYLE = Automatic; 363 | DEVELOPMENT_TEAM = 5NHDC5EV44; 364 | INFOPLIST_FILE = Gdpr/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = ( 366 | "$(inherited)", 367 | "@executable_path/Frameworks", 368 | ); 369 | PRODUCT_BUNDLE_IDENTIFIER = ro.imagin.Gdpr; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | SWIFT_VERSION = 4.0; 372 | TARGETED_DEVICE_FAMILY = "1,2"; 373 | }; 374 | name = Release; 375 | }; 376 | /* End XCBuildConfiguration section */ 377 | 378 | /* Begin XCConfigurationList section */ 379 | 2845157420B3528B009F3EE1 /* Build configuration list for PBXProject "Gdpr" */ = { 380 | isa = XCConfigurationList; 381 | buildConfigurations = ( 382 | 2845158920B3528F009F3EE1 /* Debug */, 383 | 2845158A20B3528F009F3EE1 /* Release */, 384 | ); 385 | defaultConfigurationIsVisible = 0; 386 | defaultConfigurationName = Release; 387 | }; 388 | 2845158B20B3528F009F3EE1 /* Build configuration list for PBXNativeTarget "Gdpr" */ = { 389 | isa = XCConfigurationList; 390 | buildConfigurations = ( 391 | 2845158C20B3528F009F3EE1 /* Debug */, 392 | 2845158D20B3528F009F3EE1 /* Release */, 393 | ); 394 | defaultConfigurationIsVisible = 0; 395 | defaultConfigurationName = Release; 396 | }; 397 | /* End XCConfigurationList section */ 398 | }; 399 | rootObject = 2845157120B3528B009F3EE1 /* Project object */; 400 | } 401 | --------------------------------------------------------------------------------