Showing
2 changed files
with
106 additions
and
3 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,11 +1589,15 @@ public final class WarplySDK { | ... | @@ -1589,11 +1589,15 @@ 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 | + if self.isUserAuthenticated() { | ||
1594 | + // User is authenticated - get full experience with personalized campaigns and coupon filtering | ||
1595 | + print("✅ [WarplySDK] User authenticated - loading personalized campaigns and coupon availability") | ||
1596 | + | ||
1593 | self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in | 1597 | self.getCampaignsPersonalized(language: language, filters: filters, completion: { personalizedCampaigns in |
1594 | campaignsArray = campaignsArray + (personalizedCampaigns ?? []) | 1598 | campaignsArray = campaignsArray + (personalizedCampaigns ?? []) |
1595 | 1599 | ||
1596 | - // Get available coupons (still using MyApi temporarily) | 1600 | + // Get available coupons for authenticated users |
1597 | self.getAvailableCoupons { availabilityData in | 1601 | self.getAvailableCoupons { availabilityData in |
1598 | if let availabilityData = availabilityData { | 1602 | if let availabilityData = availabilityData { |
1599 | for tempCampaign in campaignsArray { | 1603 | for tempCampaign in campaignsArray { |
... | @@ -1605,11 +1609,14 @@ public final class WarplySDK { | ... | @@ -1605,11 +1609,14 @@ public final class WarplySDK { |
1605 | } | 1609 | } |
1606 | } | 1610 | } |
1607 | 1611 | ||
1612 | + // For authenticated users: use parseCampaigns (filters by coupon availability) | ||
1608 | let parsedCampaigns = self.parseCampaigns(campaignsArray) | 1613 | let parsedCampaigns = self.parseCampaigns(campaignsArray) |
1609 | completion(parsedCampaigns) | 1614 | completion(parsedCampaigns) |
1610 | } | 1615 | } |
1611 | }, failureCallback: { errorCode in | 1616 | }, failureCallback: { errorCode in |
1612 | - // If personalized campaigns fail, continue with regular campaigns | 1617 | + // If personalized campaigns fail, still try coupon availability |
1618 | + print("⚠️ [WarplySDK] Personalized campaigns failed for authenticated user - continuing with basic campaigns") | ||
1619 | + | ||
1613 | self.getAvailableCoupons { availabilityData in | 1620 | self.getAvailableCoupons { availabilityData in |
1614 | if let availabilityData = availabilityData { | 1621 | if let availabilityData = availabilityData { |
1615 | for tempCampaign in campaignsArray { | 1622 | for tempCampaign in campaignsArray { |
... | @@ -1621,11 +1628,19 @@ public final class WarplySDK { | ... | @@ -1621,11 +1628,19 @@ public final class WarplySDK { |
1621 | } | 1628 | } |
1622 | } | 1629 | } |
1623 | 1630 | ||
1631 | + // For authenticated users: use parseCampaigns (filters by coupon availability) | ||
1624 | let parsedCampaigns = self.parseCampaigns(campaignsArray) | 1632 | let parsedCampaigns = self.parseCampaigns(campaignsArray) |
1625 | completion(parsedCampaigns) | 1633 | completion(parsedCampaigns) |
1626 | } | 1634 | } |
1627 | }) | 1635 | }) |
1628 | } else { | 1636 | } else { |
1637 | + // User not authenticated - return all basic campaigns without coupon filtering | ||
1638 | + print("ℹ️ [WarplySDK] User not authenticated - returning all basic campaigns without coupon filtering") | ||
1639 | + | ||
1640 | + // Skip parseCampaigns entirely - return raw campaigns without filtering | ||
1641 | + completion(campaignsArray) | ||
1642 | + } | ||
1643 | + } else { | ||
1629 | let dynatraceEvent = LoyaltySDKDynatraceEventModel() | 1644 | let dynatraceEvent = LoyaltySDKDynatraceEventModel() |
1630 | dynatraceEvent._eventName = "custom_error_campaigns_loyalty" | 1645 | dynatraceEvent._eventName = "custom_error_campaigns_loyalty" |
1631 | dynatraceEvent._parameters = nil | 1646 | dynatraceEvent._parameters = nil |
... | @@ -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