CLIENT_DOCUMENTATION.md 39.1 KB

SwiftWarplyFramework Client Documentation

🚀 5-Minute Setup Guide

Get started with SwiftWarplyFramework in just 5 minutes!

Version: 2.1.0 | Minimum iOS: 17.0 | Swift: 5.0+


📋 Quick Navigation

🚀 5-Minute SetupStart Here!
📦 Installation
⚙️ Configuration
🔧 Initialization
📱 First API Call
Verification

📚 Complete Documentation
🔐 Authentication
🎯 Campaigns
🎫 Coupons
🏪 Market Pass
📡 Events
📱 Push Notifications
⚠️ Error Handling
🔄 Migration Guide


🚀 5-Minute Setup Guide

Prerequisites

Before you start, make sure you have:

  • iOS 17.0+ project
  • Swift 5.0+
  • CocoaPods installed
  • Web ID and Merchant ID from Warply

Step 1: Installation

1.1 Add to Podfile

Open your Podfile and add:

platform :ios, '17.0'

target 'YourApp' do
  use_frameworks!

  pod 'SwiftWarplyFramework', :git => 'https://git@git.warp.ly/open-source/warply_sdk_framework.git', :tag => 2.1.0
end

1.2 Install Framework

Run in terminal:

pod install

1.3 Open Workspace

open YourApp.xcworkspace

Verification: Framework should appear in your Pods project


Step 2: Configuration

2.1 Import Framework

In your AppDelegate.swift:

import SwiftWarplyFramework

2.2 Configure in AppDelegate

Add this to didFinishLaunchingWithOptions:

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // 🔧 Configure the SDK
        WarplySDK.shared.configure(
            webId: "YOUR_WEB_ID",        // Replace with your Web ID
            merchantId: "YOUR_MERCHANT_ID", // Replace with your Merchant ID
            environment: .production      // Use .development for testing
        )

        return true
    }
}

Verification: No compilation errors after adding this code


Step 3: Initialization

3.1 Initialize SDK

Add this right after the configure call:

// 🚀 Initialize the SDK
WarplySDK.shared.initialize { success in
    if success {
        print("✅ WarplySDK initialized successfully")
    } else {
        print("❌ WarplySDK initialization failed")
    }
}

3.2 Complete AppDelegate Example

Your complete AppDelegate.swift should look like this:

import UIKit
import SwiftWarplyFramework

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // 🔧 Configure the SDK
        WarplySDK.shared.configure(
            webId: "YOUR_WEB_ID",
            merchantId: "YOUR_MERCHANT_ID",
            environment: .production
        )

        // 🚀 Initialize the SDK
        WarplySDK.shared.initialize { success in
            if success {
                print("✅ WarplySDK initialized successfully")
            } else {
                print("❌ WarplySDK initialization failed")
            }
        }

        return true
    }
}

Verification: Run your app and check console for "✅ WarplySDK initialized successfully"


Step 4: First API Call

4.1 Test in a ViewController

Add this to any ViewController's viewDidLoad:

import SwiftWarplyFramework

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // 📱 Test API call
        testWarplySDK()
    }

    func testWarplySDK() {
        // Simple completion handler approach
        WarplySDK.shared.getCampaigns(language: "en") { campaigns in
            DispatchQueue.main.async {
                if let campaigns = campaigns {
                    print("🎉 Success! Retrieved \(campaigns.count) campaigns")
                    // TODO: Update your UI here
                } else {
                    print("⚠️ No campaigns received")
                }
            }
        }
    }
}

4.2 Modern Async/Await Approach (Recommended)

For modern Swift development:

func testWarplySDKAsync() {
    Task {
        do {
            let campaigns = try await WarplySDK.shared.getCampaigns(language: "en")
            print("🎉 Success! Retrieved \(campaigns.count) campaigns")

            // Update UI on main thread
            await MainActor.run {
                // TODO: Update your UI here
            }
        } catch {
            print("❌ Error: \(error.localizedDescription)")
        }
    }
}

Verification: Run your app and check console for "🎉 Success! Retrieved X campaigns"


Step 5: Verification

5.1 Check Console Output

You should see:

✅ WarplySDK initialized successfully
🎉 Success! Retrieved X campaigns

5.2 Common Issues & Quick Fixes

Issue Solution
"❌ WarplySDK initialization failed" Check your Web ID and Merchant ID
"⚠️ No campaigns received" Verify network connection and credentials
Compilation errors Make sure iOS deployment target is 17.0+
Pod install fails Update CocoaPods: sudo gem install cocoapods

5.3 Next Steps

🎉 Congratulations! Your SwiftWarplyFramework is now set up and working!

What's next?


📱 View Controller Integration

Using Framework View Controllers

The SwiftWarplyFramework provides ready-to-use view controllers that you can integrate into your app's navigation flow.

Available View Controllers

View Controller Purpose Description
MyRewardsViewController Main rewards screen Displays campaigns, offers, and banners
ProfileViewController User profile & coupons Shows user profile and coupon management
CouponViewController Individual coupon details Displays detailed coupon information

Basic Integration Example

import SwiftWarplyFramework

class MainViewController: UIViewController {

    @IBAction func showRewardsButtonTapped(_ sender: UIButton) {
        // Create the rewards view controller
        let rewardsVC = MyRewardsViewController()

        // Push onto navigation stack
        navigationController?.pushViewController(rewardsVC, animated: true)
    }

    @IBAction func showProfileButtonTapped(_ sender: UIButton) {
        // Create the profile view controller
        let profileVC = ProfileViewController()

        // Push onto navigation stack
        navigationController?.pushViewController(profileVC, animated: true)
    }
}

Modal Presentation

class MainViewController: UIViewController {

    @IBAction func showRewardsModal(_ sender: UIButton) {
        let rewardsVC = MyRewardsViewController()

        // Wrap in navigation controller for modal presentation
        let navController = UINavigationController(rootViewController: rewardsVC)
        navController.modalPresentationStyle = .fullScreen

        present(navController, animated: true)
    }
}

Tab Bar Integration

class TabBarController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setupTabs()
    }

    private func setupTabs() {
        // Main app tab
        let homeVC = HomeViewController()
        let homeNav = UINavigationController(rootViewController: homeVC)
        homeNav.tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house"), tag: 0)

        // Warply rewards tab
        let rewardsVC = MyRewardsViewController()
        let rewardsNav = UINavigationController(rootViewController: rewardsVC)
        rewardsNav.tabBarItem = UITabBarItem(title: "Rewards", image: UIImage(systemName: "gift"), tag: 1)

        // Profile tab
        let profileVC = ProfileViewController()
        let profileNav = UINavigationController(rootViewController: profileVC)
        profileNav.tabBarItem = UITabBarItem(title: "Profile", image: UIImage(systemName: "person"), tag: 2)

        viewControllers = [homeNav, rewardsNav, profileNav]
    }
}

Custom Navigation Setup

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationAppearance()
    }

    private func setupNavigationAppearance() {
        // Customize navigation bar for framework view controllers
        navigationBar.prefersLargeTitles = false
        navigationBar.tintColor = .systemBlue

        // Set up navigation bar appearance
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = .systemBackground

        navigationBar.standardAppearance = appearance
        navigationBar.scrollEdgeAppearance = appearance
    }

    func showWarplyRewards() {
        let rewardsVC = MyRewardsViewController()
        pushViewController(rewardsVC, animated: true)
    }

    func showWarplyProfile() {
        let profileVC = ProfileViewController()
        pushViewController(profileVC, animated: true)
    }
}

Programmatic Navigation Flow

class WarplyFlowCoordinator {
    private weak var navigationController: UINavigationController?

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func startRewardsFlow() {
        let rewardsVC = MyRewardsViewController()
        navigationController?.pushViewController(rewardsVC, animated: true)
    }

    func showProfile() {
        let profileVC = ProfileViewController()
        navigationController?.pushViewController(profileVC, animated: true)
    }

    func showCouponDetails(with offer: OfferModel) {
        let couponVC = CouponViewController()
        couponVC.coupon = offer
        navigationController?.pushViewController(couponVC, animated: true)
    }

    func dismissToRoot() {
        navigationController?.popToRootViewController(animated: true)
    }
}

SwiftUI Integration

import SwiftUI
import SwiftWarplyFramework

struct WarplyRewardsView: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> MyRewardsViewController {
        return MyRewardsViewController()
    }

    func updateUIViewController(_ uiViewController: MyRewardsViewController, context: Context) {
        // Update if needed
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink("Show Rewards") {
                    WarplyRewardsView()
                        .navigationBarTitleDisplayMode(.inline)
                }
                .padding()

                NavigationLink("Show Profile") {
                    WarplyProfileView()
                        .navigationBarTitleDisplayMode(.inline)
                }
                .padding()
            }
            .navigationTitle("Main Menu")
        }
    }
}

struct WarplyProfileView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> ProfileViewController {
        return ProfileViewController()
    }

    func updateUIViewController(_ uiViewController: ProfileViewController, context: Context) {
        // Update if needed
    }
}

Handling Navigation Events

class MainViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setupWarplyEventListeners()
    }

    private func setupWarplyEventListeners() {
        // Listen for navigation events from framework
        let subscription = WarplySDK.shared.subscribe(to: "navigation_requested") { [weak self] data in
            DispatchQueue.main.async {
                self?.handleWarplyNavigation(data)
            }
        }
    }

    private func handleWarplyNavigation(_ data: Any) {
        // Handle navigation requests from framework view controllers
        if let navigationData = data as? [String: Any],
           let destination = navigationData["destination"] as? String {

            switch destination {
            case "profile":
                showProfile()
            case "coupon_details":
                if let couponId = navigationData["coupon_id"] as? String {
                    showCouponDetails(couponId: couponId)
                }
            default:
                break
            }
        }
    }

    private func showProfile() {
        let profileVC = ProfileViewController()
        navigationController?.pushViewController(profileVC, animated: true)
    }

    private func showCouponDetails(couponId: String) {
        let couponVC = CouponViewController()
        // Configure with coupon ID
        navigationController?.pushViewController(couponVC, animated: true)
    }
}

Best Practices for View Controller Integration

class BestPracticesExample: UIViewController {

    // ✅ Good: Keep reference to avoid memory issues
    private var currentWarplyVC: UIViewController?

    func showRewardsViewController() {
        // ✅ Good: Check if already presented
        guard currentWarplyVC == nil else { return }

        let rewardsVC = MyRewardsViewController()
        currentWarplyVC = rewardsVC

        // ✅ Good: Proper navigation setup
        if let navigationController = navigationController {
            navigationController.pushViewController(rewardsVC, animated: true)
        } else {
            // ✅ Good: Fallback for modal presentation
            let navController = UINavigationController(rootViewController: rewardsVC)
            present(navController, animated: true)
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // ✅ Good: Clean up reference when returning
        if currentWarplyVC != nil && !children.contains(where: { $0 is MyRewardsViewController }) {
            currentWarplyVC = nil
        }
    }

    // ✅ Good: Provide easy access methods
    func showWarplyRewards() {
        let rewardsVC = MyRewardsViewController()
        navigationController?.pushViewController(rewardsVC, animated: true)
    }

    func showWarplyProfile() {
        let profileVC = ProfileViewController()
        navigationController?.pushViewController(profileVC, animated: true)
    }

    // ✅ Good: Handle authentication state
    func showWarplyContentIfAuthenticated() {
        // Check authentication first
        WarplySDK.shared.verifyTicket(guid: "user-guid", ticket: "user-ticket") { [weak self] response in
            DispatchQueue.main.async {
                if response?.getStatus == 1 {
                    self?.showWarplyRewards()
                } else {
                    self?.showLoginRequired()
                }
            }
        }
    }

    private func showLoginRequired() {
        let alert = UIAlertController(
            title: "Login Required",
            message: "Please log in to access rewards",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Common Integration Patterns

// Pattern 1: Direct navigation
@IBAction func showRewards(_ sender: Any) {
    let rewardsVC = MyRewardsViewController()
    navigationController?.pushViewController(rewardsVC, animated: true)
}

// Pattern 2: Conditional navigation
func showRewardsIfReady() {
    guard WarplySDK.shared.getNetworkStatus() == 1 else {
        showNetworkError()
        return
    }

    let rewardsVC = MyRewardsViewController()
    navigationController?.pushViewController(rewardsVC, animated: true)
}

// Pattern 3: Coordinator pattern
class AppCoordinator {
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func showWarplyFlow() {
        let rewardsVC = MyRewardsViewController()
        navigationController.pushViewController(rewardsVC, animated: true)
    }
}

// Pattern 4: Delegate pattern
protocol WarplyNavigationDelegate: AnyObject {
    func didRequestShowProfile()
    func didRequestShowCoupon(_ coupon: OfferModel)
}

class MainViewController: UIViewController, WarplyNavigationDelegate {
    func didRequestShowProfile() {
        let profileVC = ProfileViewController()
        navigationController?.pushViewController(profileVC, animated: true)
    }

    func didRequestShowCoupon(_ coupon: OfferModel) {
        let couponVC = CouponViewController()
        couponVC.coupon = coupon
        navigationController?.pushViewController(couponVC, animated: true)
    }
}

📚 Complete Documentation

The sections below provide detailed information for advanced usage


⚙️ Configuration

Environment Setup

// Production environment (default)
WarplySDK.shared.configure(
    webId: "your-production-web-id",
    merchantId: "your-production-merchant-id",
    environment: .production
)

// Development environment
WarplySDK.shared.configure(
    webId: "your-development-web-id", 
    merchantId: "your-development-merchant-id",
    environment: .development
)

SDK Properties

// Language setting (default: "el")
WarplySDK.shared.applicationLocale = "en" // or "el"

// Dark mode support
WarplySDK.shared.isDarkModeEnabled = true

// Analytics tracking
WarplySDK.shared.trackersEnabled = true

// Network status
let networkStatus = WarplySDK.shared.getNetworkStatus()
// Returns: 1 = connected, 0 = disconnected, -1 = unknown

🔐 Authentication

User Login

// Completion handler approach
WarplySDK.shared.verifyTicket(guid: "user-guid", ticket: "user-ticket") { response in
    if let response = response, response.getStatus == 1 {
        print("Login successful")
        // User is authenticated
    } else {
        print("Login failed")
    }
}

// Async/await approach
Task {
    do {
        let response = try await WarplySDK.shared.verifyTicket(guid: "user-guid", ticket: "user-ticket")
        if response.getStatus == 1 {
            print("Login successful")
        }
    } catch {
        print("Login error: \(error)")
    }
}

User Logout

// Completion handler
WarplySDK.shared.logout { response in
    if let response = response, response.getStatus == 1 {
        print("Logout successful")
        // Clear user data
    }
}

// Async/await
Task {
    do {
        let response = try await WarplySDK.shared.logout()
        print("Logout successful")
    } catch {
        print("Logout error: \(error)")
    }
}

Token Management

// Update tokens after authentication
WarplySDK.shared.updateRefreshToken(
    accessToken: "new-access-token",
    refreshToken: "new-refresh-token"
)

🎯 Campaign Management

Get All Campaigns

// Basic campaign retrieval
WarplySDK.shared.getCampaigns(language: "en") { campaigns in
    guard let campaigns = campaigns else { return }

    for campaign in campaigns {
        print("Campaign: \(campaign.title ?? "No title")")
        print("Description: \(campaign.subtitle ?? "No description")")
        print("Image URL: \(campaign.logo_url ?? "No image")")
    }
}

// With filters
let filters: [String: Any] = [
    "communication_category": "offers",
    "extra_fields": [
        "category": "food",
        "location": "athens"
    ]
]

WarplySDK.shared.getCampaigns(language: "en", filters: filters) { campaigns in
    // Handle filtered campaigns
}

Get Personalized Campaigns

// Async/await example
Task {
    do {
        let personalizedCampaigns = try await WarplySDK.shared.getCampaignsPersonalized(language: "en")

        // Display personalized content
        for campaign in personalizedCampaigns {
            print("Personalized campaign: \(campaign.title ?? "")")
        }
    } catch {
        print("Error getting personalized campaigns: \(error)")
    }
}

Get Supermarket Campaign

WarplySDK.shared.getSupermarketCampaign(language: "en") { campaign in
    if let campaign = campaign {
        print("Supermarket campaign available: \(campaign.title ?? "")")
        // Show supermarket offers
    } else {
        print("No supermarket campaign available")
    }
}

Campaign Interaction

// Get single campaign details
WarplySDK.shared.getSingleCampaign(sessionUuid: "campaign-session-uuid") { response in
    if let response = response, response.getStatus == 1 {
        print("Campaign marked as read")
    }
}

// Construct campaign URL for web view
let campaignUrl = WarplySDK.shared.constructCampaignUrl(campaign)
let campaignParams = WarplySDK.shared.constructCampaignParams(campaign)

// Open campaign in web view
let webView = WKWebView()
if let url = URL(string: campaignUrl) {
    let request = URLRequest(url: url)
    webView.load(request)
}

Campaign State Management

// Get cached campaigns
let cachedCampaigns = WarplySDK.shared.getCampaignList()
let allCampaigns = WarplySDK.shared.getAllCampaignList()
let carouselCampaigns = WarplySDK.shared.getCarouselList()

🎫 Coupon System

Get User Coupons

// Completion handler approach
WarplySDK.shared.getCoupons(language: "en", completion: { coupons in
    guard let coupons = coupons else { return }

    for coupon in coupons {
        print("Coupon: \(coupon.name ?? "No name")")
        print("Discount: \(coupon.discount ?? 0)%")
        print("Expires: \(coupon.expiration ?? "No expiration")")
        print("Status: \(coupon.status == 1 ? "Active" : "Used")")
    }
}, failureCallback: { errorCode in
    print("Failed to get coupons: \(errorCode)")
})

// Async/await approach
Task {
    do {
        let coupons = try await WarplySDK.shared.getCoupons(language: "en")

        // Filter active coupons
        let activeCoupons = coupons.filter { $0.status == 1 }
        print("Active coupons: \(activeCoupons.count)")

    } catch let error as WarplyError {
        print("Coupon error: \(error.localizedDescription)")
    } catch {
        print("Unknown error: \(error)")
    }
}

Get Coupon Sets

// Available coupon categories/sets
WarplySDK.shared.getCouponSets { couponSets in
    guard let couponSets = couponSets else { return }

    for couponSet in couponSets {
        print("Coupon Set: \(couponSet.name ?? "No name")")
        print("Description: \(couponSet.short_description ?? "No description")")
    }
}

// Async/await
Task {
    do {
        let couponSets = try await WarplySDK.shared.getCouponSets()
        // Handle coupon sets
    } catch {
        print("Error getting coupon sets: \(error)")
    }
}

Check Coupon Availability

WarplySDK.shared.getAvailableCoupons { availabilityData in
    guard let availabilityData = availabilityData else { return }

    for (couponSetId, availability) in availabilityData {
        print("Coupon set \(couponSetId): \(availability) available")
    }
}

Coupon State Management

// Get cached coupons
let activeCoupons = WarplySDK.shared.getCouponList()
let usedCoupons = WarplySDK.shared.getOldCouponList()
let allOldCoupons = WarplySDK.shared.getAllOldCouponList()

🏪 Market Pass

Get Market Pass Details

// Completion handler
WarplySDK.shared.getMarketPassDetails(completion: { marketPass in
    guard let marketPass = marketPass else { return }

    print("Market Pass Balance: \(marketPass.balance ?? 0)")
    print("Points: \(marketPass.points ?? 0)")
    print("Status: \(marketPass.status ?? "Unknown")")

}, failureCallback: { errorCode in
    print("Market pass error: \(errorCode)")
})

// Async/await
Task {
    do {
        let marketPass = try await WarplySDK.shared.getMarketPassDetails()
        print("Market Pass retrieved successfully")

        // Access market pass data
        let balance = marketPass.balance ?? 0
        let points = marketPass.points ?? 0

    } catch {
        print("Market pass error: \(error)")
    }
}

Get Supermarket History

Task {
    do {
        let history = try await WarplySDK.shared.getRedeemedSMHistory(language: "en")

        print("Total redeemed value: \(history._totalRedeemedValue)")
        print("Redeemed coupons: \(history._redeemedCouponList.count)")

        for coupon in history._redeemedCouponList {
            print("Redeemed: \(coupon.name ?? "") on \(coupon.redeemed_date ?? Date())")
        }

    } catch {
        print("History error: \(error)")
    }
}

Market Pass UI Integration

// Open supermarket map
WarplySDK.shared.openSupermarketsMap(self) // Pass current view controller

// Open supermarket flow
WarplySDK.shared.openSuperMarketsFlow(self)

// Get map URL for custom implementation
let mapUrl = WarplySDK.shared.getMarketPassMapUrl()

📡 Event System

The framework supports both modern EventDispatcher and legacy SwiftEventBus for backward compatibility.

Using Modern EventDispatcher (Recommended)

import SwiftWarplyFramework

class ViewController: UIViewController {
    private var eventSubscriptions: [EventSubscription] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        setupEventListeners()
    }

    private func setupEventListeners() {
        // Subscribe to campaigns retrieved event
        let subscription = WarplySDK.shared.subscribe(CampaignsRetrievedEvent.self) { event in
            print("Campaigns retrieved: \(event.data ?? "No data")")
            DispatchQueue.main.async {
                // Update UI
                self.refreshCampaignsList()
            }
        }
        eventSubscriptions.append(subscription)

        // Subscribe to market pass events
        let marketSubscription = WarplySDK.shared.subscribe(MarketPassDetailsEvent.self) { event in
            print("Market pass details updated")
            DispatchQueue.main.async {
                self.updateMarketPassUI()
            }
        }
        eventSubscriptions.append(marketSubscription)
    }

    deinit {
        // Subscriptions are automatically cleaned up
        eventSubscriptions.removeAll()
    }
}

Using SwiftEventBus (Legacy Support)

import SwiftEventBus

class LegacyViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Subscribe to events
        SwiftEventBus.onMainThread(self, name: "campaigns_retrieved") { result in
            print("Campaigns retrieved via SwiftEventBus")
            // Handle event
        }

        SwiftEventBus.onMainThread(self, name: "coupons_fetched") { result in
            print("Coupons fetched via SwiftEventBus")
            // Handle event
        }
    }

    deinit {
        SwiftEventBus.unregister(self)
    }
}

Available Events

Event Name EventDispatcher Type Description
campaigns_retrieved CampaignsRetrievedEvent Campaigns data loaded
coupons_fetched CouponsRetrievedEvent Coupons data loaded
market_pass_details_fetched MarketPassDetailsEvent Market pass data loaded
ccms_retrieved CCMSRetrievedEvent CCMS campaigns loaded
seasonals_retrieved SeasonalsRetrievedEvent Seasonal campaigns loaded
dynatrace DynatraceEvent Analytics events

📱 Push Notifications

Setup

// In AppDelegate
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    WarplySDK.shared.updateDeviceToken(tokenString)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    // Check if it's a Warply notification
    if WarplySDK.shared.checkForLoyaltySDKNotification(userInfo) {
        print("Warply notification handled")
    } else {
        // Handle other notifications
    }
}

Handle Notifications

// Manual notification handling
WarplySDK.shared.handleNotification(notificationPayload)

// Check if notification belongs to Warply SDK
let isWarplyNotification = WarplySDK.shared.checkForLoyaltySDKNotification(payload)

⚠️ Error Handling

WarplyError Types

public enum WarplyError: Error {
    case networkError           // Network request failed
    case invalidResponse        // Invalid response received
    case authenticationFailed   // Authentication failed
    case dataParsingError      // Failed to parse response data
    case unknownError(Int)     // Unknown error with code
}

Error Handling Patterns

// Async/await error handling
Task {
    do {
        let campaigns = try await WarplySDK.shared.getCampaigns(language: "en")
        // Success
    } catch let error as WarplyError {
        switch error {
        case .networkError:
            showNetworkErrorAlert()
        case .authenticationFailed:
            redirectToLogin()
        case .invalidResponse:
            showGenericErrorAlert()
        case .dataParsingError:
            logParsingError()
        case .unknownError(let code):
            logUnknownError(code: code)
        }
    } catch {
        print("Unexpected error: \(error)")
    }
}

// Completion handler error handling
WarplySDK.shared.getCoupons(language: "en", completion: { coupons in
    if let coupons = coupons {
        // Success
    } else {
        // Handle nil response
        showErrorAlert("Failed to load coupons")
    }
}, failureCallback: { errorCode in
    // Handle specific error codes
    switch errorCode {
    case 401:
        redirectToLogin()
    case 500:
        showServerErrorAlert()
    default:
        showGenericErrorAlert()
    }
})

Network Status Checking

func checkNetworkAndProceed() {
    let networkStatus = WarplySDK.shared.getNetworkStatus()

    switch networkStatus {
    case 1:
        // Connected - proceed with API calls
        loadData()
    case 0:
        // Disconnected - show offline message
        showOfflineAlert()
    case -1:
        // Unknown - try anyway or show warning
        showNetworkWarning()
    default:
        break
    }
}

🔄 Migration Guide

From Legacy SwiftEventBus to EventDispatcher

Before:

SwiftEventBus.onMainThread(self, name: "campaigns_retrieved") { result in
    // Handle event
}

After:

let subscription = WarplySDK.shared.subscribe(CampaignsRetrievedEvent.self) { event in
    // Handle event with type safety
}

From Completion Handlers to Async/Await

Before:

WarplySDK.shared.getCampaigns(language: "en") { campaigns in
    if let campaigns = campaigns {
        // Handle success
    } else {
        // Handle failure
    }
}

After:

Task {
    do {
        let campaigns = try await WarplySDK.shared.getCampaigns(language: "en")
        // Handle success
    } catch {
        // Handle error with proper error types
    }
}

Gradual Migration Strategy

  1. Keep existing code working - No breaking changes required
  2. Add async/await gradually - Start with new features
  3. Migrate events when convenient - EventDispatcher provides better type safety
  4. Update error handling - Use structured WarplyError types

💡 Best Practices

1. Initialization

// ✅ Good: Initialize in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    WarplySDK.shared.configure(webId: "...", merchantId: "...")
    WarplySDK.shared.initialize { success in
        // SDK ready
    }
    return true
}

// ❌ Bad: Initialize in view controllers
override func viewDidLoad() {
    WarplySDK.shared.configure(...) // Too late!
}

2. Error Handling

// ✅ Good: Comprehensive error handling
Task {
    do {
        let campaigns = try await WarplySDK.shared.getCampaigns(language: "en")
        updateUI(with: campaigns)
    } catch let error as WarplyError {
        handleWarplyError(error)
    } catch {
        handleUnexpectedError(error)
    }
}

// ❌ Bad: Ignoring errors
Task {
    let campaigns = try? await WarplySDK.shared.getCampaigns(language: "en")
    // Silently fails
}

3. Event Management

// ✅ Good: Proper subscription management
class ViewController: UIViewController {
    private var eventSubscriptions: [EventSubscription] = []

    private func setupEvents() {
        let subscription = WarplySDK.shared.subscribe(CampaignsRetrievedEvent.self) { event in
            // Handle event
        }
        eventSubscriptions.append(subscription)
    }

    deinit {
        eventSubscriptions.removeAll() // Automatic cleanup
    }
}

// ❌ Bad: Memory leaks
class BadViewController: UIViewController {
    override func viewDidLoad() {
        WarplySDK.shared.subscribe(CampaignsRetrievedEvent.self) { event in
            // This creates a retain cycle!
            self.handleEvent(event)
        }
    }
}

4. UI Updates

// ✅ Good: Main thread UI updates
Task {
    let campaigns = try await WarplySDK.shared.getCampaigns(language: "en")

    await MainActor.run {
        self.updateCampaignsUI(campaigns)
    }
}

// ✅ Also good: Using completion handlers
WarplySDK.shared.getCampaigns(language: "en") { campaigns in
    DispatchQueue.main.async {
        self.updateCampaignsUI(campaigns)
    }
}

5. Caching Strategy

// ✅ Good: Use SDK's built-in caching
func loadCampaigns() {
    // First, show cached data
    let cachedCampaigns = WarplySDK.shared.getCampaignList()
    if !cachedCampaigns.isEmpty {
        updateUI(with: cachedCampaigns)
    }

    // Then, fetch fresh data
    Task {
        do {
            let freshCampaigns = try await WarplySDK.shared.getCampaigns(language: "en")
            updateUI(with: freshCampaigns)
        } catch {
            // Handle error, keep cached data
        }
    }
}

🔧 Troubleshooting

Common Issues

1. SDK Not Initializing

Problem: SDK initialization fails

WarplySDK.shared.initialize { success in
    if !success {
        print("Initialization failed")
    }
}

Solutions:

  • Check network connectivity
  • Verify webId and merchantId are correct
  • Ensure proper environment configuration

2. No Data Returned

Problem: API calls return nil or empty arrays

Solutions:

// Check authentication status
WarplySDK.shared.verifyTicket(guid: "...", ticket: "...") { response in
    if response?.getStatus != 1 {
        print("User not authenticated")
    }
}

// Check network status
let networkStatus = WarplySDK.shared.getNetworkStatus()
if networkStatus != 1 {
    print("Network issue")
}

// Check language parameter
WarplySDK.shared.getCampaigns(language: "en") // Try different language

3. Events Not Firing

Problem: Event listeners not receiving events

Solutions:

// Ensure proper subscription
let subscription = WarplySDK.shared.subscribe(CampaignsRetrievedEvent.self) { event in
    print("Event received: \(event)")
}

// Keep subscription reference
self.eventSubscriptions.append(subscription)

// For SwiftEventBus, ensure proper registration
SwiftEventBus.onMainThread(self, name: "campaigns_retrieved") { result in
    print("SwiftEventBus event received")
}

4. Memory Issues

Problem: Memory leaks or crashes

Solutions:

// Use weak references in closures
WarplySDK.shared.getCampaigns(language: "en") { [weak self] campaigns in
    self?.updateUI(campaigns)
}

// Proper event cleanup
deinit {
    SwiftEventBus.unregister(self)
    eventSubscriptions.removeAll()
}

Debug Mode

// Enable debug logging
WarplySDK.shared.trackersEnabled = true

// Check SDK state
print("App UUID: \(WarplySDK.shared.appUuid)")
print("Merchant ID: \(WarplySDK.shared.merchantId)")
print("Language: \(WarplySDK.shared.applicationLocale)")
print("Network Status: \(WarplySDK.shared.getNetworkStatus())")

Performance Optimization

// Batch API calls when possible
Task {
    async let campaigns = WarplySDK.shared.getCampaigns(language: "en")
    async let coupons = WarplySDK.shared.getCoupons(language: "en")
    async let marketPass = WarplySDK.shared.getMarketPassDetails()

    do {
        let (campaignData, couponData, marketData) = try await (campaigns, coupons, marketPass)
        // Update UI with all data
    } catch {
        // Handle errors
    }
}

📞 Support

Getting Help

  1. Documentation: Refer to this guide and inline code documentation
  2. Sample Code: Check the examples in this documentation
  3. Error Messages: Use the structured WarplyError types for debugging
  4. Network Issues: Check getNetworkStatus() before API calls

Reporting Issues

When reporting issues, please include:

  1. Framework Version: 2.1.0
  2. iOS Version: Minimum 17.0
  3. Error Details: Full error messages and stack traces
  4. Code Sample: Minimal reproducible example
  5. Network Status: Result of getNetworkStatus()
  6. Configuration: Environment and setup details (without sensitive data)

Framework Information

// Get framework version and status
print("Framework Version: 2.1.0")
print("SDK Initialized: \(WarplySDK.shared != nil)")
print("Network Status: \(WarplySDK.shared.getNetworkStatus())")
print("Current Language: \(WarplySDK.shared.applicationLocale)")

🎉 Conclusion

The SwiftWarplyFramework provides a comprehensive solution for loyalty programs, campaigns, coupons, and market pass functionality. With both modern async/await patterns and legacy completion handler support, you can integrate the framework at your own pace while maintaining backward compatibility.

Key Benefits:

  • Modern Swift Patterns - Async/await, structured error handling
  • Backward Compatibility - Existing code continues to work
  • Type Safety - Comprehensive error types and event system
  • Performance - Built-in caching and efficient networking
  • Flexibility - Multiple API patterns and event systems

Start with the Quick Start guide and gradually adopt the advanced features as needed. The framework is designed to grow with your application's requirements.


Happy Coding! 🚀