Repository: ralcr/ConsentKit
Branch: master
Commit: e196c4c3b145
Files: 31
Total size: 66.2 KB
Directory structure:
gitextract_6bh629f5/
├── Consent.podspec
├── LICENSE
├── README.md
├── demo-ios/
│ ├── Gdpr/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── InMemoryDataSource.swift
│ │ ├── Info.plist
│ │ ├── NavigationViewController.swift
│ │ ├── SimpleViewController.swift
│ │ ├── TableViewController.swift
│ │ └── ViewController.swift
│ └── Gdpr.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata/
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata/
│ │ ├── cristi.xcuserdatad/
│ │ │ └── UserInterfaceState.xcuserstate
│ │ └── cristianbaluta.xcuserdatad/
│ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata/
│ ├── cristi.xcuserdatad/
│ │ ├── xcdebugger/
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes/
│ │ └── xcschememanagement.plist
│ └── cristianbaluta.xcuserdatad/
│ └── xcschemes/
│ └── xcschememanagement.plist
└── src/
├── CloudKitViewControllerHeader.swift
├── ConsentKit.swift
├── ConsentKitCell.swift
├── ConsentKitCell.xib
├── ConsentKitCellProtocol.swift
├── ConsentKitServices.swift
├── ConsentKitTableViewDataSource.swift
├── ConsentKitUserDefaultsDataSource.swift
└── ConsentKitViewController.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: Consent.podspec
================================================
Pod::Spec.new do |s|
s.name = 'ConsentKit'
s.version = '0.9'
s.summary = 'Keep track of GDPR consents in your app.'
s.description = <<-DESC
Keep track of GDPR consents in your app.
DESC
s.module_name = "ConsentKit"
s.homepage = 'https://github.com/ralcr/Consent'
s.license = 'MIT'
s.authors = { 'Cristian Baluta' => 'cristi.baluta@gmail.com' }
s.ios.deployment_target = '10.0'
s.source = { :git => 'https://github.com/ralcr/Consent', :tag => s.version }
s.source_files = 'src/*.{h,swift}'
end
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Cristian Baluta
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ConsentKit
Note: This library only helps you collect consents for the services in your app, actually disabling those services is developer's job.

## Usage
### Define the services in your app
Note: Some generic services can be found in ConsentKitServices
```swift
enum Services: String, ConsentKitItem {
case icloud = "iCloud"
case analytics = "analytics"
func title() -> String {
switch self {
case .icloud: return "iCloud"
case .analytics: return "Google analytics"
}
}
func description() -> String {
switch self {
case .icloud: return "Wether to accept iCloud or not"
case .analytics: return "Google analytics"
}
}
func alertMessage() -> String? {
switch self {
case .icloud: return nil
case .analytics: return "I accept this app to store anonymous analytics in Google Analytics!"
}
}
}
```
### Instantiate the lib, preferably once in the AppDelegate, but can be anywhere and as many times as you like.
```swift
let gdpr = ConsentKit()
```
### Check if you have missing consents
If yes, add the default ConsentKitViewController to handle all the switches for you.
```swift
if gdpr.needsReviewing([Services.icloud, Services.analytics]) {
let vc = ConsentKitViewController()
vc.items = [Services.icloud, Services.analytics, ConsentKitServices.location]
self.present(vc, animated: true)
}
```
### Custom storage
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
```swift
let gdpr = ConsentKit(dataSource: InMemoryDataSource())// InMemoryDataSource implements ConsentKitDataSource
protocol ConsentKitDataSource {
func isAccepted (_ item: ConsentKitItem) -> Bool
func isReviewed (_ item: ConsentKitItem) -> Bool
func setAccepted (_ value: Bool, for item: ConsentKitItem)
func reset (_ item: ConsentKitItem)// Optional
}
```
## Contribution
Help me create a complete list of services that the developers can use.
Thanks.
================================================
FILE: demo-ios/Gdpr/AppDelegate.swift
================================================
//
// AppDelegate.swift
// Gdpr
//
// Created by Cristian Baluta on 21/05/2018.
// Copyright © 2018 Cristian Baluta. All rights reserved.
//
import UIKit
enum Services: String, ConsentKitItem {
case icloud = "iCloud"
case analytics = "analytics"
func title() -> String {
switch self {
case .icloud: return "iCloud"
case .analytics: return "Google analytics"
}
}
func description() -> String {
switch self {
case .icloud: return "Store data to the Apple's iCloud. This will allow the data to by synced across all your devices."
case .analytics: return "Help developer understand how the app is used by sharing usage with Google analytics."
}
}
func alertMessage() -> String? {
switch self {
case .icloud: return nil
case .analytics: return nil
}
}
}
let gdpr = ConsentKit()
//let gdpr = ConsentKit(dataSource: InMemoryDataSource())
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// For testing purposes we reset all values from previous run
gdpr.reset(Services.icloud)
gdpr.reset(Services.analytics)
gdpr.reset(ConsentKitServices.location)
return true
}
}
================================================
FILE: demo-ios/Gdpr/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: demo-ios/Gdpr/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: demo-ios/Gdpr/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: demo-ios/Gdpr/Base.lproj/Main.storyboard
================================================
================================================
FILE: demo-ios/Gdpr/InMemoryDataSource.swift
================================================
//
// InMemorydataSource.swift
// Gdpr
//
// Created by Cristian Baluta on 23/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import Foundation
class InMemoryDataSource: ConsentKitDataSource {
private var dict = [String: Bool?]()
func isAccepted (_ item: ConsentKitItem) -> Bool {
return dict[item.rawValue] == true
}
func isReviewed (_ item: ConsentKitItem) -> Bool {
return dict[item.rawValue] != nil
}
func setAccepted (_ value: Bool, for item: ConsentKitItem) {
dict[item.rawValue] = value
}
}
================================================
FILE: demo-ios/Gdpr/Info.plist
================================================
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
1.0
CFBundleVersion
1
LSRequiresIPhoneOS
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UIRequiredDeviceCapabilities
armv7
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
================================================
FILE: demo-ios/Gdpr/NavigationViewController.swift
================================================
//
// NavigationViewController.swift
// Gdpr
//
// Created by Cristian Baluta on 23/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import UIKit
class NavigationViewController: UIViewController {
@IBOutlet var label: UILabel!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if gdpr.needsReviewing([Services.icloud, Services.analytics, ConsentKitServices.location]) {
label.text = "Gdpr needs reviewing!"
} else {
label.text = "All gdpr items already reviewed"
}
}
@IBAction func handleGdpr() {
let vc = ConsentKitViewController()
vc.items = [Services.icloud, Services.analytics, ConsentKitServices.location]
vc.didFinishReview = {
self.navigationController?.popViewController(animated: true)
}
self.navigationController?.pushViewController(vc, animated: true)
}
}
================================================
FILE: demo-ios/Gdpr/SimpleViewController.swift
================================================
//
// SimpleViewController.swift
// Gdpr
//
// Created by Cristian Baluta on 22/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import UIKit
class SimpleViewController: UIViewController {
@IBOutlet var label: UILabel!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if gdpr.needsReviewing([Services.icloud, Services.analytics, ConsentKitServices.location]) {
label.text = "Gdpr needs reviewing!"
} else {
label.text = "All gdpr items already reviewed"
}
}
@IBAction func handleGdpr() {
let vc = ConsentKitViewController()
vc.items = [Services.icloud, Services.analytics, ConsentKitServices.location]
self.present(vc, animated: true)
}
}
================================================
FILE: demo-ios/Gdpr/TableViewController.swift
================================================
//
// TableViewController.swift
// Gdpr
//
// Created by Cristian Baluta on 04/06/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return section == 1 ? 1 : 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 1 {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
return cell
} else {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = "IndePath \(indexPath)"
return cell
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0:
return "Section 1"
case 1:
return "GDPR section"
default:
return "Section 3"
}
}
}
================================================
FILE: demo-ios/Gdpr/ViewController.swift
================================================
//
// ViewController.swift
// Gdpr
//
// Created by Cristian Baluta on 21/05/2018.
// Copyright © 2018 Cristian Baluta. All rights reserved.
//
import UIKit
class ViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
================================================
FILE: demo-ios/Gdpr.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
283DB05C20BADECD00A8BC14 /* ConsentKitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 283DB05B20BADECD00A8BC14 /* ConsentKitCell.xib */; };
283DB05E20BC77D200A8BC14 /* ConsentKitCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283DB05D20BC77D200A8BC14 /* ConsentKitCellProtocol.swift */; };
2845157D20B3528B009F3EE1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845157C20B3528B009F3EE1 /* AppDelegate.swift */; };
2845157F20B3528B009F3EE1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845157E20B3528B009F3EE1 /* ViewController.swift */; };
2845158220B3528B009F3EE1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2845158020B3528B009F3EE1 /* Main.storyboard */; };
2845158420B3528F009F3EE1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2845158320B3528F009F3EE1 /* Assets.xcassets */; };
2845158720B3528F009F3EE1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2845158520B3528F009F3EE1 /* LaunchScreen.storyboard */; };
2845159320B352F4009F3EE1 /* CloudKitViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845158F20B352F4009F3EE1 /* CloudKitViewControllerHeader.swift */; };
2845159420B352F4009F3EE1 /* ConsentKitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159020B352F4009F3EE1 /* ConsentKitCell.swift */; };
2845159520B352F4009F3EE1 /* ConsentKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159120B352F4009F3EE1 /* ConsentKit.swift */; };
2845159620B352F4009F3EE1 /* ConsentKitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159220B352F4009F3EE1 /* ConsentKitViewController.swift */; };
2845159820B4BB95009F3EE1 /* SimpleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159720B4BB95009F3EE1 /* SimpleViewController.swift */; };
2845159A20B4BDBC009F3EE1 /* NavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159920B4BDBC009F3EE1 /* NavigationViewController.swift */; };
2845159C20B5EA1C009F3EE1 /* InMemoryDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159B20B5EA1C009F3EE1 /* InMemoryDataSource.swift */; };
2845159E20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159D20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift */; };
284515A020B5F738009F3EE1 /* ConsentKitServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845159F20B5F738009F3EE1 /* ConsentKitServices.swift */; };
5684412C20C5A4DB00D9A471 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5684412B20C5A4DB00D9A471 /* TableViewController.swift */; };
5684413220C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5684413120C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
283DB05B20BADECD00A8BC14 /* ConsentKitCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConsentKitCell.xib; sourceTree = ""; };
283DB05D20BC77D200A8BC14 /* ConsentKitCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitCellProtocol.swift; sourceTree = ""; };
2845157920B3528B009F3EE1 /* Gdpr.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gdpr.app; sourceTree = BUILT_PRODUCTS_DIR; };
2845157C20B3528B009F3EE1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
2845157E20B3528B009F3EE1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
2845158120B3528B009F3EE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
2845158320B3528F009F3EE1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
2845158620B3528F009F3EE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
2845158820B3528F009F3EE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
2845158F20B352F4009F3EE1 /* CloudKitViewControllerHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitViewControllerHeader.swift; sourceTree = ""; };
2845159020B352F4009F3EE1 /* ConsentKitCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentKitCell.swift; sourceTree = ""; };
2845159120B352F4009F3EE1 /* ConsentKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentKit.swift; sourceTree = ""; };
2845159220B352F4009F3EE1 /* ConsentKitViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentKitViewController.swift; sourceTree = ""; };
2845159720B4BB95009F3EE1 /* SimpleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleViewController.swift; sourceTree = ""; };
2845159920B4BDBC009F3EE1 /* NavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewController.swift; sourceTree = ""; };
2845159B20B5EA1C009F3EE1 /* InMemoryDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryDataSource.swift; sourceTree = ""; };
2845159D20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitUserDefaultsDataSource.swift; sourceTree = ""; };
2845159F20B5F738009F3EE1 /* ConsentKitServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitServices.swift; sourceTree = ""; };
5684412B20C5A4DB00D9A471 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; };
5684413120C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentKitTableViewDataSource.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2845157620B3528B009F3EE1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2845157020B3528B009F3EE1 = {
isa = PBXGroup;
children = (
2845157B20B3528B009F3EE1 /* Gdpr */,
2845157A20B3528B009F3EE1 /* Products */,
);
sourceTree = "";
};
2845157A20B3528B009F3EE1 /* Products */ = {
isa = PBXGroup;
children = (
2845157920B3528B009F3EE1 /* Gdpr.app */,
);
name = Products;
sourceTree = "";
};
2845157B20B3528B009F3EE1 /* Gdpr */ = {
isa = PBXGroup;
children = (
2845157C20B3528B009F3EE1 /* AppDelegate.swift */,
2845157E20B3528B009F3EE1 /* ViewController.swift */,
2845159720B4BB95009F3EE1 /* SimpleViewController.swift */,
2845159920B4BDBC009F3EE1 /* NavigationViewController.swift */,
5684412B20C5A4DB00D9A471 /* TableViewController.swift */,
2845159B20B5EA1C009F3EE1 /* InMemoryDataSource.swift */,
2845158020B3528B009F3EE1 /* Main.storyboard */,
2845158320B3528F009F3EE1 /* Assets.xcassets */,
2845158520B3528F009F3EE1 /* LaunchScreen.storyboard */,
2845158820B3528F009F3EE1 /* Info.plist */,
2845158E20B352F4009F3EE1 /* src */,
);
path = Gdpr;
sourceTree = "";
};
2845158E20B352F4009F3EE1 /* src */ = {
isa = PBXGroup;
children = (
2845159120B352F4009F3EE1 /* ConsentKit.swift */,
2845159F20B5F738009F3EE1 /* ConsentKitServices.swift */,
2845159D20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift */,
2845159220B352F4009F3EE1 /* ConsentKitViewController.swift */,
5684413120C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift */,
283DB05D20BC77D200A8BC14 /* ConsentKitCellProtocol.swift */,
2845159020B352F4009F3EE1 /* ConsentKitCell.swift */,
283DB05B20BADECD00A8BC14 /* ConsentKitCell.xib */,
2845158F20B352F4009F3EE1 /* CloudKitViewControllerHeader.swift */,
);
name = src;
path = ../../src;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
2845157820B3528B009F3EE1 /* Gdpr */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2845158B20B3528F009F3EE1 /* Build configuration list for PBXNativeTarget "Gdpr" */;
buildPhases = (
2845157520B3528B009F3EE1 /* Sources */,
2845157620B3528B009F3EE1 /* Frameworks */,
2845157720B3528B009F3EE1 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Gdpr;
productName = Gdpr;
productReference = 2845157920B3528B009F3EE1 /* Gdpr.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
2845157120B3528B009F3EE1 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0930;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = "Imagin soft";
TargetAttributes = {
2845157820B3528B009F3EE1 = {
CreatedOnToolsVersion = 9.3;
};
};
};
buildConfigurationList = 2845157420B3528B009F3EE1 /* Build configuration list for PBXProject "Gdpr" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 2845157020B3528B009F3EE1;
productRefGroup = 2845157A20B3528B009F3EE1 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
2845157820B3528B009F3EE1 /* Gdpr */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
2845157720B3528B009F3EE1 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2845158720B3528F009F3EE1 /* LaunchScreen.storyboard in Resources */,
283DB05C20BADECD00A8BC14 /* ConsentKitCell.xib in Resources */,
2845158420B3528F009F3EE1 /* Assets.xcassets in Resources */,
2845158220B3528B009F3EE1 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2845157520B3528B009F3EE1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2845159A20B4BDBC009F3EE1 /* NavigationViewController.swift in Sources */,
2845159E20B5EDD6009F3EE1 /* ConsentKitUserDefaultsDataSource.swift in Sources */,
5684412C20C5A4DB00D9A471 /* TableViewController.swift in Sources */,
284515A020B5F738009F3EE1 /* ConsentKitServices.swift in Sources */,
2845157F20B3528B009F3EE1 /* ViewController.swift in Sources */,
2845159C20B5EA1C009F3EE1 /* InMemoryDataSource.swift in Sources */,
2845159420B352F4009F3EE1 /* ConsentKitCell.swift in Sources */,
2845159620B352F4009F3EE1 /* ConsentKitViewController.swift in Sources */,
5684413220C6D62E00D9A471 /* ConsentKitTableViewDataSource.swift in Sources */,
283DB05E20BC77D200A8BC14 /* ConsentKitCellProtocol.swift in Sources */,
2845157D20B3528B009F3EE1 /* AppDelegate.swift in Sources */,
2845159320B352F4009F3EE1 /* CloudKitViewControllerHeader.swift in Sources */,
2845159820B4BB95009F3EE1 /* SimpleViewController.swift in Sources */,
2845159520B352F4009F3EE1 /* ConsentKit.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
2845158020B3528B009F3EE1 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
2845158120B3528B009F3EE1 /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
2845158520B3528F009F3EE1 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
2845158620B3528F009F3EE1 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
2845158920B3528F009F3EE1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
2845158A20B3528F009F3EE1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
2845158C20B3528F009F3EE1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5NHDC5EV44;
INFOPLIST_FILE = Gdpr/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = ro.imagin.Gdpr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
2845158D20B3528F009F3EE1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5NHDC5EV44;
INFOPLIST_FILE = Gdpr/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = ro.imagin.Gdpr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2845157420B3528B009F3EE1 /* Build configuration list for PBXProject "Gdpr" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2845158920B3528F009F3EE1 /* Debug */,
2845158A20B3528F009F3EE1 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2845158B20B3528F009F3EE1 /* Build configuration list for PBXNativeTarget "Gdpr" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2845158C20B3528F009F3EE1 /* Debug */,
2845158D20B3528F009F3EE1 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 2845157120B3528B009F3EE1 /* Project object */;
}
================================================
FILE: demo-ios/Gdpr.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: demo-ios/Gdpr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: demo-ios/Gdpr.xcodeproj/xcuserdata/cristi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
================================================
================================================
FILE: demo-ios/Gdpr.xcodeproj/xcuserdata/cristi.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
SchemeUserState
Gdpr.xcscheme
orderHint
0
================================================
FILE: demo-ios/Gdpr.xcodeproj/xcuserdata/cristianbaluta.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
SchemeUserState
Gdpr.xcscheme
orderHint
0
================================================
FILE: src/CloudKitViewControllerHeader.swift
================================================
//
// CloudKitViewControllerHeader.swift
//
// Created by Cristian Baluta on 21/05/2018.
// Copyright © 2018 Cristian Baluta. All rights reserved.
//
import UIKit
class CloudKitViewControllerHeader: UIView {
var didDone: (() -> Void)?
private let padding = CGFloat(16)
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
let label = UILabel()
label.text = "Review services!"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.sizeToFit()
label.center = CGPoint(x: label.frame.size.width/2 + padding, y: 30)
self.addSubview(label)
let button = UIButton()
button.setTitle("Done", for: .normal)
button.setTitleColor(UIColor.orange, for: .normal)
button.addTarget(self, action: #selector(handleButton), for: .touchUpInside)
button.sizeToFit()
button.center = CGPoint(x: frame.size.width - button.frame.size.width/2 - padding, y: 30)
self.addSubview(button)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func handleButton() {
didDone?()
}
}
================================================
FILE: src/ConsentKit.swift
================================================
//
// ConsentKit.swift
//
// Created by Cristian Baluta on 21/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import Foundation
protocol ConsentKitItem {
var rawValue: String {get}
/// Title used in the cell and alert
func title() -> String
/// Description used in the cell
func description() -> String
/// Message displayed in an alert when the switch is turned on.
func alertMessage() -> String?
}
protocol ConsentKitDataSource {
/// Returns if the item has a value of true
func isAccepted (_ item: ConsentKitItem) -> Bool
/// Returns if the item's value is different than nil, which means it was reviewed
func isReviewed (_ item: ConsentKitItem) -> Bool
/// Set a value for the item
func setAccepted (_ value: Bool, for item: ConsentKitItem)
/// Resets the value of an item, which means that isReviewed will return false after that
func reset (_ item: ConsentKitItem)
}
extension ConsentKitDataSource {
func reset (_ item: ConsentKitItem) {
// This is a empty implementation to allow this method to be optional
}
}
class ConsentKit {
/// Assign a custom dataSource. By default UserDefaults is used and each key is prefixed with "ConsentKit-"
var dataSource: ConsentKitDataSource = ConsentKitUserDefaultsDataSource()
convenience init (dataSource: ConsentKitDataSource) {
self.init()
self.dataSource = dataSource
}
/// Returns if there's any value that was not reviewed
/// Used to check if the ConsentKitViewController should be presented or not
/// This will also be useful to present the controller if any new service added later in the app needs reviewing
func needsReviewing(_ items: [ConsentKitItem]) -> Bool {
for item in items {
if !isReviewed(item) {
return true
}
}
return false
}
}
extension ConsentKit: ConsentKitDataSource {
func isAccepted (_ item: ConsentKitItem) -> Bool {
return dataSource.isAccepted(item)
}
func isReviewed (_ item: ConsentKitItem) -> Bool {
return dataSource.isReviewed(item)
}
func setAccepted (_ value: Bool, for item: ConsentKitItem) {
dataSource.setAccepted(value, for: item)
}
func reset (_ item: ConsentKitItem) {
dataSource.reset(item)
}
}
================================================
FILE: src/ConsentKitCell.swift
================================================
//
// ConsentKitCell.swift
//
// Created by Cristian Baluta on 21/05/2018.
// Copyright © 2018 Cristian Baluta. All rights reserved.
//
import UIKit
class ConsentKitCell: UITableViewCell, ConsentKitCellProtocol {
@IBOutlet private var titleLabel: UILabel!
@IBOutlet private var subtitleLabel: UILabel!
@IBOutlet private var switchButton: UISwitch!
var title: String = "" {
didSet {
titleLabel.text = title
}
}
var subtitle: String = "" {
didSet {
subtitleLabel.text = subtitle
}
}
var value: Bool = false {
didSet {
switchButton.isOn = value
}
}
var valueDidChange: ((Bool) -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
switchButton.onTintColor = UIColor.orange
}
static func instantiateFromXib() -> ConsentKitCell {
let arrNib: Array = Bundle.main.loadNibNamed("ConsentKitCell", owner: self, options: nil)!
return arrNib.first as! ConsentKitCell
}
@IBAction func switchChanged(_ sender: UISwitch) {
valueDidChange?(sender.isOn)
}
}
================================================
FILE: src/ConsentKitCell.xib
================================================
================================================
FILE: src/ConsentKitCellProtocol.swift
================================================
//
// ConsentKitCellProtocol.swift
//
// Created by Cristian Baluta on 28/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import Foundation
protocol ConsentKitCellProtocol {
var title: String {get set}
var subtitle: String {get set}
var value: Bool {get set}
var valueDidChange: ((Bool) -> Void)? {get set}
}
================================================
FILE: src/ConsentKitServices.swift
================================================
//
// ConsentKitServices.swift
//
// Created by Cristian Baluta on 23/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import Foundation
enum ConsentKitServices: String, ConsentKitItem {
case icloud = "ck-icloud"
case analytics = "ck-analytics"
case location = "ck-location"
func title() -> String {
switch self {
case .icloud: return "iCloud"
case .analytics: return "Google analytics"
case .location: return "Location"
}
}
func description() -> String {
switch self {
case .icloud: return "Store data to the Apple's iCloud. This will allow the data to by synced across all your devices."
case .analytics: return "Help developer understand how the app is used by sharing usage with Google analytics."
case .location: return "Store location on server"
}
}
func alertMessage() -> String? {
switch self {
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"
case .analytics: return "I accept that this app will store anonymous usage data in Google analytics for the purpose of improving the app"
case .location: return "I accept to store my location on the server"
}
}
}
================================================
FILE: src/ConsentKitTableViewDataSource.swift
================================================
//
// ConsentKitTableViewDataSource.swift
// Gdpr
//
// Created by Cristian Baluta on 05/06/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import UIKit
class ConsentKitTableViewDataSource: NSObject, UITableViewDelegate, UITableViewDataSource {
var items: [ConsentKitItem] = []
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
var cell: ConsentKitCellProtocol = ConsentKitCell.instantiateFromXib()
cell.title = item.title()
cell.subtitle = item.description()
cell.value = gdpr.isAccepted(item)
cell.valueDidChange = { isOn in
self.item(item, didChangeValue: isOn, in: cell)
}
return cell as! ConsentKitCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func item(_ item: ConsentKitItem, didChangeValue value: Bool, in cell: ConsentKitCellProtocol) {
if value {
guard let message = item.alertMessage() else {
self.gdpr.setAccepted(true, for: item)
self.didAccept?(item)
return
}
let alert = UIAlertController(title: item.title(), message: message, preferredStyle: .alert)
alert.addAction(
UIAlertAction(title: "Accept", style: .default, handler: { _ in
self.gdpr.setAccepted(true, for: item)
self.didAccept?(item)
})
)
alert.addAction(
UIAlertAction(title: "Decline", style: .cancel, handler: { _ in
var cell = cell
cell.value = false
self.gdpr.setAccepted(false, for: item)
self.didReject?(item)
})
)
self.present(alert, animated: true, completion: nil)
} else {
self.gdpr.setAccepted(false, for: item)
didReject?(item)
}
}
}
================================================
FILE: src/ConsentKitUserDefaultsDataSource.swift
================================================
//
// ConsentKitUserDefaultsDataSource.swift
//
// Created by Cristian Baluta on 23/05/2018.
// Copyright © 2018 Imagin soft. All rights reserved.
//
import Foundation
class ConsentKitUserDefaultsDataSource {
fileprivate let prefix = "ConsentKit-"
fileprivate let userDefaults = UserDefaults.standard
fileprivate func get (_ key: String) -> Any? {
return userDefaults.object(forKey: prefix + key)
}
fileprivate func set (_ value: Bool?, forKey key: String) {
if let v = value {
userDefaults.set(v, forKey: prefix + key)
} else {
userDefaults.removeObject(forKey: prefix + key)
}
userDefaults.synchronize()
}
}
extension ConsentKitUserDefaultsDataSource: ConsentKitDataSource {
func isAccepted (_ item: ConsentKitItem) -> Bool {
return (get(item.rawValue) as? Bool) == true
}
func isReviewed (_ item: ConsentKitItem) -> Bool {
return get(item.rawValue) != nil
}
func setAccepted (_ value: Bool, for item: ConsentKitItem) {
set(value, forKey: item.rawValue)
}
func reset (_ item: ConsentKitItem) {
set(nil, forKey: item.rawValue)
}
}
================================================
FILE: src/ConsentKitViewController.swift
================================================
//
// ConsentKitViewController.swift
//
// Created by Cristian Baluta on 21/05/2018.
// Copyright © 2018 Cristian Baluta. All rights reserved.
//
import UIKit
class ConsentKitViewController: UITableViewController {
var didAccept: ((ConsentKitItem) -> Void)?
var didReject: ((ConsentKitItem) -> Void)?
var didFinishReview: (() -> Void)?
var items: [ConsentKitItem] = [] {
didSet {
tableView.reloadData()
}
}
fileprivate let gdpr = ConsentKit()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.navigationController == nil {
// Add a custom header only if the VC is not pushed into a navigationController
let header = CloudKitViewControllerHeader(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 80))
header.didDone = {
self.handleDone()
self.dismiss(animated: true, completion: nil)
}
tableView.tableHeaderView = header
} else {
self.title = "Review services!"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(handleDone))
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
var cell: ConsentKitCellProtocol = ConsentKitCell.instantiateFromXib()
cell.title = item.title()
cell.subtitle = item.description()
cell.value = gdpr.isAccepted(item)
cell.valueDidChange = { isOn in
self.item(item, didChangeValue: isOn, in: cell)
}
return cell as! ConsentKitCell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
// MARK: - Switch changed
func item(_ item: ConsentKitItem, didChangeValue value: Bool, in cell: ConsentKitCellProtocol) {
if value {
guard let message = item.alertMessage() else {
self.gdpr.setAccepted(true, for: item)
self.didAccept?(item)
return
}
let alert = UIAlertController(title: item.title(), message: message, preferredStyle: .alert)
alert.addAction(
UIAlertAction(title: "Accept", style: .default, handler: { _ in
self.gdpr.setAccepted(true, for: item)
self.didAccept?(item)
})
)
alert.addAction(
UIAlertAction(title: "Decline", style: .cancel, handler: { _ in
var cell = cell
cell.value = false
self.gdpr.setAccepted(false, for: item)
self.didReject?(item)
})
)
self.present(alert, animated: true, completion: nil)
} else {
self.gdpr.setAccepted(false, for: item)
didReject?(item)
}
}
@objc func handleDone() {
// Set to false the untouched switches, to prevent gdpr being called again
for item in items {
if !gdpr.isReviewed(item) {
gdpr.setAccepted(false, for: item)
}
}
didFinishReview?()
}
}