Skip to content
Open
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
63 changes: 63 additions & 0 deletions DemoApp/REES46Demo/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
@IBOutlet private weak var resetDidButton: UIButton!
@IBOutlet private weak var showStoriesButton: UIButton!
@IBOutlet private weak var showSnackBarButton: UIButton!
private var showTestPopupButton: UIButton!

public var waitIndicator: SdkActivityIndicator!

Expand Down Expand Up @@ -188,6 +189,48 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
storiesCollectionView.showStories()
}

@objc
private func showTestPopup() {
guard let sdk = globalSDK else {
return
}

let componentsDict: [String: Any] = [
"header": "Test Popup",
"text": "This is a test popup for iOS SDK"
]

let componentsJSON: String
if let componentsData = try? JSONSerialization.data(withJSONObject: componentsDict),
let componentsString = String(data: componentsData, encoding: .utf8) {
componentsJSON = componentsString
} else {
componentsJSON = "{}"
}

let testPopupData: [String: Any] = [
"id": 999,
"channels": ["email"],
"position": "centered",
"delay": 0,
"html": """
<div class="popup-title">Test Popup</div>
<p class="popup-999__intro">This is a test popup for iOS SDK</p>
""",
"components": componentsJSON,
"web_push_system": false,
"popup_actions": "{}"
]

let testPopup = Popup(json: testPopupData)

sdk.popupPresenter.dismissCurrentPopup()

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
sdk.popupPresenter.presentPopup(testPopup)
}
}

func setupSdkDemoAppViews() {
navigationController?.navigationBar.isHidden = true
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.size.width, height: 2000)
Expand All @@ -199,6 +242,8 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
resetDidButton.addTarget(self, action: #selector(didTapReset), for: .touchUpInside)
showStoriesButton.addTarget(self, action: #selector(showStories), for: .touchUpInside)

setupTestPopupButton()

fontInterPreload()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.setupSdkLabels()
Expand Down Expand Up @@ -231,6 +276,24 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
self.waitIndicator.hideIndicatorWhenStopped = true
}

func setupTestPopupButton() {
showTestPopupButton = DemoShopButton(type: .system)
showTestPopupButton.setTitle("Show Test Popup", for: .normal)
showTestPopupButton.setTitleColor(.white, for: .normal)
showTestPopupButton.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(showTestPopupButton)

// Place button next to other test buttons
NSLayoutConstraint.activate([
showTestPopupButton.topAnchor.constraint(equalTo: showStoriesButton.bottomAnchor, constant: 10),
showTestPopupButton.leadingAnchor.constraint(equalTo: showStoriesButton.leadingAnchor),
showTestPopupButton.widthAnchor.constraint(equalTo: showStoriesButton.widthAnchor),
showTestPopupButton.heightAnchor.constraint(equalTo: showStoriesButton.heightAnchor)
])

showTestPopupButton.addTarget(self, action: #selector(showTestPopup), for: .touchUpInside)
}

func fontInterPreload() {
fcmTokenLabel.font = SdkDynamicFont.dynamicFont(textStyle: .headline, weight: .bold)
pushTokenLabel .font = SdkDynamicFont.dynamicFont(textStyle: .headline, weight: .bold)
Expand Down
24 changes: 24 additions & 0 deletions REES46/Classes/Presentation/PopupPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class PopupPresenter {
private var currentPopup: NotificationWidget?
private var popupQueue: [Popup] = []
private let serialQueue = DispatchQueue(label: "com.rees46.popup.presenter")
private var popupShownFlags: [Int: Date] = [:]

public init(sdk: AnyObject) {
self.sdk = sdk
Expand Down Expand Up @@ -55,6 +56,14 @@ public class PopupPresenter {
// MARK: - Private Methods

private func showPopupNow(_ popup: Popup) {
// Check if popup was shown in the last 60 seconds
if let shownDate = popupShownFlags[popup.id] {
let timeSinceShown = Date().timeIntervalSince(shownDate)
if timeSinceShown < 60 {
return // Popup was already shown, skip
}
}

guard let presentingVC = getPresentingViewController(for: popup) else {
return // No VC available or delegate prevented presentation
}
Expand All @@ -68,6 +77,21 @@ public class PopupPresenter {
self?.dismissCurrentPopup()
}
)

// Store popup shown flag in memory for 60 seconds
self.popupShownFlags[popup.id] = Date()

// Remove flag after 60 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 60) { [weak self] in
self?.popupShownFlags.removeValue(forKey: popup.id)
}

// Send popup shown event to server
if let sdk = self.sdk as? PersonalizationSDK {
sdk.trackPopupShown(popupId: popup.id) { _ in
// Handle result (log if needed)
}
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions REES46/Classes/Sdk/Extensions/Popup.extension.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
extension Popup {
init(json: [String: Any]) {
public init(json: [String: Any]) {
self.id = json["id"] as? Int ?? 0
self.channels = json["channels"] as? [String] ?? []
self.position = json["position"] as? String ?? ""
Expand All @@ -17,7 +17,7 @@ extension Popup {
}
}

func extractTitleAndSubtitle() -> (title: String?, subTitle: String?) {
public func extractTitleAndSubtitle() -> (title: String?, subTitle: String?) {
let title = RegexHelper.extract(
using: RegexPattern.title,
from: html
Expand Down
20 changes: 10 additions & 10 deletions REES46/Classes/Sdk/Model/Popup.struct.swift
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import Foundation

public struct Popup: Codable {
enum Position: String {
public enum Position: String {
case centered = "centered"
case bottom = "fixed_bottom"
case top = "top"
}

let id: Int
let channels: [String]
let position: String
let delay: Int
let html: String
let components: PopupComponents?
let web_push_system: Bool
let popup_actions: String
public let id: Int
public let channels: [String]
public let position: String
public let delay: Int
public let html: String
public let components: PopupComponents?
public let web_push_system: Bool
public let popup_actions: String

func getParsedPopupActions() -> PopupActions? {
public func getParsedPopupActions() -> PopupActions? {
guard let data = popup_actions.data(using: .utf8) else { return nil }
let decoder = JSONDecoder()
do {
Expand Down
6 changes: 5 additions & 1 deletion REES46/Classes/Sdk/impl/SimplePersonalizationSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ class SimplePersonalizationSDK: PersonalizationSDK {
trackEventService.trackEvent(event: event, category: category, label: label, value: value, completion: completion)
}

func trackPopupShown(popupId: Int, completion: @escaping (Result<Void, SdkError>) -> Void) {
trackEventService.trackPopupShown(popupId: popupId, completion: completion)
}

func trackSource(source: RecommendedByCase, code: String) {
trackSourceService.trackSource(source: source, code: code)
}
Expand Down Expand Up @@ -1196,7 +1200,7 @@ class SimplePersonalizationSDK: PersonalizationSDK {
}
do {
if data.isEmpty {
if path.contains("clicked") || path.contains("closed") || path.contains("received") {
if path.contains("clicked") || path.contains("closed") || path.contains("received") || path.contains("showed") {
completion(.success([:]))
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public protocol PersonalizationSDK {
func track(event: Event, recommendedBy: RecomendedBy?, completion: @escaping (Result<Void, SdkError>) -> Void)
func trackSource(source: RecommendedByCase, code: String)
func trackEvent(event: String, category: String?, label: String?, value: Int?, completion: @escaping (Result<Void, SdkError>) -> Void)
func trackPopupShown(popupId: Int, completion: @escaping (Result<Void, SdkError>) -> Void)
func recommend(blockId: String, currentProductId: String?, currentCategoryId: String?, locations: String?, imageSize: String?,timeOut: Double?, withLocations: Bool, extended: Bool, completion: @escaping (Result<RecommenderResponse, SdkError>) -> Void)
func suggest(query: String, locations: String?, excludedMerchants: [String]?, excludedBrands: [String]?, timeOut: Double?, extended: String?, completion: @escaping (Result<SearchResponse, SdkError>) -> Void)
func getProductsList(brands: String?, merchants: String?, categories: String?, locations: String?, limit: Int?, page: Int?, filters: [String: Any]?, completion: @escaping(Result<ProductsListResponse, SdkError>) -> Void)
Expand Down
26 changes: 26 additions & 0 deletions REES46/Classes/Tracking/Service/impl/TrackServiceImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TrackEventServiceImpl: TrackEventServiceProtocol {
private struct Constants {
static let trackStoriesPath = "track/stories"
static let trackCustomEventPath = "push/custom"
static let popupShownPath = "popup/showed"

static let id = "id"
static let amount = "amount"
Expand Down Expand Up @@ -283,6 +284,31 @@ class TrackEventServiceImpl: TrackEventServiceProtocol {
}
}

func trackPopupShown(popupId: Int, completion: @escaping (Result<Void, SdkError>) -> Void) {
guard let sdk = sdk else {
completion(.failure(.custom(error: "trackPopupShown: SDK is not initialized")))
return
}

sessionQueue.addOperation {
let params: [String: Any] = [
Constants.shopId: sdk.shopId,
Constants.did: sdk.deviceId,
Constants.sid: sdk.userSeance,
"popup": popupId
]

sdk.postRequest(path: Constants.popupShownPath, params: params) { result in
switch result {
case .success:
completion(.success(Void()))
case .failure(let error):
completion(.failure(error))
}
}
}
}

private func showPopup(jsonResult: [String: Any]) {
guard let popupData = jsonResult["popup"] as? [String: Any], !popupData.isEmpty else {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import Foundation
protocol TrackEventServiceProtocol {
func track(event: Event, recommendedBy: RecomendedBy?, completion: @escaping (Result<Void, SdkError>) -> Void)
func trackEvent(event: String, category: String?, label: String?, value: Int?, completion: @escaping (Result<Void, SdkError>) -> Void)
func trackPopupShown(popupId: Int, completion: @escaping (Result<Void, SdkError>) -> Void)
}
Loading