SwiftWarplyFramework Client Documentation
🚀 5-Minute Setup Guide
Get started with SwiftWarplyFramework in just 5 minutes!
Version: 2.2.10 | Minimum iOS: 17.0 | Swift: 5.0+
📋 Quick Navigation
🚀 5-Minute Setup ← Start 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
Choose your preferred installation method:
Option A: Swift Package Manager (SPM) - Recommended 🆕
A.1 Using Xcode (Easiest)
- Open your project in Xcode
- Go to File → Add Package Dependencies
- Enter the repository URL:
https://git.warp.ly/open-source/warply_sdk_framework.git
- Select Version:
2.2.10
or Up to Next Major - Click Add Package
- Select SwiftWarplyFramework and click Add Package
A.2 Using Package.swift
Add to your Package.swift
dependencies:
dependencies: [
.package(url: "https://git.warp.ly/open-source/warply_sdk_framework.git", from: "2.2.10")
]
Then add to your target:
targets: [
.target(
name: "YourApp",
dependencies: ["SwiftWarplyFramework"]
)
]
✅ Verification: Framework should appear in Package Dependencies
Option B: CocoaPods
B.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.2.10'
end
B.2 Install Framework
Run in terminal:
pod install
B.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(
appUuid: "YOUR_APP_UUID", // Replace with your App Uuid
merchantId: "YOUR_MERCHANT_ID", // Replace with your Merchant ID
environment: .production, // Use .development for testing
language: "el" // Language: "en" or "el" (default: "el")
)
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(
appUuid: "YOUR_APP_UUID",
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: "el") { 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: "el")
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?
- 🔐 Set up user authentication
- 🎯 Load and display campaigns
- 🎫 Implement coupon functionality
- 📡 Add event handling
📱 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(
appUuid: "your-production-app-uuid",
merchantId: "your-production-merchant-id",
environment: .production
)
// Development environment
WarplySDK.shared.configure(
appUuid: "your-development-app-uuid",
merchantId: "your-development-merchant-id",
environment: .development
)
Language Configuration
// Configure language during SDK setup (recommended)
WarplySDK.shared.configure(
appUuid: "your-app-uuid",
merchantId: "your-merchant-id",
environment: .production,
language: "el" // "en" for English, "el" for Greek (default)
)
// Change language at runtime
WarplySDK.shared.applicationLocale = "el" // or "el"
// Language affects:
// - API responses and content localization
// - Campaign and coupon descriptions
// - Error messages and UI text
// - Date and number formatting
SDK Properties
// Language setting (default: "el")
WarplySDK.shared.applicationLocale = "el" // or "en"
// 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: "el") { 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: "el", 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: "el") { 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: "el", 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: "el")
// 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: "el")
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: "el")
// 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: "el", 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: "el") { campaigns in
if let campaigns = campaigns {
// Handle success
} else {
// Handle failure
}
}
After:
Task {
do {
let campaigns = try await WarplySDK.shared.getCampaigns(language: "el")
// Handle success
} catch {
// Handle error with proper error types
}
}
Gradual Migration Strategy
- Keep existing code working - No breaking changes required
- Add async/await gradually - Start with new features
- Migrate events when convenient - EventDispatcher provides better type safety
- 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(appUuid: "...", 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: "el")
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: "el")
// 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: "el")
await MainActor.run {
self.updateCampaignsUI(campaigns)
}
}
// ✅ Also good: Using completion handlers
WarplySDK.shared.getCampaigns(language: "el") { 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: "el")
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 appUuid 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: "el") // 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: "el") { [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: "el")
async let coupons = WarplySDK.shared.getCoupons(language: "el")
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
- Documentation: Refer to this guide and inline code documentation
- Sample Code: Check the examples in this documentation
- Error Messages: Use the structured WarplyError types for debugging
-
Network Issues: Check
getNetworkStatus()
before API calls
Reporting Issues
When reporting issues, please include:
-
Framework Version:
2.1.0
- iOS Version: Minimum 17.0
- Error Details: Full error messages and stack traces
- Code Sample: Minimal reproducible example
-
Network Status: Result of
getNetworkStatus()
- 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.
📋 Changelog
Version 2.2.10 - June 23, 2025
🆕 New Features
-
Language Configuration Support: Added configurable language parameter to SDK initialization
- New
language
parameter inconfigure()
method (defaults to "el") - Runtime language switching via
applicationLocale
property - Automatic configuration sync when language changes
- New
🔧 Network Improvements
-
Comprehensive Header System: Implemented complete HTTP header management based on original Objective-C implementation
- Core loyalty headers:
loyalty-web-id
,loyalty-date
,loyalty-signature
- Device identification headers:
unique-device-id
,vendor
,platform
,os_version
- App identification headers:
loyalty-bundle-id
,manufacturer
,ios_device_model
- Authentication headers with proper Bearer token handling
- Special endpoint headers for registration, and logout flows
- Core loyalty headers:
-
Dynamic BaseURL Management: Enhanced baseURL handling for improved configuration flexibility
- Dynamic baseURL reading from Configuration on every request
- Environment-aware URL switching (development/production)
- Real-time configuration updates without restart
- Fallback safety mechanisms with default stage URL
- SHA256 Signature Generation: Added secure signature generation for API authentication
- Device Info Utilities: Enhanced device information collection for headers
- Platform-Specific Headers: iOS-specific headers for better backend compatibility
Happy Coding! 🚀