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