Skip to content
Merged
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
138 changes: 74 additions & 64 deletions ConsentViewController/Classes/ConsentViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ import Reachability
sometimes, depending on how the scenario was set up, the message might not show
at all, thus this call back won't be called.
*/
public var willShowMessage: Callback?
public var onMessageReady: Callback?

/**
A `Callback` that will be called when the user selects an option on the WebView.
Expand Down Expand Up @@ -258,37 +258,42 @@ import Reachability
return nil
}

/// :nodoc:
override open func viewDidLoad() {
super.viewDidLoad()
webView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
let messageUrl: URL
private enum MessageStatus { case notStarted, loading, loaded }
private var messageStatus = MessageStatus.notStarted
public func loadMessage() {
if(messageStatus == .loading || messageStatus == .loaded) { return }

do {
messageUrl = try sourcePoint.getMessageUrl(forTargetingParams: targetingParams, debugLevel: debugLevel.rawValue)
messageStatus = .loading
loadView()
let messageUrl = try sourcePoint.getMessageUrl(forTargetingParams: targetingParams, debugLevel: debugLevel.rawValue)
print ("url: \((messageUrl.absoluteString))")
UserDefaults.standard.setValue(true, forKey: "IABConsent_CMPPresent")
setSubjectToGDPR()
try setSubjectToGDPR()
guard Reachability()!.connection != .none else {
onErrorOccurred?(NoInternetConnection())
messageStatus = .notStarted
return
}
webView.load(URLRequest(url: messageUrl))
messageStatus = .loaded
} catch let error as ConsentViewControllerError {
messageStatus = .notStarted
onErrorOccurred?(error)
return
} catch {}
}

private func setSubjectToGDPR() {
if(UserDefaults.standard.string(forKey: ConsentViewController.IAB_CONSENT_SUBJECT_TO_GDPR) != nil) { return }
/// :nodoc:
override open func viewDidLoad() {
super.viewDidLoad()
loadMessage()
}

do {
let gdprStatus = try sourcePoint.getGdprStatus()
UserDefaults.standard.setValue(String(gdprStatus), forKey: ConsentViewController.IAB_CONSENT_SUBJECT_TO_GDPR)
} catch {
print(error)
}
private func setSubjectToGDPR() throws {
if(UserDefaults.standard.string(forKey: ConsentViewController.IAB_CONSENT_SUBJECT_TO_GDPR) != nil) { return }
let gdprStatus = try sourcePoint.getGdprStatus()
UserDefaults.standard.setValue(String(gdprStatus), forKey: ConsentViewController.IAB_CONSENT_SUBJECT_TO_GDPR)
}

/**
Expand Down Expand Up @@ -407,71 +412,76 @@ import Reachability
userDefaults.setValue(String(parsedPurposeConsents), forKey: ConsentViewController.IAB_CONSENT_PARSED_PURPOSE_CONSENTS)
}

private func isDefined(_ string: String) -> Bool {
return string != "undefined"
private func onReceiveMessage(willShow: Bool) {
willShow ? onMessageReady?(self) : done()
}

/// :nodoc:
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard
let messageBody = message.body as? [String: Any],
let name = messageBody["name"] as? String,
let body = messageBody["body"] as? [String: Any?]
else { return }
private func onMessageChoiceSelect(choiceType: Int) {
guard Reachability()!.connection != .none else {
onErrorOccurred?(NoInternetConnection())
done()
return
}
self.choiceType = choiceType
onMessageChoiceSelect?(self)
}

private func onInteractionComplete(euconsent: String, consentUUID: String) {
self.euconsent = euconsent
self.consentUUID = consentUUID
do {
try storeIABVars(euconsent)
let userDefaults = UserDefaults.standard
userDefaults.setValue(euconsent, forKey: ConsentViewController.EU_CONSENT_KEY)
userDefaults.setValue(consentUUID, forKey: ConsentViewController.CONSENT_UUID_KEY)
userDefaults.synchronize()
} catch let error as ConsentViewControllerError {
onErrorOccurred?(error)
} catch {}
done()
}

private func onErrorOccurred(_ error: ConsentViewControllerError) {
onErrorOccurred?(error)
done()
}

private func handleMessage(withName name: String, andBody body: [String:Any?]) {
switch name {
case "onReceiveMessageData": // when the message is first loaded
if(body["willShowMessage"] as! Int) == 1 {
webView.frame = webView.superview!.bounds
willShowMessage?(self)
return
}
done()
guard let willShow = body["willShowMessage"] as? Int else { fallthrough }
onReceiveMessage(willShow: willShow == 1)
case "onMessageChoiceSelect": // when a choice is selected
guard let choiceType = body["choiceType"] as? Int else { return }
guard Reachability()!.connection != .none else {
onErrorOccurred?(NoInternetConnection())
done()
return
}
self.choiceType = choiceType
onMessageChoiceSelect?(self)

guard let choiceType = body["choiceType"] as? Int else { fallthrough }
onMessageChoiceSelect(choiceType: choiceType)
case "interactionComplete": // when interaction with message is complete
let userDefaults = UserDefaults.standard
guard
let euconsent = body["euconsent"] as? String,
let consentUUID = body["consentUUID"] as? String
else {
print("Could not get EUConsent and ConsentUUID")
done()
return
}

self.euconsent = euconsent
self.consentUUID = consentUUID
do {
try storeIABVars(euconsent)
userDefaults.setValue(euconsent, forKey: ConsentViewController.EU_CONSENT_KEY)
userDefaults.setValue(consentUUID, forKey: ConsentViewController.CONSENT_UUID_KEY)
userDefaults.synchronize()
} catch let error as ConsentViewControllerError {
onErrorOccurred?(error)
} catch {}
done()
else { fallthrough }
onInteractionComplete(euconsent: euconsent, consentUUID: consentUUID)
case "onErrorOccurred":
let errorType = body["errorType"] as? String ?? ""
onErrorOccurred?(WebViewErrors[errorType] ?? PrivacyManagerUnknownError())
done()
onErrorOccurred(WebViewErrors[body["errorType"] as? String ?? ""] ?? PrivacyManagerUnknownError())
default:
print("userContentController was called but the message body: \(name) is unknown.")
done()
onErrorOccurred(PrivacyManagerUnknownMessageResponse(name: name, body: body))
}
}

/// :nodoc:
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard
let messageBody = message.body as? [String: Any],
let name = messageBody["name"] as? String,
let body = messageBody["body"] as? [String: Any?]
else {
onErrorOccurred(PrivacyManagerUnknownMessageResponse(name: "", body: ["":""]))
return
}
handleMessage(withName: name, andBody: body)
}

private func done() {
onInteractionComplete?(self)
webView.removeFromSuperview()
}
}

Expand Down
38 changes: 24 additions & 14 deletions ConsentViewController/Classes/ConsentViewControllerError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct UnableToParseConsentStringError: ConsentViewControllerError {
public var errorDescription: String? { get { return "Could not parse the raw string \(euConsent) into a ConsentString" } }

init(euConsent: String) { self.euConsent = euConsent }
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)" } }
}

public struct InvalidMessageURLError: ConsentViewControllerError {
Expand All @@ -27,12 +27,11 @@ public struct InvalidMessageURLError: ConsentViewControllerError {
public var helpAnchor: String? { get { return "Please make sure the query params are URL encodable." } }

init(urlString: String) { self.urlString = urlString }
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
}

public struct InvalidURLError: ConsentViewControllerError {
private let urlName: String
private let urlString: String
private let urlName: String, urlString: String

public var failureReason: String? { get { return "Failed to parse \(urlName)." } }
public var errorDescription: String? { get { return "Could not convert \(urlString) into URL." } }
Expand All @@ -42,12 +41,11 @@ public struct InvalidURLError: ConsentViewControllerError {
self.urlName = urlName
self.urlString = urlString
}
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
}

public struct SiteIDNotFound: ConsentViewControllerError {
private let accountId: String
private let siteName: String
private let accountId: String, siteName: String

public var failureReason: String? { get { return "Could not find site ID)." } }
public var errorDescription: String? { get { return "Could not find a site with name \(siteName) for the account id \(accountId)" } }
Expand All @@ -57,7 +55,7 @@ public struct SiteIDNotFound: ConsentViewControllerError {
self.accountId = accountId
self.siteName = siteName
}
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
}

public struct GdprStatusNotFound: ConsentViewControllerError {
Expand All @@ -67,13 +65,13 @@ public struct GdprStatusNotFound: ConsentViewControllerError {
public var helpAnchor: String? { get { return "Make sure \(gdprStatusUrl) is correct." } }

init(gdprStatusUrl: URL) { self.gdprStatusUrl = gdprStatusUrl }
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
}

public struct ConsentsAPIError: ConsentViewControllerError {
public var failureReason: String? { get { return "Failed to get Custom Consents" } }
public var errorDescription: String? { get { return "Failed to either get custom consents or parse the endpoint's response" } }
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)" } }
}

public let WebViewErrors: [String : ConsentViewControllerError] = [
Expand All @@ -85,22 +83,34 @@ public struct PrivacyManagerLoadError: ConsentViewControllerError {
public var failureReason: String? { get { return "Failed start the Privacy Manager" } }
public var errorDescription: String? { get { return "Could not load the Privacy Manager due to a javascript error." } }
public var helpAnchor: String? { get { return "This is most probably happening due to a misconfiguration on the Publisher's portal." } }
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
}

public struct PrivacyManagerSaveError: ConsentViewControllerError {
public var failureReason: String? { get { return "Failed to save user consents on Privacy Manager" } }
public var errorDescription: String? { get { return "Something wrong happened while saving the privacy settings on the Privacy Manager" } }
public var helpAnchor: String? { get { return "This might have occurred due to faulty internet connection." } }
public var description: String { get {return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
public var description: String { get { return "\(failureReason!)\n\(errorDescription!)\n\(helpAnchor!)" } }
}

public struct PrivacyManagerUnknownMessageResponse: ConsentViewControllerError {
private let name: String, body: [String:Any?]
init(name: String, body: [String:Any?]) {
self.name = name
self.body = body
}
public var failureReason: String? { get {
return "Couldn't parse message in userContentController. Called with name: \(name) and body: \(body)"
}}
public var description: String { get { return "\(failureReason!)\n" } }
}

public struct PrivacyManagerUnknownError: ConsentViewControllerError {
public var failureReason: String? { get { return "Something bad happened in the javascript world." } }
public var description: String { get {return "\(failureReason!)\n" } }
public var description: String { get { return "\(failureReason!)\n" } }
}

public struct NoInternetConnection: ConsentViewControllerError {
public var failureReason: String? { get { return "The device is not connected to the internet." } }
public var description: String { get {return "\(failureReason!)\n" } }
public var description: String { get { return "\(failureReason!)\n" } }
}
Loading