Skip to content

[DIA-5359] Add preferences to iOS SDK #615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ConsentViewController/Assets/javascript/SPJSReceiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ var handleMessageEvent = function(SDK) {

function isFromPM(event) {
event.settings = event.settings || {};
return event.fromPM || event.settings.vendorList;
return window.spLegislation == "preferences" || event.fromPM || event.settings.vendorList;
}

function isError(event) {
Expand Down
78 changes: 78 additions & 0 deletions ConsentViewController/Classes/Consents/SPPreferencesConsent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// SPPreferencesConsent.swift
// Pods
//
// Created by Fedko Dmytro on 06.05.2025
//

import Foundation

@objcMembers public class SPPreferencesConsent: NSObject, Codable, CampaignConsent, NSCopying {
var applies: Bool = true
public var uuid: String?
public var status, rejectedStatus: [PreferencesStatus]?
var dateCreated: SPDate
var messageId: String?

init(
uuid: String? = nil,
dateCreated: SPDate,
status: [PreferencesStatus]?,
rejectedStatus: [PreferencesStatus]?,
messageId: String? = nil
) {
self.uuid = uuid
self.dateCreated = dateCreated
self.status = status
self.rejectedStatus = rejectedStatus
self.messageId = messageId
}
}

extension SPPreferencesConsent {
override open var description: String {
"""
SPPreferencesConsent(
- uuid: \(uuid ?? "")
- status: \(status ?? [])
- rejectedStatus: \(rejectedStatus ?? [])
- dateCreated: \(dateCreated)
)
"""
}

public static func empty() -> SPPreferencesConsent { SPPreferencesConsent(
dateCreated: .now(),
status: [],
rejectedStatus: []
)}

public func copy(with zone: NSZone? = nil) -> Any {
SPPreferencesConsent(
uuid: uuid,
dateCreated: dateCreated,
status: status,
rejectedStatus: rejectedStatus,
messageId: messageId
)
}
}

extension SPPreferencesConsent {
public struct PreferencesStatus: Codable {
var categoryId: Int
var channels: [PreferencesChannels]?
var changed: Bool?
var dateConsented: SPDate?
var subType: PreferencesSubType? = PreferencesSubType.Unknown
}

struct PreferencesChannels: Codable {
var channelId: Int
var status: Bool
}

public enum PreferencesSubType: Codable {
case AIPolicy, TermsAndConditions, PrivacyPolicy, LegalPolicy, TermsOfSale, Unknown
}
}
28 changes: 22 additions & 6 deletions ConsentViewController/Classes/Consents/SPUserData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ public class SPConsent<ConsentType: Codable & Equatable & NSCopying>: NSObject,
/// - SeeAlso: `SPCCPAConsent`
public let ccpa: SPConsent<SPCCPAConsent>?

/// Consent data for USNat. This attribute will be nil if your setup doesn't include a CCPA campaign
/// Consent data for USNat. This attribute will be nil if your setup doesn't include a USNat campaign
/// - SeeAlso: `SPUSNatConsent`
public let usnat: SPConsent<SPUSNatConsent>?

/// Consent data for Preferences. This attribute will be nil if your setup doesn't include a Preferences campaign
/// - SeeAlso: `SPPreferencesConsent`
public let preferences: SPConsent<SPPreferencesConsent>?

public var webConsents: SPWebConsents { SPWebConsents(
gdpr: .init(uuid: gdpr?.consents?.uuid, webConsentPayload: gdpr?.consents?.webConsentPayload),
ccpa: .init(uuid: ccpa?.consents?.uuid, webConsentPayload: ccpa?.consents?.webConsentPayload),
Expand All @@ -80,28 +84,32 @@ public class SPConsent<ConsentType: Codable & Equatable & NSCopying>: NSObject,
self != SPUserData() &&
gdpr?.consents != SPGDPRConsent.empty() &&
ccpa?.consents != SPCCPAConsent.empty() &&
usnat?.consents != SPUSNatConsent.empty()
usnat?.consents != SPUSNatConsent.empty() &&
preferences?.consents != SPPreferencesConsent.empty()
}

override public var description: String {
"gdpr: \(String(describing: gdpr)), ccpa: \(String(describing: ccpa)), usnat: \(String(describing: usnat))"
"gdpr: \(String(describing: gdpr)), ccpa: \(String(describing: ccpa)), usnat: \(String(describing: usnat)), preferences: \(String(describing: preferences))"
}

public init(
gdpr: SPConsent<SPGDPRConsent>? = nil,
ccpa: SPConsent<SPCCPAConsent>? = nil,
usnat: SPConsent<SPUSNatConsent>? = nil
usnat: SPConsent<SPUSNatConsent>? = nil,
preferences: SPConsent<SPPreferencesConsent>? = nil
) {
self.gdpr = gdpr
self.ccpa = ccpa
self.usnat = usnat
self.preferences = preferences
}

public func copy(with zone: NSZone? = nil) -> Any {
SPUserData(
gdpr: gdpr?.copy() as? SPConsent<SPGDPRConsent>,
ccpa: ccpa?.copy() as? SPConsent<SPCCPAConsent>,
usnat: usnat?.copy() as? SPConsent<SPUSNatConsent>
usnat: usnat?.copy() as? SPConsent<SPUSNatConsent>,
preferences: preferences?.copy() as? SPConsent<SPPreferencesConsent>
)
}

Expand All @@ -112,7 +120,8 @@ public class SPConsent<ConsentType: Codable & Equatable & NSCopying>: NSObject,
ccpa?.applies == object.ccpa?.applies &&
ccpa?.consents == object.ccpa?.consents &&
usnat?.applies == object.usnat?.applies &&
usnat?.consents == object.usnat?.consents
usnat?.consents == object.usnat?.consents &&
preferences?.consents == object.preferences?.consents
} else {
return false
}
Expand All @@ -126,6 +135,7 @@ public protocol SPObjcUserData {
func objcCCPAApplies() -> Bool
func objcUSNatConsents() -> SPUSNatConsent?
func objcUSNatApplies() -> Bool
func objcPreferencesConsents() -> SPPreferencesConsent?
}

@objc extension SPUserData: SPObjcUserData {
Expand Down Expand Up @@ -161,4 +171,10 @@ public protocol SPObjcUserData {
public func objcUSNatApplies() -> Bool {
usnat?.applies ?? false
}

/// Returns Preferences consent data if any available.
/// - SeeAlso: `SPPreferencesConsent`
public func objcPreferencesConsents() -> SPPreferencesConsent? {
preferences?.consents
}
}
4 changes: 3 additions & 1 deletion ConsentViewController/Classes/SPCampaignType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

@objc public enum SPCampaignType: Int, Equatable {
case gdpr, ios14, ccpa, usnat, unknown
case gdpr, ios14, ccpa, usnat, preferences, unknown
}

extension SPCampaignType: Codable {
Expand All @@ -20,6 +20,7 @@ extension SPCampaignType: Codable {
case .ccpa: return "CCPA"
case .ios14: return "ios14"
case .usnat: return "usnat"
case .preferences: return "preferences"
default: return "unknown"
}
}
Expand All @@ -30,6 +31,7 @@ extension SPCampaignType: Codable {
case "CCPA": self = .ccpa
case "ios14": self = .ios14
case "usnat": self = .usnat
case "preferences": self = .preferences
default: self = .unknown
}
}
Expand Down
4 changes: 3 additions & 1 deletion ConsentViewController/Classes/SPCampaigns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public typealias SPTargetingParams = [String: String]
/// a active vendor list of that legislation.
@objcMembers public class SPCampaigns: NSObject {
public let environment: SPCampaignEnv
public let gdpr, ccpa, usnat, ios14: SPCampaign?
public let gdpr, ccpa, usnat, ios14, preferences: SPCampaign?

override public var description: String {
"""
Expand All @@ -126,12 +126,14 @@ public typealias SPTargetingParams = [String: String]
ccpa: SPCampaign? = nil,
usnat: SPCampaign? = nil,
ios14: SPCampaign? = nil,
preferences: SPCampaign? = nil,
environment: SPCampaignEnv = .Public
) {
self.gdpr = gdpr
self.ccpa = ccpa
self.usnat = usnat
self.ios14 = ios14
self.preferences = preferences
self.environment = environment
}
}
2 changes: 1 addition & 1 deletion ConsentViewController/Classes/SPConsentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ import SPMobileCore
func report(action: SPAction) {
responsesToReceive += 1
switch action.campaignType {
case .ccpa, .gdpr, .usnat:
case .ccpa, .gdpr, .usnat, .preferences:
spCoordinator.reportAction(action) { [weak self] result in
self?.responsesToReceive -= 1
switch result {
Expand Down
78 changes: 76 additions & 2 deletions ConsentViewController/Classes/SourcePointClient/Adapters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ extension SPMobileCore.SPCampaignType {
return .usnat
case "IOS14":
return .ios14
case "Preferences":
return .preferences

default:
return .unknown
Expand Down Expand Up @@ -255,6 +257,48 @@ extension SPMobileCore.USNatConsent {
}
}

extension SPMobileCore.PreferencesConsent.PreferencesSubType? {
func toNative() -> SPPreferencesConsent.PreferencesSubType {
switch self {
case .aipolicy: return .AIPolicy
case .legalpolicy: return .LegalPolicy
case .privacypolicy: return .PrivacyPolicy
case .termsandconditions: return .TermsAndConditions
case .termsofsale: return .TermsOfSale
case .unknown: return .Unknown
default: return .Unknown
}
}
}

extension SPMobileCore.PreferencesConsent.PreferencesStatusPreferencesChannels {
func toNative() -> SPPreferencesConsent.PreferencesChannels { return .init(channelId: Int(self.channelId), status: self.status) }
}

extension SPMobileCore.PreferencesConsent.PreferencesStatus {
func toNative() -> SPPreferencesConsent.PreferencesStatus {
return .init(
categoryId: Int(self.categoryId),
channels: self.channels?.map { $0.toNative() },
changed: self.changed?.boolValue,
dateConsented: SPDate(string: self.dateConsented?.instantToString() ?? SPDate.now().originalDateString),
subType: self.subType.toNative()
)
}
}

extension SPMobileCore.PreferencesConsent {
func toNative() -> SPPreferencesConsent {
return .init(
uuid: self.uuid,
dateCreated: SPDate(string: self.dateCreated?.instantToString() ?? SPDate.now().originalDateString),
status: self.status?.map { $0.toNative() },
rejectedStatus: self.rejectedStatus?.map { $0.toNative() },
messageId: String(Int(truncating: self.messageId ?? 0))
)
}
}

extension SPUserDataSPConsent<GDPRConsent>? {
func toNative() -> SPConsent<SPGDPRConsent>? {
return SPConsent<SPGDPRConsent>.init(
Expand Down Expand Up @@ -282,12 +326,22 @@ extension SPUserDataSPConsent<USNatConsent>? {
}
}

extension SPUserDataSPConsent<PreferencesConsent>? {
func toNative() -> SPConsent<SPPreferencesConsent>? {
return SPConsent<SPPreferencesConsent>.init(
consents: self?.consents?.toNative(),
applies: true
)
}
}

extension SPMobileCore.SPUserData {
func toNative() -> SPUserData {
return SPUserData(
gdpr: self.gdpr.toNative(),
ccpa: self.ccpa.toNative(),
usnat: self.usnat.toNative()
usnat: self.usnat.toNative(),
preferences: self.preferences.toNative()
)
}
}
Expand Down Expand Up @@ -453,6 +507,7 @@ extension SPCampaignType {
case .ccpa: return .ccpa
case .usnat: return .usnat
case .ios14: return .ios14
case .preferences: return .preferences
case .unknown: return .unknown
}
}
Expand Down Expand Up @@ -501,6 +556,7 @@ extension SourcepointClientCoordinator.State {
ccpa: self.ccpa.toCore(metaData: self.ccpaMetaData),
usNat: self.usnat.toCore(metaData: self.usNatMetaData),
ios14: self.ios14.toCore(),
preferences: emptyPreferencesCampaign(),
authId: self.storedAuthId,
propertyId: Int32(propertyId),
accountId: Int32(accountId),
Expand Down Expand Up @@ -593,6 +649,23 @@ extension SourcepointClientCoordinator.State.AttCampaign? {
}
}

func emptyPreferencesCampaign() -> SPMobileCore.State.PreferencesState {
return SPMobileCore.State.PreferencesState(
metaData: SPMobileCore.State.PreferencesStatePreferencesMetaData(
configurationId: "",
additionsChangeDate: SPDate(date: Date.distantPast).toCore(),
legalDocLiveDate: nil
),
consents: SPMobileCore.PreferencesConsent(
dateCreated: nil,
messageId: nil,
status: nil,
rejectedStatus: nil,
uuid: nil
)
)
}

extension SourcepointClientCoordinator.State.GDPRMetaData? {
func toCore() -> SPMobileCore.State.GDPRStateGDPRMetaData {
return SPMobileCore.State.GDPRStateGDPRMetaData.init(
Expand Down Expand Up @@ -662,7 +735,8 @@ extension SPCampaigns {
gdpr: gdpr.toCore(),
ccpa: ccpa.toCore(),
usnat: usnat.toCore(),
ios14: ios14.toCore()
ios14: ios14.toCore(),
preferences: preferences.toCore()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// Created by Andre Herculano on 14.09.22.
//
// swiftlint:disable type_body_length file_length
// swiftlint:disable type_body_length

import Foundation
import SPMobileCore
Expand Down Expand Up @@ -297,30 +297,6 @@ class SourcepointClientCoordinator: SPClientCoordinator {
}
}

func buildChoiceAllCampaigns(action: SPAction) -> ChoiceAllRequest.ChoiceAllCampaigns {
var gdprApplies: Bool?
var ccpaApplies: Bool?
var usnatApplies: Bool?
switch action.campaignType {
case .gdpr:
gdprApplies = state.gdpr?.applies

case .ccpa:
ccpaApplies = state.ccpa?.applies

case .usnat:
usnatApplies = state.usnat?.applies

case .ios14, .unknown:
break
}
return .init(
gdpr: .init(applies: gdprApplies ?? false),
ccpa: .init(applies: ccpaApplies ?? false),
usnat: .init(applies: usnatApplies ?? false)
)
}

func reportAction(_ action: SPAction, handler: @escaping ActionHandler) {
coreCoordinator.reportAction(
action: action.toCore()
Expand Down
Loading
Loading