Skip to content
This repository was archived by the owner on Apr 16, 2024. It is now read-only.

Commit 43a8751

Browse files
committed
Merge branch 'develop'
2 parents 9960d6a + 94a17f0 commit 43a8751

16 files changed

+391
-88
lines changed

.github/workflows/swift.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ jobs:
1919
- uses: actions/checkout@v2
2020
- name: CocoaPod Install
2121
run: cd Example && pod install
22-
- name: Example app Unit and UI testing -> iPhone 11 (iOS 14.0)
23-
run: xcodebuild test -scheme ConsentViewController-Example -workspace Example/CCPAConsentViewController.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.0' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
24-
- name: SourcePointMetaApp UI testing -> iPhone 11 (iOS 14.0)
25-
run: xcodebuild test -scheme SourcePointMetaApp -workspace Example/CCPAConsentViewController.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.0' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
22+
- name: Example app Unit and UI testing -> iPhone 11 (iOS 14.2)
23+
run: xcodebuild test -scheme ConsentViewController-Example -workspace Example/CCPAConsentViewController.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.2' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
24+
- name: SourcePointMetaApp UI testing -> iPhone 11 (iOS 14.2)
25+
run: xcodebuild test -scheme SourcePointMetaApp -workspace Example/CCPAConsentViewController.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.2' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
2626

CCPAConsentViewController.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'CCPAConsentViewController'
3-
s.version = '1.4.0'
3+
s.version = '1.5.0'
44
s.summary = 'SourcePoint\'s CCPAConsentViewController to handle privacy consents.'
55
s.homepage = 'https://www.sourcepoint.com'
66
s.license = { :type => 'MIT', :file => 'LICENSE' }

CCPAConsentViewController/Classes/Action.swift

+25-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import Foundation
99

1010
/// User actions. Its integer representation matches with what SourcePoint's endpoints expect.
11-
@objc public enum Action: Int, Codable, CaseIterable, CustomStringConvertible {
11+
@objc public enum ActionType: Int, Codable, CaseIterable, CustomStringConvertible {
1212
case SaveAndExit = 1
1313
case AcceptAll = 11
1414
case ShowPrivacyManager = 12
@@ -25,3 +25,27 @@ import Foundation
2525
}
2626
}
2727
}
28+
29+
/// Action consists of `ActionType` and publisherData.
30+
@objcMembers public class Action: NSObject {
31+
public let type: ActionType
32+
public var publisherData: [String: SPCCPAArbitraryJson?] = [:]
33+
34+
public override var description: String {
35+
"""
36+
GDPRAction(type: \(type), publisherData: \(String(describing: publisherData))
37+
"""
38+
}
39+
40+
public override func isEqual(_ object: Any?) -> Bool {
41+
guard let other = object as? Action else {
42+
return false
43+
}
44+
return other.type == type &&
45+
other.publisherData == publisherData
46+
}
47+
48+
public init(type: ActionType) {
49+
self.type = type
50+
}
51+
}

CCPAConsentViewController/Classes/CCPAConsentViewController.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ extension CCPAConsentViewController: ConsentDelegate {
267267

268268
public func onAction(_ action: Action, consents: PMConsents?) {
269269
consentDelegate?.onAction?(action, consents: consents)
270-
if action == .AcceptAll || action == .RejectAll || action == .SaveAndExit {
270+
let type = action.type
271+
if type == .AcceptAll || type == .RejectAll || type == .SaveAndExit {
271272
sourcePoint.postAction(action: action, consentUUID: consentUUID, consents: consents) { [weak self] actionResponse, error in
272273
if let response = actionResponse {
273274
self?.consentUUID = response.uuid

CCPAConsentViewController/Classes/MessageWebViewController.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class MessageWebViewController: MessageViewController, WKUIDelegate, WKNavigatio
132132

133133
func onAction(_ action: Action, consents: PMConsents?) {
134134
consentDelegate?.onAction?(action, consents: consents)
135-
switch action {
135+
switch action.type {
136136
case .ShowPrivacyManager:
137137
showPrivacyManagerFromMessageAction()
138138
case .Dismiss:
@@ -219,13 +219,13 @@ class MessageWebViewController: MessageViewController, WKUIDelegate, WKNavigatio
219219
case "onAction":
220220
guard
221221
let payload = body["body"] as? [String: Any],
222-
let actionType = payload["type"] as? Int,
223-
let action = Action(rawValue: actionType)
222+
let typeString = payload["type"] as? Int,
223+
let actionType = ActionType(rawValue: typeString)
224224
else {
225225
onError(error: CCPAMessageEventParsingError(message: Optional(message.body).debugDescription))
226226
return
227227
}
228-
onAction(action, consents: getPMConsentsIfAny(payload))
228+
onAction(Action(type: actionType), consents: getPMConsentsIfAny(payload))
229229
case "onError":
230230
onError(error: CCPAWebViewError())
231231
default:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
//
2+
// SPCCPAArbitraryJson.swift
3+
// CCPAConsentViewController
4+
//
5+
// Created by Vilas on 13/12/20.
6+
//
7+
8+
import Foundation
9+
10+
public enum SPCCPAArbitraryJson: Codable, CustomStringConvertible, Equatable {
11+
public var description: String {
12+
switch self {
13+
case .string(let string): return "\"\(string)\""
14+
case .number(let double):
15+
if let int = Int(exactly: double) {
16+
return "\(int)"
17+
} else {
18+
return "\(double)"
19+
}
20+
case .object(let object):
21+
let keyValues = object
22+
.map { (key, value) in "\"\(key)\": \(value)" }
23+
.joined(separator: ",")
24+
return "{\(keyValues)}"
25+
case .array(let array):
26+
return "\(array)"
27+
case .bool(let bool):
28+
return "\(bool)"
29+
case .null:
30+
return "null"
31+
}
32+
}
33+
34+
public struct Key: CodingKey, Hashable, CustomStringConvertible {
35+
public var description: String {
36+
return stringValue
37+
}
38+
public let stringValue: String
39+
public init(_ string: String) { self.stringValue = string }
40+
public init?(stringValue: String) { self.init(stringValue) }
41+
public var intValue: Int? { return nil }
42+
public init?(intValue: Int) { return nil }
43+
}
44+
45+
case string(String)
46+
case number(Double)
47+
case object([Key: SPCCPAArbitraryJson])
48+
case array([SPCCPAArbitraryJson])
49+
case bool(Bool)
50+
case null
51+
52+
/// Creates the equivalent of an empty object JSON
53+
public init() {
54+
self = .object([:])
55+
}
56+
57+
public init(from decoder: Decoder) throws {
58+
if let string = try? decoder.singleValueContainer().decode(String.self) {
59+
self = .string(string)
60+
} else if let number = try? decoder.singleValueContainer().decode(Double.self) { self = .number(number) } else if let object = try? decoder.container(keyedBy: Key.self) {
61+
var result: [Key: SPCCPAArbitraryJson] = [:]
62+
for key in object.allKeys {
63+
result[key] = (try? object.decode(SPCCPAArbitraryJson.self, forKey: key)) ?? .null
64+
}
65+
self = .object(result)
66+
} else if var array = try? decoder.unkeyedContainer() {
67+
var result: [SPCCPAArbitraryJson] = []
68+
for _ in 0..<(array.count ?? 0) {
69+
result.append(try array.decode(SPCCPAArbitraryJson.self))
70+
}
71+
self = .array(result)
72+
} else if let bool = try? decoder.singleValueContainer().decode(Bool.self) {
73+
self = .bool(bool)
74+
} else if let isNull = try? decoder.singleValueContainer().decodeNil(), isNull { self = .null
75+
} else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [],
76+
debugDescription: "Unknown SPGDPRArbitraryJson type")) }
77+
}
78+
79+
public func encode(to encoder: Encoder) throws {
80+
switch self {
81+
case .string(let string):
82+
var container = encoder.singleValueContainer()
83+
try container.encode(string)
84+
case .number(let number):
85+
var container = encoder.singleValueContainer()
86+
try container.encode(number)
87+
case .bool(let bool):
88+
var container = encoder.singleValueContainer()
89+
try container.encode(bool)
90+
case .object(let object):
91+
var container = encoder.container(keyedBy: Key.self)
92+
for (key, value) in object {
93+
try container.encode(value, forKey: key)
94+
}
95+
case .array(let array):
96+
var container = encoder.unkeyedContainer()
97+
for value in array {
98+
try container.encode(value)
99+
}
100+
case .null:
101+
var container = encoder.singleValueContainer()
102+
try container.encodeNil()
103+
}
104+
}
105+
106+
public var objectValue: [String: SPCCPAArbitraryJson]? {
107+
switch self {
108+
case .object(let object):
109+
let mapped: [String: SPCCPAArbitraryJson] = Dictionary(uniqueKeysWithValues:
110+
object.map { (key, value) in (key.stringValue, value) })
111+
return mapped
112+
default: return nil
113+
}
114+
}
115+
116+
public var arrayValue: [SPCCPAArbitraryJson]? {
117+
switch self {
118+
case .array(let array): return array
119+
default: return nil
120+
}
121+
}
122+
123+
public subscript(key: String) -> SPCCPAArbitraryJson? {
124+
guard let jsonKey = Key(stringValue: key),
125+
case .object(let object) = self,
126+
let value = object[jsonKey]
127+
else { return nil }
128+
return value
129+
}
130+
131+
public var stringValue: String? {
132+
switch self {
133+
case .string(let string): return string
134+
default: return nil
135+
}
136+
}
137+
138+
public var nullValue: Any? {
139+
return nil
140+
}
141+
142+
public var doubleValue: Double? {
143+
switch self {
144+
case .number(let number): return number
145+
default: return nil
146+
}
147+
}
148+
149+
public var intValue: Int? {
150+
switch self {
151+
case .number(let number): return Int(number)
152+
default: return nil
153+
}
154+
}
155+
156+
public subscript(index: Int) -> SPCCPAArbitraryJson? {
157+
switch self {
158+
case .array(let array): return array[index]
159+
default: return nil
160+
}
161+
}
162+
163+
public var boolValue: Bool? {
164+
switch self {
165+
case .bool(let bool): return bool
166+
default: return nil
167+
}
168+
}
169+
170+
public var anyValue: Any? {
171+
switch self {
172+
case .string(let string): return string
173+
case .number(let number):
174+
if let int = Int(exactly: number) { return int } else { return number }
175+
case .bool(let bool): return bool
176+
case .object(let object):
177+
return Dictionary(uniqueKeysWithValues:
178+
object.compactMap { (key, value) -> (String, Any)? in
179+
if let nonNilValue = value.anyValue {
180+
return (key.stringValue, nonNilValue)
181+
} else { return nil }
182+
})
183+
case .array(let array):
184+
return array.compactMap { $0.anyValue }
185+
case .null:
186+
return nil
187+
}
188+
}
189+
190+
public var dictionaryValue: [String: Any]? {
191+
return anyValue as? [String: Any]
192+
}
193+
194+
public subscript(dynamicMember member: String) -> SPCCPAArbitraryJson {
195+
return self[member] ?? .null
196+
}
197+
}
198+
199+
public extension SPCCPAArbitraryJson {
200+
init(_ value: Any) throws {
201+
if let string = value as? String { self = .string(string) } else if let number = value as? NSNumber {
202+
switch CFGetTypeID(number as CFTypeRef) {
203+
case CFBooleanGetTypeID():
204+
self = .bool(number.boolValue)
205+
case CFNumberGetTypeID():
206+
self = .number(number.doubleValue)
207+
default:
208+
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Cannot encode value"))
209+
}
210+
} else if let object = value as? [String: Any] {
211+
var result: [Key: SPCCPAArbitraryJson] = [:]
212+
for (key, subvalue) in object {
213+
result[Key(key)] = try SPCCPAArbitraryJson(subvalue)
214+
}
215+
self = .object(result)
216+
} else if let array = value as? [Any] {
217+
self = .array(try array.map(SPCCPAArbitraryJson.init))
218+
} else if case Optional<Any>.none = value {
219+
self = .null
220+
} else if let obj = value as? NSObject, obj.isEqual(NSNull()) {
221+
self = .null
222+
} else {
223+
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Cannot encode value"))
224+
}
225+
}
226+
}

CCPAConsentViewController/Classes/SourcePointClient/SourcePointClient.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,18 @@ class SourcePointClient {
112112
}
113113

114114
func postAction(action: Action, consentUUID: ConsentUUID, consents: PMConsents?, completionHandler: @escaping (ActionResponse?, CCPAAPIParsingError?) -> Void) {
115-
let url = postActionUrl(action.rawValue)
115+
let url = postActionUrl(action.type.rawValue)
116116
let meta = UserDefaults.standard.string(forKey: CCPAConsentViewController.META_KEY) ?? "{}"
117-
let ccpaConsents = CPPAPMConsents(rejectedVendors: consents?.vendors.rejected ?? [], rejectedCategories: consents?.categories.rejected ?? [])
117+
let ccpaConsents = CCPAPMConsents(rejectedVendors: consents?.vendors.rejected ?? [], rejectedCategories: consents?.categories.rejected ?? [])
118118
guard let body = try? json.encode(ActionRequest(
119119
propertyId: propertyId,
120120
accountId: accountId,
121121
privacyManagerId: pmId,
122122
uuid: consentUUID,
123123
requestUUID: requestUUID,
124124
consents: ccpaConsents,
125-
meta: meta
125+
meta: meta,
126+
publisherData: action.publisherData
126127
)) else {
127128
completionHandler(nil, CCPAAPIParsingError(url?.absoluteString ?? "POST consent", nil))
128129
return

CCPAConsentViewController/Classes/SourcePointRequestsResponses.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ struct ActionRequest: WrapperApiRequest {
4040
let privacyManagerId: String
4141
let uuid: ConsentUUID
4242
let requestUUID: UUID
43-
let consents: CPPAPMConsents
43+
let consents: CCPAPMConsents
4444
let meta: Meta
45+
let publisherData: [String: SPCCPAArbitraryJson?]?
4546
}
4647

4748
@objc public class PMConsents: NSObject, Codable {
@@ -62,6 +63,6 @@ struct ActionRequest: WrapperApiRequest {
6263
}
6364
}
6465

65-
struct CPPAPMConsents: Codable {
66+
struct CCPAPMConsents: Codable {
6667
let rejectedVendors, rejectedCategories: [String]
6768
}

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.5.0 (Dec, 15, 2020)
2+
* Added a feature to attach an arbitrary payload(publisher data) to action data. Check how to use it in this [section of the README](https://github.com/SourcePointUSA/CCPA_iOS_SDK#pubdata). [#55](https://github.com/SourcePointUSA/CCPA_iOS_SDK/pull/55)
3+
14
## 1.4.0 (Nov, 07, 2020)
25
* Prefixed error classes with `CCPA` to avoid class naming clashes with the GDPR SDK [#50](https://github.com/SourcePointUSA/CCPA_iOS_SDK/pull/50)
36
* Fixed an issue with authenticated consent not clearing user data when authId changed [#54](https://github.com/SourcePointUSA/CCPA_iOS_SDK/pull/54)

0 commit comments

Comments
 (0)