Skip to content
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

[APP-2879] Force show creatives on client apps for testing #97

Merged
merged 8 commits into from
Jun 21, 2024
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
4 changes: 3 additions & 1 deletion Example/CreativeUITest/CreativeUITest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ final class CreativeUITest: XCTestCase, BaseXCTestCase {
}

func testLoadCreative_clickClose_closesCreative() {
launch(with: .production)
launch(with: .production, extras: [
"SKIP_FATIGUE_ON_CREATIVE": "true"
])

HomePage
.tapOnPushMeToCreative()
Expand Down
9 changes: 9 additions & 0 deletions Example/CreativeUITest/Protocols/BaseXCTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,21 @@ extension BaseXCTestCase {
}

func launch(with mode: Mode) {
launch(with: mode, extras: [:])
}

func launch(with mode: Mode, extras: [String: String] = [:]) {
let app = XCUIApplication()
app.launchEnvironment = [
"COM_ATTENTIVE_EXAMPLE_DOMAIN" : "mobileapps",
"COM_ATTENTIVE_EXAMPLE_MODE" : mode.rawValue,
"COM_ATTENTIVE_EXAMPLE_IS_UI_TEST" : "YES",
]

if !extras.isEmpty {
app.launchEnvironment.merge(extras) { _, new in new }
}

app.launch()
}

Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,28 @@ ATTNSDK *sdk = [[ATTNSDK alloc] initWithDomain:@"domain"];
[sdk updateDomain: @"differentDomain"];
```

### Skip Fatigue on Creative

Determinates if fatigue rules evaluation will be skipped for Creative. Default value is `false`.

#### Swift

```swift
let sdk = ATTNSDK(domain: "domain")
sdk.skipFatigueOnCreative = true
```

#### Objective-C

```objective-c
ATTNSDK *sdk = [[ATTNSDK alloc] initWithDomain:@"domain"];
sdk.skipFatigueOnCreative = YES;
```

Alternatively, `SKIP_FATIGUE_ON_CREATIVE` can be added as an environment value in the project scheme or even included in CI files.

Environment value can be a string with value `"true"` or `"false"`.

### Clear the current user

If the user logs out then the current user identifiers should be deleted:
Expand Down
3 changes: 3 additions & 0 deletions Sources/ATTNConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
import Foundation

struct ATTNConstants {
private init() { }

static let sdkVersion = "0.6.0"
static let skipFatigueEnvKey = "SKIP_FATIGUE_ON_CREATIVE"
}
22 changes: 22 additions & 0 deletions Sources/Helpers/Extension/ATTNSDK+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// ATTNSDK+Extension.swift
// attentive-ios-sdk-framework
//
// Created by Vladimir - Work on 2024-06-17.
//

import Foundation

extension ATTNSDK {
func send(event: ATTNEvent) {
api.send(event: event, userIdentity: userIdentity)
}

func initializeSkipFatigueOnCreatives() {
if let skipFatigueValue = ProcessInfo.processInfo.environment[ATTNConstants.skipFatigueEnvKey] {
self.skipFatigueOnCreative = skipFatigueValue.booleanValue
} else {
self.skipFatigueOnCreative = false
}
}
}
14 changes: 14 additions & 0 deletions Sources/Helpers/Extension/Boolean+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Boolean+Extension.swift
// attentive-ios-sdk-framework
//
// Created by Vladimir - Work on 2024-06-17.
//

import Foundation

extension Bool {
var stringValue: String {
self ? "true" : "false"
}
}
14 changes: 14 additions & 0 deletions Sources/Helpers/Extension/String+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// String+Extension.swift
// attentive-ios-sdk-framework
//
// Created by Vladimir - Work on 2024-06-17.
//

import Foundation

extension String {
var booleanValue: Bool {
self == "true"
}
}
94 changes: 58 additions & 36 deletions Sources/Public/SDK/ATTNSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public final class ATTNSDK: NSObject {
private var mode: ATTNSDKMode
private var urlBuilder: ATTNCreativeUrlProviding = ATTNCreativeUrlProvider()

/// Determinates if fatigue rules evaluation will be skipped for Creative. Default value is false.
@objc public var skipFatigueOnCreative: Bool = false

public init(domain: String, mode: ATTNSDKMode) {
NSLog("init attentive_ios_sdk v%@", ATTNConstants.sdkVersion)
self.domain = domain
Expand All @@ -62,6 +65,7 @@ public final class ATTNSDK: NSObject {
super.init()

self.sendInfoEvent()
self.initializeSkipFatigueOnCreatives()
}

@objc(initWithDomain:)
Expand All @@ -84,11 +88,57 @@ public final class ATTNSDK: NSObject {

@objc(trigger:)
public func trigger(_ view: UIView) {
trigger(view, handler: nil)
launchCreative(parentView: view)
}

@objc(trigger:handler:)
public func trigger(_ view: UIView, handler: ATTNCreativeTriggerCompletionHandler?) {
launchCreative(parentView: view, handler: handler)
}

@objc(trigger:creativeId:)
public func trigger(_ view: UIView, creativeId: String) {
launchCreative(parentView: view, creativeId: creativeId, handler: nil)
}

@objc(trigger:creativeId:handler:)
public func trigger(_ view: UIView, creativeId: String, handler: ATTNCreativeTriggerCompletionHandler?) {
launchCreative(parentView: view, creativeId: creativeId, handler: handler)
}

@objc(clearUser)
public func clearUser() {
userIdentity.clearUser()
}

@objc(updateDomain:)
public func update(domain: String) {
guard self.domain != domain else { return }
self.domain = domain
api.update(domain: domain)
api.send(userIdentity: userIdentity)
}
}

// MARK: Private Helpers
fileprivate extension ATTNSDK {
func sendInfoEvent() {
api.send(event: ATTNInfoEvent(), userIdentity: userIdentity)
}

func closeCreative() {
webView?.removeFromSuperview()
webView = nil
ATTNSDK.isCreativeOpen = false
triggerHandler?(ATTNCreativeTriggerStatus.closed)
NSLog("Successfully closed creative")
}

func launchCreative(
parentView view: UIView,
creativeId: String? = nil,
handler: ATTNCreativeTriggerCompletionHandler? = nil
) {
parentView = view
triggerHandler = handler

Expand All @@ -107,9 +157,13 @@ public final class ATTNSDK: NSObject {
NSLog("The iOS version is new enough, continuing to show the Attentive creative.")

let creativePageUrl = urlBuilder.buildCompanyCreativeUrl(
forDomain: domain,
mode: mode.rawValue,
userIdentity: userIdentity
configuration: ATTNCreativeUrlConfig(
domain: domain,
creativeId: creativeId,
skipFatigue: skipFatigueOnCreative,
mode: mode.rawValue,
userIdentity: userIdentity
)
)

NSLog("Requesting creative page url: %@", creativePageUrl)
Expand Down Expand Up @@ -142,34 +196,6 @@ public final class ATTNSDK: NSObject {
webView.backgroundColor = .clear
}
}

@objc(clearUser)
public func clearUser() {
userIdentity.clearUser()
}

@objc(updateDomain:)
public func update(domain: String) {
guard self.domain != domain else { return }
self.domain = domain
api.update(domain: domain)
api.send(userIdentity: userIdentity)
}
}

// MARK: Private Helpers
fileprivate extension ATTNSDK {
func sendInfoEvent() {
api.send(event: ATTNInfoEvent(), userIdentity: userIdentity)
}

func closeCreative() {
webView?.removeFromSuperview()
webView = nil
ATTNSDK.isCreativeOpen = false
triggerHandler?(ATTNCreativeTriggerStatus.closed)
NSLog("Successfully closed creative")
}
}

// MARK: WKScriptMessageHandler
Expand Down Expand Up @@ -270,10 +296,6 @@ extension ATTNSDK: WKNavigationDelegate {

// MARK: Internal Helpers
extension ATTNSDK {
func send(event: ATTNEvent) {
api.send(event: event, userIdentity: userIdentity)
}

convenience init(domain: String, mode: ATTNSDKMode, urlBuilder: ATTNCreativeUrlProviding) {
self.init(domain: domain, mode: mode)
self.urlBuilder = urlBuilder
Expand Down
40 changes: 20 additions & 20 deletions Sources/URLProviders/ATTNCreativeUrlProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
import Foundation

protocol ATTNCreativeUrlProviding {
func buildCompanyCreativeUrl(
forDomain domain: String,
mode: String,
userIdentity: ATTNUserIdentity
) -> String
func buildCompanyCreativeUrl(configuration: ATTNCreativeUrlConfig) -> String
}

struct ATTNCreativeUrlProvider: ATTNCreativeUrlProviding {
Expand All @@ -28,54 +24,58 @@ struct ATTNCreativeUrlProvider: ATTNCreativeUrlProviding {
self.appInfo = appInfo
}

func buildCompanyCreativeUrl(
forDomain domain: String,
mode: String,
userIdentity: ATTNUserIdentity
) -> String {
func buildCompanyCreativeUrl(configuration: ATTNCreativeUrlConfig) -> String {
var components = URLComponents()
components.scheme = Constants.scheme
components.host = Constants.host
components.path = Constants.path

var queryItems = [
URLQueryItem(name: "domain", value: domain)
URLQueryItem(name: "domain", value: configuration.domain)
]

if mode == "debug" {
queryItems.append(URLQueryItem(name: mode, value: "matter-trip-grass-symbol"))
if configuration.mode == "debug" {
queryItems.append(URLQueryItem(name: configuration.mode, value: "matter-trip-grass-symbol"))
}

queryItems.append(URLQueryItem(name: "vid", value: userIdentity.visitorId))
queryItems.append(URLQueryItem(name: "vid", value: configuration.userIdentity.visitorId))

if let clientUserId = userIdentity.identifiers[ATTNIdentifierType.clientUserId] as? String {
if let clientUserId = configuration.userIdentity.identifiers[ATTNIdentifierType.clientUserId] as? String {
queryItems.append(URLQueryItem(name: "cuid", value: clientUserId))
}

if let phone = userIdentity.identifiers[ATTNIdentifierType.phone] as? String {
if let phone = configuration.userIdentity.identifiers[ATTNIdentifierType.phone] as? String {
queryItems.append(URLQueryItem(name: "p", value: phone))
}

if let email = userIdentity.identifiers[ATTNIdentifierType.email] as? String {
if let email = configuration.userIdentity.identifiers[ATTNIdentifierType.email] as? String {
queryItems.append(URLQueryItem(name: "e", value: email))
}

if let klaviyoId = userIdentity.identifiers[ATTNIdentifierType.klaviyoId] as? String {
if let klaviyoId = configuration.userIdentity.identifiers[ATTNIdentifierType.klaviyoId] as? String {
queryItems.append(URLQueryItem(name: "kid", value: klaviyoId))
}

if let shopifyId = userIdentity.identifiers[ATTNIdentifierType.shopifyId] as? String {
if let shopifyId = configuration.userIdentity.identifiers[ATTNIdentifierType.shopifyId] as? String {
queryItems.append(URLQueryItem(name: "sid", value: shopifyId))
}

if let customIdentifiersJson = getCustomIdentifiersJson(userIdentity: userIdentity) {
if let customIdentifiersJson = getCustomIdentifiersJson(userIdentity: configuration.userIdentity) {
queryItems.append(URLQueryItem(name: "cstm", value: customIdentifiersJson))
}

// Add SDK info just for analytics purposes
queryItems.append(URLQueryItem(name: "sdkVersion", value: appInfo.getSdkVersion()))
queryItems.append(URLQueryItem(name: "sdkName", value: appInfo.getSdkName()))

if configuration.skipFatigue {
queryItems.append(URLQueryItem(name: "skipFatigue", value: configuration.skipFatigue.stringValue))
}

if let creativeId = configuration.creativeId {
queryItems.append(URLQueryItem(name: "attn_creative_id", value: creativeId))
}

components.queryItems = queryItems

return components.string ?? ""
Expand Down
16 changes: 16 additions & 0 deletions Sources/URLProviders/Configs/ATTNCreativeUrlConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// ATTNCreativeUrlConfig.swift
// attentive-ios-sdk-framework
//
// Created by Vladimir - Work on 2024-06-14.
//

import Foundation

struct ATTNCreativeUrlConfig {
let domain: String
let creativeId: String?
let skipFatigue: Bool
let mode: String
let userIdentity: ATTNUserIdentity
}
6 changes: 4 additions & 2 deletions Tests/Doubles/Spies/ATTNCreativeUrlProviderSpy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import Foundation
final class ATTNCreativeUrlProviderSpy: ATTNCreativeUrlProviding {
private(set) var buildCompanyCreativeUrlWasCalled = false
private(set) var usedDomain: String?
private(set) var usedCreativeId: String?

func buildCompanyCreativeUrl(forDomain domain: String, mode: String, userIdentity: ATTNSDKFramework.ATTNUserIdentity) -> String {
func buildCompanyCreativeUrl(configuration: ATTNSDKFramework.ATTNCreativeUrlConfig) -> String {
buildCompanyCreativeUrlWasCalled = true
usedDomain = domain
usedDomain = configuration.domain
usedCreativeId = configuration.creativeId
return ""
}
}
Loading
Loading