Showing
2 changed files
with
129 additions
and
26 deletions
| ... | @@ -1121,6 +1121,81 @@ After these changes, the framework should: | ... | @@ -1121,6 +1121,81 @@ After these changes, the framework should: | 
| 1121 | 1121 | ||
| 1122 | After the initial legacy credential removal, a comprehensive search revealed **2 additional problematic checks** that could still cause issues: | 1122 | After the initial legacy credential removal, a comprehensive search revealed **2 additional problematic checks** that could still cause issues: | 
| 1123 | 1123 | ||
| 1124 | +#### **Issue #7: getCampaigns Authentication Logic (FINAL FIX)** | ||
| 1125 | +**File**: `SwiftWarplyFramework/SwiftWarplyFramework/Core/WarplySDK.swift` | ||
| 1126 | +**Problem**: `getCampaigns` method failed with ERROR: -1 when user not authenticated due to failed `getCampaignsPersonalized` and `getAvailableCoupons` calls | ||
| 1127 | +**Impact**: ❌ **CRITICAL** - Campaign loading completely failed for non-authenticated users | ||
| 1128 | + | ||
| 1129 | +**Root Cause**: The method tried to call authentication-required endpoints (`getCampaignsPersonalized` and `getAvailableCoupons`) even when user was not logged in, causing the entire flow to fail. | ||
| 1130 | + | ||
| 1131 | +**Solution**: **Authentication-First Approach** | ||
| 1132 | +```swift | ||
| 1133 | +// Check if user is authenticated to determine campaign processing approach | ||
| 1134 | +if self.isUserAuthenticated() { | ||
| 1135 | + // User is authenticated - get full experience with personalized campaigns and coupon filtering | ||
| 1136 | + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability") | ||
| 1137 | + | ||
| 1138 | + self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in | ||
| 1139 | + // ... full authenticated flow with parseCampaigns filtering | ||
| 1140 | + }) | ||
| 1141 | +} else { | ||
| 1142 | + // User not authenticated - return all basic campaigns without coupon filtering | ||
| 1143 | + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering") | ||
| 1144 | + | ||
| 1145 | + // Skip parseCampaigns entirely - return raw campaigns without filtering | ||
| 1146 | + completion(campaignsArray) | ||
| 1147 | +} | ||
| 1148 | +``` | ||
| 1149 | + | ||
| 1150 | +**Key Changes**: | ||
| 1151 | +1. **Added `isUserAuthenticated()` helper method** - checks database for valid, non-expired tokens | ||
| 1152 | +2. **Authentication-first logic** - check auth status before making dependent calls | ||
| 1153 | +3. **Graceful degradation** - non-authenticated users get basic campaigns without filtering | ||
| 1154 | +4. **Skip `parseCampaigns`** - for non-authenticated users to avoid empty results due to missing coupon availability | ||
| 1155 | + | ||
| 1156 | +**Result**: ✅ **No more ERROR: -1** - campaigns load successfully for both authenticated and non-authenticated users | ||
| 1157 | + | ||
| 1158 | +--- | ||
| 1159 | +### **🔧 Additional Safety Fixes - June 27, 2025 (Session 2)** | ||
| 1160 | + | ||
| 1161 | +After the initial legacy credential removal, a comprehensive search revealed **3 additional problematic checks** that could still cause issues: | ||
| 1162 | + | ||
| 1163 | +#### **Issue #7: getCampaigns Authentication Logic (FINAL FIX)** | ||
| 1164 | +**File**: `SwiftWarplyFramework/SwiftWarplyFramework/Core/WarplySDK.swift` | ||
| 1165 | +**Problem**: `getCampaigns` method failed with ERROR: -1 when user not authenticated due to failed `getCampaignsPersonalized` and `getAvailableCoupons` calls | ||
| 1166 | +**Impact**: ❌ **CRITICAL** - Campaign loading completely failed for non-authenticated users | ||
| 1167 | + | ||
| 1168 | +**Root Cause**: The method tried to call authentication-required endpoints (`getCampaignsPersonalized` and `getAvailableCoupons`) even when user was not logged in, causing the entire flow to fail. | ||
| 1169 | + | ||
| 1170 | +**Solution**: **Authentication-First Approach** | ||
| 1171 | +```swift | ||
| 1172 | +// Check if user is authenticated to determine campaign processing approach | ||
| 1173 | +if self.isUserAuthenticated() { | ||
| 1174 | + // User is authenticated - get full experience with personalized campaigns and coupon filtering | ||
| 1175 | + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability") | ||
| 1176 | + | ||
| 1177 | + self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in | ||
| 1178 | + // ... full authenticated flow with parseCampaigns filtering | ||
| 1179 | + }) | ||
| 1180 | +} else { | ||
| 1181 | + // User not authenticated - return all basic campaigns without coupon filtering | ||
| 1182 | + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering") | ||
| 1183 | + | ||
| 1184 | + // Skip parseCampaigns entirely - return raw campaigns without filtering | ||
| 1185 | + completion(campaignsArray) | ||
| 1186 | +} | ||
| 1187 | +``` | ||
| 1188 | + | ||
| 1189 | +**Key Changes**: | ||
| 1190 | +1. **Added `isUserAuthenticated()` helper method** - checks database for valid, non-expired tokens | ||
| 1191 | +2. **Authentication-first logic** - check auth status before making dependent calls | ||
| 1192 | +3. **Graceful degradation** - non-authenticated users get basic campaigns without filtering | ||
| 1193 | +4. **Skip `parseCampaigns`** - for non-authenticated users to avoid empty results due to missing coupon availability | ||
| 1194 | + | ||
| 1195 | +**Result**: ✅ **No more ERROR: -1** - campaigns load successfully for both authenticated and non-authenticated users | ||
| 1196 | + | ||
| 1197 | +--- | ||
| 1198 | + | ||
| 1124 | #### **Issue #5: NetworkService Signature Generation (CRITICAL)** | 1199 | #### **Issue #5: NetworkService Signature Generation (CRITICAL)** | 
| 1125 | **File**: `SwiftWarplyFramework/SwiftWarplyFramework/Network/NetworkService.swift` | 1200 | **File**: `SwiftWarplyFramework/SwiftWarplyFramework/Network/NetworkService.swift` | 
| 1126 | **Problem**: Conditional signature generation that skipped the `loyalty-signature` header when API key was empty | 1201 | **Problem**: Conditional signature generation that skipped the `loyalty-signature` header when API key was empty | ... | ... | 
| ... | @@ -1589,42 +1589,57 @@ public final class WarplySDK { | ... | @@ -1589,42 +1589,57 @@ public final class WarplySDK { | 
| 1589 | } | 1589 | } | 
| 1590 | } | 1590 | } | 
| 1591 | 1591 | ||
| 1592 | - // Get personalized campaigns (still using MyApi temporarily) | 1592 | + // Check if user is authenticated to determine campaign processing approach | 
| 1593 | - self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in | 1593 | + if self.isUserAuthenticated() { | 
| 1594 | - campaignsArray = campaignsArray + (personalizedCampaigns ?? []) | 1594 | + // User is authenticated - get full experience with personalized campaigns and coupon filtering | 
| 1595 | + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability") | ||
| 1595 | 1596 | ||
| 1596 | - // Get available coupons (still using MyApi temporarily) | 1597 | + self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in | 
| 1597 | - self.getAvailableCoupons { availabilityData in | 1598 | + campaignsArray = campaignsArray + (personalizedCampaigns ?? []) | 
| 1598 | - if let availabilityData = availabilityData { | 1599 | + | 
| 1599 | - for tempCampaign in campaignsArray { | 1600 | + // Get available coupons for authenticated users | 
| 1600 | - for item in availabilityData { | 1601 | + self.getAvailableCoupons { availabilityData in | 
| 1601 | - if tempCampaign._couponset == item.key { | 1602 | + if let availabilityData = availabilityData { | 
| 1602 | - tempCampaign._coupon_availability = item.value as? Int ?? 0 | 1603 | + for tempCampaign in campaignsArray { | 
| 1604 | + for item in availabilityData { | ||
| 1605 | + if tempCampaign._couponset == item.key { | ||
| 1606 | + tempCampaign._coupon_availability = item.value as? Int ?? 0 | ||
| 1607 | + } | ||
| 1603 | } | 1608 | } | 
| 1604 | } | 1609 | } | 
| 1605 | } | 1610 | } | 
| 1611 | + | ||
| 1612 | + // For authenticated users: use parseCampaigns (filters by coupon availability) | ||
| 1613 | + let parsedCampaigns = self.parseCampaigns(campaignsArray) | ||
| 1614 | + completion(parsedCampaigns) | ||
| 1606 | } | 1615 | } | 
| 1616 | + }, failureCallback: { errorCode in | ||
| 1617 | + // If personalized campaigns fail, still try coupon availability | ||
| 1618 | + print("⚠️ [WarplySDK] Personalized campaigns failed for authenticated user - continuing with basic campaigns") | ||
| 1607 | 1619 | ||
| 1608 | - let parsedCampaigns = self.parseCampaigns(campaignsArray) | 1620 | + self.getAvailableCoupons { availabilityData in | 
| 1609 | - completion(parsedCampaigns) | 1621 | + if let availabilityData = availabilityData { | 
| 1610 | - } | 1622 | + for tempCampaign in campaignsArray { | 
| 1611 | - }, failureCallback: { errorCode in | 1623 | + for item in availabilityData { | 
| 1612 | - // If personalized campaigns fail, continue with regular campaigns | 1624 | + if tempCampaign._couponset == item.key { | 
| 1613 | - self.getAvailableCoupons { availabilityData in | 1625 | + tempCampaign._coupon_availability = item.value as? Int ?? 0 | 
| 1614 | - if let availabilityData = availabilityData { | 1626 | + } | 
| 1615 | - for tempCampaign in campaignsArray { | ||
| 1616 | - for item in availabilityData { | ||
| 1617 | - if tempCampaign._couponset == item.key { | ||
| 1618 | - tempCampaign._coupon_availability = item.value as? Int ?? 0 | ||
| 1619 | } | 1627 | } | 
| 1620 | } | 1628 | } | 
| 1621 | } | 1629 | } | 
| 1630 | + | ||
| 1631 | + // For authenticated users: use parseCampaigns (filters by coupon availability) | ||
| 1632 | + let parsedCampaigns = self.parseCampaigns(campaignsArray) | ||
| 1633 | + completion(parsedCampaigns) | ||
| 1622 | } | 1634 | } | 
| 1623 | - | 1635 | + }) | 
| 1624 | - let parsedCampaigns = self.parseCampaigns(campaignsArray) | 1636 | + } else { | 
| 1625 | - completion(parsedCampaigns) | 1637 | + // User not authenticated - return all basic campaigns without coupon filtering | 
| 1626 | - } | 1638 | + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering") | 
| 1627 | - }) | 1639 | + | 
| 1640 | + // Skip parseCampaigns entirely - return raw campaigns without filtering | ||
| 1641 | + completion(campaignsArray) | ||
| 1642 | + } | ||
| 1628 | } else { | 1643 | } else { | 
| 1629 | let dynatraceEvent = LoyaltySDKDynatraceEventModel() | 1644 | let dynatraceEvent = LoyaltySDKDynatraceEventModel() | 
| 1630 | dynatraceEvent._eventName = "custom_error_campaigns_loyalty" | 1645 | dynatraceEvent._eventName = "custom_error_campaigns_loyalty" | 
| ... | @@ -2967,6 +2982,19 @@ public final class WarplySDK { | ... | @@ -2967,6 +2982,19 @@ public final class WarplySDK { | 
| 2967 | 2982 | ||
| 2968 | // MARK: - Private Helper Methods | 2983 | // MARK: - Private Helper Methods | 
| 2969 | 2984 | ||
| 2985 | + /// Check if user is currently authenticated with valid tokens | ||
| 2986 | + private func isUserAuthenticated() -> Bool { | ||
| 2987 | + do { | ||
| 2988 | + if let tokenModel = try DatabaseManager.shared.getTokenModelSync() { | ||
| 2989 | + // Check if token exists and is not expired | ||
| 2990 | + return !tokenModel.accessToken.isEmpty && !tokenModel.isExpired | ||
| 2991 | + } | ||
| 2992 | + } catch { | ||
| 2993 | + print("⚠️ [WarplySDK] Failed to check authentication status: \(error)") | ||
| 2994 | + } | ||
| 2995 | + return false | ||
| 2996 | + } | ||
| 2997 | + | ||
| 2970 | private func parseCampaigns(_ campaigns: [CampaignItemModel]) -> [CampaignItemModel] { | 2998 | private func parseCampaigns(_ campaigns: [CampaignItemModel]) -> [CampaignItemModel] { | 
| 2971 | let filteredCampaigns = campaigns.filter { $0._coupon_availability != 0 } | 2999 | let filteredCampaigns = campaigns.filter { $0._coupon_availability != 0 } | 
| 2972 | 3000 | ... | ... | 
- 
Please register or login to post a comment