Showing
4 changed files
with
210 additions
and
16 deletions
| ... | @@ -2457,6 +2457,94 @@ public final class WarplySDK { | ... | @@ -2457,6 +2457,94 @@ public final class WarplySDK { |
| 2457 | } | 2457 | } |
| 2458 | } | 2458 | } |
| 2459 | 2459 | ||
| 2460 | + /// Get coupon sets via new endpoint with optional region/offer_category filters | ||
| 2461 | + /// - Parameters: | ||
| 2462 | + /// - region: Optional region filter (omitted from payload if nil) | ||
| 2463 | + /// - offerCategory: Optional offer category filter (omitted from payload if nil) | ||
| 2464 | + /// - completion: Returns the raw response dictionary on success | ||
| 2465 | + /// - failureCallback: Returns error code on failure | ||
| 2466 | + public func getCouponSetsNew( | ||
| 2467 | + language: String? = nil, | ||
| 2468 | + region: String? = nil, | ||
| 2469 | + offerCategory: String? = nil, | ||
| 2470 | + completion: @escaping ([CouponSetItemModel]?) -> Void, | ||
| 2471 | + failureCallback: @escaping (Int) -> Void | ||
| 2472 | + ) { | ||
| 2473 | + let finalLanguage = language ?? self.applicationLocale | ||
| 2474 | + | ||
| 2475 | + Task { | ||
| 2476 | + do { | ||
| 2477 | + let endpoint = Endpoint.getCouponSetsNew( | ||
| 2478 | + language: finalLanguage, | ||
| 2479 | + active: true, | ||
| 2480 | + visible: true, | ||
| 2481 | + region: region, | ||
| 2482 | + offerCategory: offerCategory | ||
| 2483 | + ) | ||
| 2484 | + let response = try await networkService.requestRaw(endpoint) | ||
| 2485 | + | ||
| 2486 | + var couponSetsArray: [CouponSetItemModel] = [] | ||
| 2487 | + | ||
| 2488 | + await MainActor.run { | ||
| 2489 | + print("📥 getCouponSetsNew response: \(response)") | ||
| 2490 | + | ||
| 2491 | + let dynatraceEvent = LoyaltySDKDynatraceEventModel() | ||
| 2492 | + dynatraceEvent._eventName = "custom_success_couponsetsnew_loyalty" | ||
| 2493 | + dynatraceEvent._parameters = nil | ||
| 2494 | + self.postFrameworkEvent("dynatrace", sender: dynatraceEvent) | ||
| 2495 | + | ||
| 2496 | + if let resultData = response["result"] as? [[String: Any]] { | ||
| 2497 | + for couponsetDict in resultData { | ||
| 2498 | + let tempCouponset = CouponSetItemModel(dictionary: couponsetDict) | ||
| 2499 | + couponSetsArray.append(tempCouponset) | ||
| 2500 | + } | ||
| 2501 | + } | ||
| 2502 | + | ||
| 2503 | + self.setCouponSetList(couponSetsArray) | ||
| 2504 | + completion(couponSetsArray) | ||
| 2505 | + } | ||
| 2506 | + } catch { | ||
| 2507 | + await MainActor.run { | ||
| 2508 | + let dynatraceEvent = LoyaltySDKDynatraceEventModel() | ||
| 2509 | + dynatraceEvent._eventName = "custom_error_couponsetsnew_loyalty" | ||
| 2510 | + dynatraceEvent._parameters = nil | ||
| 2511 | + self.postFrameworkEvent("dynatrace", sender: dynatraceEvent) | ||
| 2512 | + | ||
| 2513 | + if let networkError = error as? NetworkError { | ||
| 2514 | + failureCallback(networkError.code) | ||
| 2515 | + } else { | ||
| 2516 | + failureCallback(-1) | ||
| 2517 | + } | ||
| 2518 | + } | ||
| 2519 | + } | ||
| 2520 | + } | ||
| 2521 | + } | ||
| 2522 | + | ||
| 2523 | + /// Get coupon sets new (async/await variant) | ||
| 2524 | + /// - Parameters: | ||
| 2525 | + /// - language: Language code for localized content (optional, defaults to applicationLocale) | ||
| 2526 | + /// - region: Optional region filter (omitted from payload if nil) | ||
| 2527 | + /// - offerCategory: Optional offer category filter (omitted from payload if nil) | ||
| 2528 | + /// - Returns: Array of coupon set models | ||
| 2529 | + /// - Throws: WarplyError if the request fails | ||
| 2530 | + public func getCouponSetsNew( | ||
| 2531 | + language: String? = nil, | ||
| 2532 | + region: String? = nil, | ||
| 2533 | + offerCategory: String? = nil | ||
| 2534 | + ) async throws -> [CouponSetItemModel] { | ||
| 2535 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 2536 | + getCouponSetsNew(language: language, region: region, offerCategory: offerCategory, completion: { couponSets in | ||
| 2537 | + if let couponSets = couponSets { | ||
| 2538 | + continuation.resume(returning: couponSets) | ||
| 2539 | + } else { | ||
| 2540 | + continuation.resume(throwing: WarplyError.networkError) | ||
| 2541 | + } | ||
| 2542 | + }, failureCallback: { errorCode in | ||
| 2543 | + continuation.resume(throwing: WarplyError.unknownError(errorCode)) | ||
| 2544 | + }) | ||
| 2545 | + } | ||
| 2546 | + } | ||
| 2547 | + | ||
| 2460 | /// Get available coupons (async/await variant) | 2548 | /// Get available coupons (async/await variant) |
| 2461 | /// - Returns: Dictionary of coupon availability data | 2549 | /// - Returns: Dictionary of coupon availability data |
| 2462 | /// - Throws: WarplyError if the request fails | 2550 | /// - Throws: WarplyError if the request fails | ... | ... |
| ... | @@ -67,6 +67,7 @@ public enum Endpoint { | ... | @@ -67,6 +67,7 @@ public enum Endpoint { |
| 67 | // Coupons | 67 | // Coupons |
| 68 | case getCoupons(language: String, couponsetType: String) | 68 | case getCoupons(language: String, couponsetType: String) |
| 69 | case getCouponSets(language: String, active: Bool, visible: Bool, uuids: [String]?) | 69 | case getCouponSets(language: String, active: Bool, visible: Bool, uuids: [String]?) |
| 70 | + case getCouponSetsNew(language: String, active: Bool, visible: Bool, region: String?, offerCategory: String?) | ||
| 70 | case getAvailableCoupons | 71 | case getAvailableCoupons |
| 71 | 72 | ||
| 72 | // Market & Merchants | 73 | // Market & Merchants |
| ... | @@ -142,7 +143,7 @@ public enum Endpoint { | ... | @@ -142,7 +143,7 @@ public enum Endpoint { |
| 142 | return "/api/mobile/v2/{appUUID}/context/" | 143 | return "/api/mobile/v2/{appUUID}/context/" |
| 143 | 144 | ||
| 144 | // Authenticated Context endpoints - /oauth/{appUUID}/context | 145 | // Authenticated Context endpoints - /oauth/{appUUID}/context |
| 145 | - case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent: | 146 | + case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent, .getCouponSetsNew: |
| 146 | return "/oauth/{appUUID}/context" | 147 | return "/oauth/{appUUID}/context" |
| 147 | 148 | ||
| 148 | // Session endpoints - /api/session/{sessionUuid} | 149 | // Session endpoints - /api/session/{sessionUuid} |
| ... | @@ -170,7 +171,7 @@ public enum Endpoint { | ... | @@ -170,7 +171,7 @@ public enum Endpoint { |
| 170 | public var method: HTTPMethod { | 171 | public var method: HTTPMethod { |
| 171 | switch self { | 172 | switch self { |
| 172 | case .register, .changePassword, .resetPassword, .requestOtp, .verifyTicket, .refreshToken, .logout, .getCampaigns, .getCampaignsPersonalized, | 173 | case .register, .changePassword, .resetPassword, .requestOtp, .verifyTicket, .refreshToken, .logout, .getCampaigns, .getCampaignsPersonalized, |
| 173 | - .getCoupons, .getCouponSets, .getAvailableCoupons, | 174 | + .getCoupons, .getCouponSets, .getCouponSetsNew, .getAvailableCoupons, |
| 174 | .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getMerchants, .getMerchantCategories, .getStores, .getArticles, .sendEvent, .sendDeviceInfo, .getCosmoteUser, .deiLogin, .getCarouselContent: | 175 | .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getMerchants, .getMerchantCategories, .getStores, .getArticles, .sendEvent, .sendDeviceInfo, .getCosmoteUser, .deiLogin, .getCarouselContent: |
| 175 | return .POST | 176 | return .POST |
| 176 | case .getSingleCampaign, .getNetworkStatus: | 177 | case .getSingleCampaign, .getNetworkStatus: |
| ... | @@ -296,6 +297,23 @@ public enum Endpoint { | ... | @@ -296,6 +297,23 @@ public enum Endpoint { |
| 296 | "coupon": couponParams | 297 | "coupon": couponParams |
| 297 | ] | 298 | ] |
| 298 | 299 | ||
| 300 | + case .getCouponSetsNew(let language, let active, let visible, let region, let offerCategory): | ||
| 301 | + var couponParams: [String: Any] = [ | ||
| 302 | + "action": "get_offers", | ||
| 303 | + "active": active, | ||
| 304 | + "visible": visible, | ||
| 305 | + "language": language | ||
| 306 | + ] | ||
| 307 | + if let region = region { | ||
| 308 | + couponParams["region"] = region | ||
| 309 | + } | ||
| 310 | + if let offerCategory = offerCategory { | ||
| 311 | + couponParams["offer_category"] = offerCategory | ||
| 312 | + } | ||
| 313 | + return [ | ||
| 314 | + "coupon": couponParams | ||
| 315 | + ] | ||
| 316 | + | ||
| 299 | case .getAvailableCoupons: | 317 | case .getAvailableCoupons: |
| 300 | return [ | 318 | return [ |
| 301 | "coupon": [ | 319 | "coupon": [ |
| ... | @@ -505,7 +523,7 @@ public enum Endpoint { | ... | @@ -505,7 +523,7 @@ public enum Endpoint { |
| 505 | return .standardContext | 523 | return .standardContext |
| 506 | 524 | ||
| 507 | // Authenticated Context - /oauth/{appUUID}/context | 525 | // Authenticated Context - /oauth/{appUUID}/context |
| 508 | - case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent: | 526 | + case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent, .getCouponSetsNew: |
| 509 | return .authenticatedContext | 527 | return .authenticatedContext |
| 510 | 528 | ||
| 511 | // Authentication - /oauth/{appUUID}/login, /oauth/{appUUID}/token | 529 | // Authentication - /oauth/{appUUID}/login, /oauth/{appUUID}/token |
| ... | @@ -547,7 +565,7 @@ public enum Endpoint { | ... | @@ -547,7 +565,7 @@ public enum Endpoint { |
| 547 | return .standard | 565 | return .standard |
| 548 | 566 | ||
| 549 | // Bearer Token Authentication (loyalty headers + Authorization: Bearer) | 567 | // Bearer Token Authentication (loyalty headers + Authorization: Bearer) |
| 550 | - case .changePassword, .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent: | 568 | + case .changePassword, .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent, .getCouponSetsNew: |
| 551 | return .bearerToken | 569 | return .bearerToken |
| 552 | 570 | ||
| 553 | // Basic Authentication (loyalty headers + Authorization: Basic) | 571 | // Basic Authentication (loyalty headers + Authorization: Basic) | ... | ... |
| ... | @@ -77,7 +77,20 @@ public class CouponSetItemModel { | ... | @@ -77,7 +77,20 @@ public class CouponSetItemModel { |
| 77 | private var points_cause: String? | 77 | private var points_cause: String? |
| 78 | private var third_party_service: String? | 78 | private var third_party_service: String? |
| 79 | private var category: String? | 79 | private var category: String? |
| 80 | - | 80 | + private var offer_category: String? |
| 81 | + | ||
| 82 | + // getCouponSetsNew fields | ||
| 83 | + private var score: String? | ||
| 84 | + private var locked: Bool? | ||
| 85 | + private var app_img_preview: String? | ||
| 86 | + private var app_imgs: [String]? | ||
| 87 | + private var merchant_admin_name: String? | ||
| 88 | + private var merchant_img_preview: String? | ||
| 89 | + private var merchant_website: String? | ||
| 90 | + private var regions: [String]? | ||
| 91 | + private var show_as_banner: [String: Any]? | ||
| 92 | + private var tagging: [String: Any]? | ||
| 93 | + | ||
| 81 | // Bound merchant data for performance | 94 | // Bound merchant data for performance |
| 82 | private var merchant: MerchantModel? | 95 | private var merchant: MerchantModel? |
| 83 | 96 | ||
| ... | @@ -159,7 +172,38 @@ public class CouponSetItemModel { | ... | @@ -159,7 +172,38 @@ public class CouponSetItemModel { |
| 159 | self.points_cause = dictionary["points_cause"] as? String? ?? "" | 172 | self.points_cause = dictionary["points_cause"] as? String? ?? "" |
| 160 | self.third_party_service = dictionary["third_party_service"] as? String? ?? "" | 173 | self.third_party_service = dictionary["third_party_service"] as? String? ?? "" |
| 161 | self.category = dictionary["category"] as? String? ?? "" | 174 | self.category = dictionary["category"] as? String? ?? "" |
| 162 | - | 175 | + self.offer_category = dictionary["offer_category"] as? String? ?? "" |
| 176 | + | ||
| 177 | + // getCouponSetsNew fields | ||
| 178 | + self.score = dictionary["score"] as? String | ||
| 179 | + if let lockedInt = dictionary["locked"] as? Int { | ||
| 180 | + self.locked = lockedInt == 1 | ||
| 181 | + } else { | ||
| 182 | + self.locked = dictionary["locked"] as? Bool | ||
| 183 | + } | ||
| 184 | + self.app_img_preview = dictionary["app_img_preview"] as? String | ||
| 185 | + self.merchant_admin_name = dictionary["merchant_admin_name"] as? String | ||
| 186 | + self.merchant_img_preview = dictionary["merchant_img_preview"] as? String | ||
| 187 | + self.merchant_website = dictionary["merchant_website"] as? String | ||
| 188 | + self.show_as_banner = dictionary["show_as_banner"] as? [String: Any] | ||
| 189 | + self.tagging = dictionary["tagging"] as? [String: Any] | ||
| 190 | + | ||
| 191 | + // app_imgs — JSON-encoded string array, same pattern as img | ||
| 192 | + if let appImgsString = dictionary["app_imgs"] as? String, | ||
| 193 | + let appImgsData = appImgsString.data(using: .utf8), | ||
| 194 | + let appImgsArray = try? JSONSerialization.jsonObject(with: appImgsData, options: []) as? [String] { | ||
| 195 | + self.app_imgs = appImgsArray | ||
| 196 | + } else { | ||
| 197 | + self.app_imgs = [] | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + // regions — array with potential NSNull entries, filter them out | ||
| 201 | + if let regionsArray = dictionary["regions"] as? [Any] { | ||
| 202 | + self.regions = regionsArray.compactMap { $0 as? String } | ||
| 203 | + } else { | ||
| 204 | + self.regions = [] | ||
| 205 | + } | ||
| 206 | + | ||
| 163 | // UPDATED EXPIRATION HANDLING - store raw date string | 207 | // UPDATED EXPIRATION HANDLING - store raw date string |
| 164 | if let expirationObject = dictionary["expiration"] as? [String: Any], | 208 | if let expirationObject = dictionary["expiration"] as? [String: Any], |
| 165 | let expirationValue = expirationObject["value"] as? String { | 209 | let expirationValue = expirationObject["value"] as? String { |
| ... | @@ -266,7 +310,20 @@ public class CouponSetItemModel { | ... | @@ -266,7 +310,20 @@ public class CouponSetItemModel { |
| 266 | public var _points_cause: String { get { return self.points_cause ?? "" } } | 310 | public var _points_cause: String { get { return self.points_cause ?? "" } } |
| 267 | public var _third_party_service: String { get { return self.third_party_service ?? "" } } | 311 | public var _third_party_service: String { get { return self.third_party_service ?? "" } } |
| 268 | public var _category: String { get { return self.category ?? "" } } | 312 | public var _category: String { get { return self.category ?? "" } } |
| 269 | - | 313 | + public var _offer_category: String { get { return self.offer_category ?? "" } } |
| 314 | + | ||
| 315 | + // getCouponSetsNew field accessors | ||
| 316 | + public var _score: String? { get { return self.score } set { self.score = newValue } } | ||
| 317 | + public var _locked: Bool { get { return self.locked ?? false } } | ||
| 318 | + public var _app_img_preview: String { get { return self.app_img_preview ?? "" } } | ||
| 319 | + public var _app_imgs: [String]? { get { return self.app_imgs } } | ||
| 320 | + public var _merchant_admin_name: String { get { return self.merchant_admin_name ?? "" } } | ||
| 321 | + public var _merchant_img_preview: String { get { return self.merchant_img_preview ?? "" } } | ||
| 322 | + public var _merchant_website: String { get { return self.merchant_website ?? "" } } | ||
| 323 | + public var _regions: [String]? { get { return self.regions } } | ||
| 324 | + public var _show_as_banner: [String: Any]? { get { return self.show_as_banner } } | ||
| 325 | + public var _tagging: [String: Any]? { get { return self.tagging } } | ||
| 326 | + | ||
| 270 | // Bound merchant data accessor | 327 | // Bound merchant data accessor |
| 271 | public var _merchant: MerchantModel? { | 328 | public var _merchant: MerchantModel? { |
| 272 | get { return self.merchant } | 329 | get { return self.merchant } | ... | ... |
| ... | @@ -270,20 +270,49 @@ import UIKit | ... | @@ -270,20 +270,49 @@ import UIKit |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | // MARK: - Coupon Sets Loading | 272 | // MARK: - Coupon Sets Loading |
| 273 | + // private func loadCouponSets() { | ||
| 274 | + // // Load coupon sets from WarplySDK | ||
| 275 | + // WarplySDK.shared.getCouponSets { [weak self] couponSets in | ||
| 276 | + // guard let self = self, let couponSets = couponSets else { return } | ||
| 277 | + | ||
| 278 | + // self.couponSets = couponSets | ||
| 279 | + | ||
| 280 | + // // Load merchants after getting coupon sets | ||
| 281 | + // self.loadMerchants() | ||
| 282 | + | ||
| 283 | + // } failureCallback: { [weak self] errorCode in | ||
| 284 | + // print("Failed to load coupon sets: \(errorCode)") | ||
| 285 | + // // No sections added on failure - table will be empty | ||
| 286 | + // } | ||
| 287 | + // } | ||
| 288 | + | ||
| 273 | private func loadCouponSets() { | 289 | private func loadCouponSets() { |
| 274 | // Load coupon sets from WarplySDK | 290 | // Load coupon sets from WarplySDK |
| 275 | - WarplySDK.shared.getCouponSets { [weak self] couponSets in | 291 | + // WarplySDK.shared.getCouponSets { [weak self] couponSets in |
| 276 | - guard let self = self, let couponSets = couponSets else { return } | 292 | + // guard let self = self, let couponSets = couponSets else { return } |
| 277 | 293 | ||
| 278 | - self.couponSets = couponSets | 294 | + // self.couponSets = couponSets |
| 279 | 295 | ||
| 280 | - // Load merchants after getting coupon sets | 296 | + // // Load merchants after getting coupon sets |
| 281 | - self.loadMerchants() | 297 | + // self.loadMerchants() |
| 282 | 298 | ||
| 283 | - } failureCallback: { [weak self] errorCode in | 299 | + // } failureCallback: { [weak self] errorCode in |
| 284 | - print("Failed to load coupon sets: \(errorCode)") | 300 | + // print("Failed to load coupon sets: \(errorCode)") |
| 285 | - // No sections added on failure - table will be empty | 301 | + // // No sections added on failure - table will be empty |
| 286 | - } | 302 | + // } |
| 303 | + | ||
| 304 | + WarplySDK.shared.getCouponSetsNew( | ||
| 305 | + completion: { [weak self] couponSets in | ||
| 306 | + guard let self = self, let couponSets = couponSets else { return } | ||
| 307 | + | ||
| 308 | + self.couponSets = couponSets | ||
| 309 | + self?.createCouponSetsSection() | ||
| 310 | + | ||
| 311 | + }, | ||
| 312 | + failureCallback: { errorCode in | ||
| 313 | + print("=== getCouponSetsNew Error: \(errorCode)") | ||
| 314 | + } | ||
| 315 | + ) | ||
| 287 | } | 316 | } |
| 288 | 317 | ||
| 289 | // MARK: - Coupons Loading | 318 | // MARK: - Coupons Loading |
| ... | @@ -385,6 +414,8 @@ import UIKit | ... | @@ -385,6 +414,8 @@ import UIKit |
| 385 | } | 414 | } |
| 386 | 415 | ||
| 387 | private func createCouponSetsSection() { | 416 | private func createCouponSetsSection() { |
| 417 | + // TODO: no merchant match any more | ||
| 418 | + | ||
| 388 | print("🔍 [MyRewardsViewController] Starting coupon filtering:") | 419 | print("🔍 [MyRewardsViewController] Starting coupon filtering:") |
| 389 | print(" - Coupon Sets: \(couponSets.count)") | 420 | print(" - Coupon Sets: \(couponSets.count)") |
| 390 | print(" - Merchants: \(merchants.count)") | 421 | print(" - Merchants: \(merchants.count)") | ... | ... |
-
Please register or login to post a comment