Showing
10 changed files
with
189 additions
and
28 deletions
| ... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
| 7 | <key>Pods-SwiftWarplyFramework.xcscheme_^#shared#^_</key> | 7 | <key>Pods-SwiftWarplyFramework.xcscheme_^#shared#^_</key> |
| 8 | <dict> | 8 | <dict> |
| 9 | <key>orderHint</key> | 9 | <key>orderHint</key> |
| 10 | - <integer>1</integer> | 10 | + <integer>0</integer> |
| 11 | </dict> | 11 | </dict> |
| 12 | </dict> | 12 | </dict> |
| 13 | </dict> | 13 | </dict> | ... | ... |
| ... | @@ -76,6 +76,7 @@ | ... | @@ -76,6 +76,7 @@ |
| 76 | 626AF6DB2F698FF1008BCA08 /* MerchantAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626AF6DA2F698FF1008BCA08 /* MerchantAnnotation.swift */; }; | 76 | 626AF6DB2F698FF1008BCA08 /* MerchantAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626AF6DA2F698FF1008BCA08 /* MerchantAnnotation.swift */; }; |
| 77 | 626AF6DE2F699081008BCA08 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626AF6DC2F699081008BCA08 /* MapViewController.swift */; }; | 77 | 626AF6DE2F699081008BCA08 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626AF6DC2F699081008BCA08 /* MapViewController.swift */; }; |
| 78 | 626AF6DF2F699081008BCA08 /* MapViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 626AF6DD2F699081008BCA08 /* MapViewController.xib */; }; | 78 | 626AF6DF2F699081008BCA08 /* MapViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 626AF6DD2F699081008BCA08 /* MapViewController.xib */; }; |
| 79 | + 626DC8012F6ACA3B00CFC8C2 /* CarouselItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626DC8002F6ACA3B00CFC8C2 /* CarouselItemModel.swift */; }; | ||
| 79 | 62A0A6D32F67FEDC00508534 /* MyCouponsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */; }; | 80 | 62A0A6D32F67FEDC00508534 /* MyCouponsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */; }; |
| 80 | 62A0A6D42F67FEDC00508534 /* MyCouponsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */; }; | 81 | 62A0A6D42F67FEDC00508534 /* MyCouponsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */; }; |
| 81 | 62A0A6D82F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */; }; | 82 | 62A0A6D82F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */; }; |
| ... | @@ -160,6 +161,7 @@ | ... | @@ -160,6 +161,7 @@ |
| 160 | 626AF6DA2F698FF1008BCA08 /* MerchantAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MerchantAnnotation.swift; sourceTree = "<group>"; }; | 161 | 626AF6DA2F698FF1008BCA08 /* MerchantAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MerchantAnnotation.swift; sourceTree = "<group>"; }; |
| 161 | 626AF6DC2F699081008BCA08 /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = "<group>"; }; | 162 | 626AF6DC2F699081008BCA08 /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = "<group>"; }; |
| 162 | 626AF6DD2F699081008BCA08 /* MapViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MapViewController.xib; sourceTree = "<group>"; }; | 163 | 626AF6DD2F699081008BCA08 /* MapViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MapViewController.xib; sourceTree = "<group>"; }; |
| 164 | + 626DC8002F6ACA3B00CFC8C2 /* CarouselItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselItemModel.swift; sourceTree = "<group>"; }; | ||
| 163 | 62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCouponsViewController.swift; sourceTree = "<group>"; }; | 165 | 62A0A6D12F67FEDC00508534 /* MyCouponsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCouponsViewController.swift; sourceTree = "<group>"; }; |
| 164 | 62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyCouponsViewController.xib; sourceTree = "<group>"; }; | 166 | 62A0A6D22F67FEDC00508534 /* MyCouponsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyCouponsViewController.xib; sourceTree = "<group>"; }; |
| 165 | 62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCouponsHeaderTableViewCell.swift; sourceTree = "<group>"; }; | 167 | 62A0A6D62F680C6A00508534 /* MyCouponsHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCouponsHeaderTableViewCell.swift; sourceTree = "<group>"; }; |
| ... | @@ -212,6 +214,7 @@ | ... | @@ -212,6 +214,7 @@ |
| 212 | 1E089DF42DF87C39007459F1 /* Response.swift */, | 214 | 1E089DF42DF87C39007459F1 /* Response.swift */, |
| 213 | 1E089DF52DF87C39007459F1 /* SectionModel.swift */, | 215 | 1E089DF52DF87C39007459F1 /* SectionModel.swift */, |
| 214 | 1E116F6A2DE86CAD009AE791 /* Models.swift */, | 216 | 1E116F6A2DE86CAD009AE791 /* Models.swift */, |
| 217 | + 626DC8002F6ACA3B00CFC8C2 /* CarouselItemModel.swift */, | ||
| 215 | ); | 218 | ); |
| 216 | path = models; | 219 | path = models; |
| 217 | sourceTree = "<group>"; | 220 | sourceTree = "<group>"; |
| ... | @@ -733,6 +736,7 @@ | ... | @@ -733,6 +736,7 @@ |
| 733 | 1E089E0A2DF87D16007459F1 /* EventDispatcher.swift in Sources */, | 736 | 1E089E0A2DF87D16007459F1 /* EventDispatcher.swift in Sources */, |
| 734 | 1E116F692DE845B1009AE791 /* ProfileFilterCollectionViewCell.swift in Sources */, | 737 | 1E116F692DE845B1009AE791 /* ProfileFilterCollectionViewCell.swift in Sources */, |
| 735 | 1E0E72472E0C3B1200BC926F /* DatabaseManager.swift in Sources */, | 738 | 1E0E72472E0C3B1200BC926F /* DatabaseManager.swift in Sources */, |
| 739 | + 626DC8012F6ACA3B00CFC8C2 /* CarouselItemModel.swift in Sources */, | ||
| 736 | 1EDBAF0D2DE8441000911E79 /* ProfileQuestionnaireTableViewCell.swift in Sources */, | 740 | 1EDBAF0D2DE8441000911E79 /* ProfileQuestionnaireTableViewCell.swift in Sources */, |
| 737 | 1E64E1842DE48E0600543217 /* MyRewardsOfferCollectionViewCell.swift in Sources */, | 741 | 1E64E1842DE48E0600543217 /* MyRewardsOfferCollectionViewCell.swift in Sources */, |
| 738 | E6A77955282933E70045BBA8 /* ViewControllerExtensions.swift in Sources */, | 742 | E6A77955282933E70045BBA8 /* ViewControllerExtensions.swift in Sources */, | ... | ... |
| ... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
| 7 | <key>SwiftWarplyFramework.xcscheme_^#shared#^_</key> | 7 | <key>SwiftWarplyFramework.xcscheme_^#shared#^_</key> |
| 8 | <dict> | 8 | <dict> |
| 9 | <key>orderHint</key> | 9 | <key>orderHint</key> |
| 10 | - <integer>0</integer> | 10 | + <integer>1</integer> |
| 11 | </dict> | 11 | </dict> |
| 12 | </dict> | 12 | </dict> |
| 13 | </dict> | 13 | </dict> | ... | ... |
No preview for this file type
| ... | @@ -4335,3 +4335,66 @@ public final class WarplySDK { | ... | @@ -4335,3 +4335,66 @@ public final class WarplySDK { |
| 4335 | setCarouselList(carouselArray) | 4335 | setCarouselList(carouselArray) |
| 4336 | } | 4336 | } |
| 4337 | } | 4337 | } |
| 4338 | + | ||
| 4339 | +// MARK: - Carousel Content | ||
| 4340 | + | ||
| 4341 | +extension WarplySDK { | ||
| 4342 | + | ||
| 4343 | + /// Get carousel content | ||
| 4344 | + /// - Parameters: | ||
| 4345 | + /// - completion: Completion handler with array of CarouselItemModel | ||
| 4346 | + /// - failureCallback: Failure callback with error code | ||
| 4347 | + public func getCarouselContent(completion: @escaping ([CarouselItemModel]?) -> Void, failureCallback: @escaping (Int) -> Void) { | ||
| 4348 | + Task { | ||
| 4349 | + do { | ||
| 4350 | + let endpoint = Endpoint.getCarouselContent | ||
| 4351 | + let response = try await networkService.requestRaw(endpoint) | ||
| 4352 | + | ||
| 4353 | + await MainActor.run { | ||
| 4354 | + if response["status"] as? Int == 1 { | ||
| 4355 | + let dynatraceEvent = LoyaltySDKDynatraceEventModel() | ||
| 4356 | + dynatraceEvent._eventName = "custom_success_get_carousel_content_loyalty" | ||
| 4357 | + dynatraceEvent._parameters = nil | ||
| 4358 | + self.postFrameworkEvent("dynatrace", sender: dynatraceEvent) | ||
| 4359 | + | ||
| 4360 | + var items: [CarouselItemModel] = [] | ||
| 4361 | + if let resultArray = response["result"] as? [[String: Any]] { | ||
| 4362 | + for itemDict in resultArray { | ||
| 4363 | + items.append(CarouselItemModel(dictionary: itemDict)) | ||
| 4364 | + } | ||
| 4365 | + } | ||
| 4366 | + print("✅ [getCarouselContent] Parsed \(items.count) carousel items") | ||
| 4367 | + completion(items) | ||
| 4368 | + } else { | ||
| 4369 | + let dynatraceEvent = LoyaltySDKDynatraceEventModel() | ||
| 4370 | + dynatraceEvent._eventName = "custom_error_get_carousel_content_loyalty" | ||
| 4371 | + dynatraceEvent._parameters = nil | ||
| 4372 | + self.postFrameworkEvent("dynatrace", sender: dynatraceEvent) | ||
| 4373 | + failureCallback(-1) | ||
| 4374 | + } | ||
| 4375 | + } | ||
| 4376 | + } catch { | ||
| 4377 | + await MainActor.run { | ||
| 4378 | + self.handleError(error, context: "getCarouselContent", endpoint: "getCarouselContent", failureCallback: failureCallback) | ||
| 4379 | + } | ||
| 4380 | + } | ||
| 4381 | + } | ||
| 4382 | + } | ||
| 4383 | + | ||
| 4384 | + /// Get carousel content (async/await variant) | ||
| 4385 | + /// - Returns: Array of CarouselItemModel | ||
| 4386 | + /// - Throws: WarplyError if the request fails | ||
| 4387 | + public func getCarouselContent() async throws -> [CarouselItemModel] { | ||
| 4388 | + return try await withCheckedThrowingContinuation { continuation in | ||
| 4389 | + getCarouselContent(completion: { result in | ||
| 4390 | + if let result = result { | ||
| 4391 | + continuation.resume(returning: result) | ||
| 4392 | + } else { | ||
| 4393 | + continuation.resume(throwing: WarplyError.networkError) | ||
| 4394 | + } | ||
| 4395 | + }, failureCallback: { errorCode in | ||
| 4396 | + continuation.resume(throwing: WarplyError.unknownError(errorCode)) | ||
| 4397 | + }) | ||
| 4398 | + } | ||
| 4399 | + } | ||
| 4400 | +} | ... | ... |
| ... | @@ -78,6 +78,9 @@ public enum Endpoint { | ... | @@ -78,6 +78,9 @@ public enum Endpoint { |
| 78 | // Articles | 78 | // Articles |
| 79 | case getArticles(language: String, categories: [String]?) | 79 | case getArticles(language: String, categories: [String]?) |
| 80 | 80 | ||
| 81 | + // Carousel Content | ||
| 82 | + case getCarouselContent | ||
| 83 | + | ||
| 81 | // Card Management | 84 | // Card Management |
| 82 | case addCard(cardNumber: String, cardIssuer: String, cardHolder: String, expirationMonth: String, expirationYear: String) | 85 | case addCard(cardNumber: String, cardIssuer: String, cardHolder: String, expirationMonth: String, expirationYear: String) |
| 83 | case getCards | 86 | case getCards |
| ... | @@ -139,7 +142,7 @@ public enum Endpoint { | ... | @@ -139,7 +142,7 @@ public enum Endpoint { |
| 139 | return "/api/mobile/v2/{appUUID}/context/" | 142 | return "/api/mobile/v2/{appUUID}/context/" |
| 140 | 143 | ||
| 141 | // Authenticated Context endpoints - /oauth/{appUUID}/context | 144 | // Authenticated Context endpoints - /oauth/{appUUID}/context |
| 142 | - case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon: | 145 | + case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent: |
| 143 | return "/oauth/{appUUID}/context" | 146 | return "/oauth/{appUUID}/context" |
| 144 | 147 | ||
| 145 | // Session endpoints - /api/session/{sessionUuid} | 148 | // Session endpoints - /api/session/{sessionUuid} |
| ... | @@ -168,7 +171,7 @@ public enum Endpoint { | ... | @@ -168,7 +171,7 @@ public enum Endpoint { |
| 168 | switch self { | 171 | switch self { |
| 169 | case .register, .changePassword, .resetPassword, .requestOtp, .verifyTicket, .refreshToken, .logout, .getCampaigns, .getCampaignsPersonalized, | 172 | case .register, .changePassword, .resetPassword, .requestOtp, .verifyTicket, .refreshToken, .logout, .getCampaigns, .getCampaignsPersonalized, |
| 170 | .getCoupons, .getCouponSets, .getAvailableCoupons, | 173 | .getCoupons, .getCouponSets, .getAvailableCoupons, |
| 171 | - .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getMerchants, .getMerchantCategories, .getStores, .getArticles, .sendEvent, .sendDeviceInfo, .getCosmoteUser, .deiLogin: | 174 | + .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getMerchants, .getMerchantCategories, .getStores, .getArticles, .sendEvent, .sendDeviceInfo, .getCosmoteUser, .deiLogin, .getCarouselContent: |
| 172 | return .POST | 175 | return .POST |
| 173 | case .getSingleCampaign, .getNetworkStatus: | 176 | case .getSingleCampaign, .getNetworkStatus: |
| 174 | return .GET | 177 | return .GET |
| ... | @@ -438,6 +441,14 @@ public enum Endpoint { | ... | @@ -438,6 +441,14 @@ public enum Endpoint { |
| 438 | "content": contentParams | 441 | "content": contentParams |
| 439 | ] | 442 | ] |
| 440 | 443 | ||
| 444 | + // Carousel Content - using content structure with get_carousel action | ||
| 445 | + case .getCarouselContent: | ||
| 446 | + return [ | ||
| 447 | + "content": [ | ||
| 448 | + "action": "get_carousel" | ||
| 449 | + ] | ||
| 450 | + ] | ||
| 451 | + | ||
| 441 | // Analytics endpoints - events structure | 452 | // Analytics endpoints - events structure |
| 442 | case .sendEvent(let eventName, let priority): | 453 | case .sendEvent(let eventName, let priority): |
| 443 | return [ | 454 | return [ |
| ... | @@ -494,7 +505,7 @@ public enum Endpoint { | ... | @@ -494,7 +505,7 @@ public enum Endpoint { |
| 494 | return .standardContext | 505 | return .standardContext |
| 495 | 506 | ||
| 496 | // Authenticated Context - /oauth/{appUUID}/context | 507 | // Authenticated Context - /oauth/{appUUID}/context |
| 497 | - case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon: | 508 | + case .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent: |
| 498 | return .authenticatedContext | 509 | return .authenticatedContext |
| 499 | 510 | ||
| 500 | // Authentication - /oauth/{appUUID}/login, /oauth/{appUUID}/token | 511 | // Authentication - /oauth/{appUUID}/login, /oauth/{appUUID}/token |
| ... | @@ -536,7 +547,7 @@ public enum Endpoint { | ... | @@ -536,7 +547,7 @@ public enum Endpoint { |
| 536 | return .standard | 547 | return .standard |
| 537 | 548 | ||
| 538 | // Bearer Token Authentication (loyalty headers + Authorization: Bearer) | 549 | // Bearer Token Authentication (loyalty headers + Authorization: Bearer) |
| 539 | - case .changePassword, .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon: | 550 | + case .changePassword, .getCampaignsPersonalized, .getCoupons, .getMarketPassDetails, .getProfile, .addCard, .getCards, .deleteCard, .getTransactionHistory, .getPointsHistory, .validateCoupon, .redeemCoupon, .retrieveCoupon, .getCarouselContent: |
| 540 | return .bearerToken | 551 | return .bearerToken |
| 541 | 552 | ||
| 542 | // Basic Authentication (loyalty headers + Authorization: Basic) | 553 | // Basic Authentication (loyalty headers + Authorization: Basic) | ... | ... |
| ... | @@ -38,13 +38,17 @@ public class MyRewardsBannerOfferCollectionViewCell: UICollectionViewCell { | ... | @@ -38,13 +38,17 @@ public class MyRewardsBannerOfferCollectionViewCell: UICollectionViewCell { |
| 38 | contentView.layer.masksToBounds = true | 38 | contentView.layer.masksToBounds = true |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | - func configureCell(data: CampaignItemModel) { | 41 | + // func configureCell(data: CampaignItemModel) { |
| 42 | - // Use campaign's banner image - no hardcoded defaults | 42 | + // // Use campaign's banner image - no hardcoded defaults |
| 43 | - self.postImageURL = data._banner_img_mobile ?? "" | 43 | + // self.postImageURL = data._banner_img_mobile ?? "" |
| 44 | - } | 44 | + // } |
| 45 | - | 45 | + |
| 46 | - func configureCell(data: ArticleModel) { | 46 | + // func configureCell(data: ArticleModel) { |
| 47 | - // Use article's preview image - same visual treatment as campaigns | 47 | + // // Use article's preview image - same visual treatment as campaigns |
| 48 | - self.postImageURL = data._img_preview.isEmpty ? nil : data._img_preview | 48 | + // self.postImageURL = data._img_preview.isEmpty ? nil : data._img_preview |
| 49 | + // } | ||
| 50 | + | ||
| 51 | + func configureCell(data: CarouselItemModel) { | ||
| 52 | + self.postImageURL = data._app_img | ||
| 49 | } | 53 | } |
| 50 | } | 54 | } | ... | ... |
| ... | @@ -10,6 +10,7 @@ import UIKit | ... | @@ -10,6 +10,7 @@ import UIKit |
| 10 | protocol MyRewardsBannerOffersScrollTableViewCellDelegate: AnyObject { | 10 | protocol MyRewardsBannerOffersScrollTableViewCellDelegate: AnyObject { |
| 11 | func didSelectBannerOffer(_ index: Int) | 11 | func didSelectBannerOffer(_ index: Int) |
| 12 | func didSelectBannerArticle(_ index: Int) | 12 | func didSelectBannerArticle(_ index: Int) |
| 13 | + func didSelectBannerCarouselItem(_ index: Int) | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | @objc(MyRewardsBannerOffersScrollTableViewCell) | 16 | @objc(MyRewardsBannerOffersScrollTableViewCell) |
| ... | @@ -96,7 +97,7 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource, | ... | @@ -96,7 +97,7 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource, |
| 96 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | 97 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
| 97 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyRewardsBannerOfferCollectionViewCell", for: indexPath) as! MyRewardsBannerOfferCollectionViewCell | 98 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyRewardsBannerOfferCollectionViewCell", for: indexPath) as! MyRewardsBannerOfferCollectionViewCell |
| 98 | 99 | ||
| 99 | - // Handle both CampaignItemModel and ArticleModel | 100 | + // Handle CarouselItemModel |
| 100 | guard let data = self.data, | 101 | guard let data = self.data, |
| 101 | let items = data.items, | 102 | let items = data.items, |
| 102 | indexPath.row < items.count else { | 103 | indexPath.row < items.count else { |
| ... | @@ -105,10 +106,13 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource, | ... | @@ -105,10 +106,13 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource, |
| 105 | 106 | ||
| 106 | let item = items[indexPath.row] | 107 | let item = items[indexPath.row] |
| 107 | 108 | ||
| 108 | - if let campaign = item as? CampaignItemModel { | 109 | + // if let campaign = item as? CampaignItemModel { |
| 109 | - cell.configureCell(data: campaign) | 110 | + // cell.configureCell(data: campaign) |
| 110 | - } else if let article = item as? ArticleModel { | 111 | + // } else if let article = item as? ArticleModel { |
| 111 | - cell.configureCell(data: article) | 112 | + // cell.configureCell(data: article) |
| 113 | + // } | ||
| 114 | + if let carouselItem = item as? CarouselItemModel { | ||
| 115 | + cell.configureCell(data: carouselItem) | ||
| 112 | } | 116 | } |
| 113 | 117 | ||
| 114 | return cell | 118 | return cell |
| ... | @@ -124,10 +128,13 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource, | ... | @@ -124,10 +128,13 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource, |
| 124 | 128 | ||
| 125 | let item = items[indexPath.row] | 129 | let item = items[indexPath.row] |
| 126 | 130 | ||
| 127 | - if item is CampaignItemModel { | 131 | + // if item is CampaignItemModel { |
| 128 | - delegate?.didSelectBannerOffer(indexPath.row) | 132 | + // delegate?.didSelectBannerOffer(indexPath.row) |
| 129 | - } else if item is ArticleModel { | 133 | + // } else if item is ArticleModel { |
| 130 | - delegate?.didSelectBannerArticle(indexPath.row) | 134 | + // delegate?.didSelectBannerArticle(indexPath.row) |
| 135 | + // } | ||
| 136 | + if item is CarouselItemModel { | ||
| 137 | + delegate?.didSelectBannerCarouselItem(indexPath.row) | ||
| 131 | } | 138 | } |
| 132 | } | 139 | } |
| 133 | 140 | ... | ... |
| 1 | +// | ||
| 2 | +// CarouselItemModel.swift | ||
| 3 | +// SwiftWarplyFramework | ||
| 4 | +// | ||
| 5 | +// Created by Manos Chorianopoulos on 18/3/26. | ||
| 6 | +// | ||
| 7 | + | ||
| 8 | +import Foundation | ||
| 9 | + | ||
| 10 | +public class CarouselItemModel: NSObject { | ||
| 11 | + | ||
| 12 | + private var uuid: String? | ||
| 13 | + private var name: String? | ||
| 14 | + private var entity: String? | ||
| 15 | + private var app_img: String? | ||
| 16 | + private var app_url: String? | ||
| 17 | + private var url: String? | ||
| 18 | + private var web_img: String? | ||
| 19 | + private var web_img_responsive: String? | ||
| 20 | + | ||
| 21 | + public override init() { | ||
| 22 | + super.init() | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public init(dictionary: [String: Any]) { | ||
| 26 | + self.uuid = dictionary["uuid"] as? String ?? "" | ||
| 27 | + self.name = dictionary["name"] as? String ?? "" | ||
| 28 | + self.entity = dictionary["entity"] as? String ?? "" | ||
| 29 | + self.app_img = dictionary["app_img"] as? String | ||
| 30 | + self.app_url = dictionary["app_url"] as? String | ||
| 31 | + self.url = dictionary["url"] as? String | ||
| 32 | + self.web_img = dictionary["web_img"] as? String | ||
| 33 | + self.web_img_responsive = dictionary["web_img_responsive"] as? String | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public var _uuid: String { get { return self.uuid ?? "" } } | ||
| 37 | + public var _name: String { get { return self.name ?? "" } } | ||
| 38 | + public var _entity: String { get { return self.entity ?? "" } } | ||
| 39 | + public var _app_img: String? { get { return self.app_img } } | ||
| 40 | + public var _app_url: String? { get { return self.app_url } } | ||
| 41 | + public var _url: String? { get { return self.url } } | ||
| 42 | + public var _web_img: String? { get { return self.web_img } } | ||
| 43 | + public var _web_img_responsive: String? { get { return self.web_img_responsive } } | ||
| 44 | +} |
| ... | @@ -42,6 +42,9 @@ import UIKit | ... | @@ -42,6 +42,9 @@ import UIKit |
| 42 | // Dynamic sections array - populated by API calls | 42 | // Dynamic sections array - populated by API calls |
| 43 | var sections: [SectionModel] = [] | 43 | var sections: [SectionModel] = [] |
| 44 | 44 | ||
| 45 | + // carouselItems data for banners | ||
| 46 | + var carouselItems: [CarouselItemModel] = [] | ||
| 47 | + | ||
| 45 | // Campaign data for banners | 48 | // Campaign data for banners |
| 46 | var bannerCampaigns: [CampaignItemModel] = [] | 49 | var bannerCampaigns: [CampaignItemModel] = [] |
| 47 | 50 | ||
| ... | @@ -87,7 +90,8 @@ import UIKit | ... | @@ -87,7 +90,8 @@ import UIKit |
| 87 | 90 | ||
| 88 | // Load data | 91 | // Load data |
| 89 | loadProfile() // Load Profile | 92 | loadProfile() // Load Profile |
| 90 | - loadCampaigns() // Load campaigns | 93 | + loadCarouselContent() |
| 94 | + // loadCampaigns() // Load campaigns | ||
| 91 | loadCouponSets() // Load couponsets | 95 | loadCouponSets() // Load couponsets |
| 92 | loadCoupons() | 96 | loadCoupons() |
| 93 | 97 | ||
| ... | @@ -160,6 +164,23 @@ import UIKit | ... | @@ -160,6 +164,23 @@ import UIKit |
| 160 | self.navigationController?.setNavigationBarHidden(false, animated: animated) | 164 | self.navigationController?.setNavigationBarHidden(false, animated: animated) |
| 161 | } | 165 | } |
| 162 | 166 | ||
| 167 | + // MARK: - CarouselContent Loading | ||
| 168 | + private func loadCarouselContent() { | ||
| 169 | + // Load CarouselContent from WarplySDK | ||
| 170 | + WarplySDK.shared.getCarouselContent { [weak self] carouselContent in | ||
| 171 | + guard let self = self, let carouselContent = carouselContent else { | ||
| 172 | + return | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + self.carouselItems = carouselContent | ||
| 176 | + self.createBannerSection() | ||
| 177 | + | ||
| 178 | + print("=== getCarouselContent ✅ [MyRewardsViewController] Loaded \(carouselContent.count) carouselContent") | ||
| 179 | + | ||
| 180 | + } failureCallback: { [weak self] errorCode in | ||
| 181 | + print("=== getCarouselContent Failed to load carouselContent: \(errorCode)") | ||
| 182 | + } | ||
| 183 | + } | ||
| 163 | 184 | ||
| 164 | // MARK: - Campaign Loading | 185 | // MARK: - Campaign Loading |
| 165 | private func loadCampaigns() { | 186 | private func loadCampaigns() { |
| ... | @@ -220,11 +241,14 @@ import UIKit | ... | @@ -220,11 +241,14 @@ import UIKit |
| 220 | // Combine campaigns and articles for banner section | 241 | // Combine campaigns and articles for banner section |
| 221 | var bannerItems: [Any] = [] | 242 | var bannerItems: [Any] = [] |
| 222 | 243 | ||
| 223 | - // Add campaigns first | 244 | + // // Add campaigns first |
| 224 | - bannerItems.append(contentsOf: self.bannerCampaigns) | 245 | + // bannerItems.append(contentsOf: self.bannerCampaigns) |
| 225 | 246 | ||
| 226 | - // Add articles after campaigns | 247 | + // // Add articles after campaigns |
| 227 | - bannerItems.append(contentsOf: self.articles) | 248 | + // bannerItems.append(contentsOf: self.articles) |
| 249 | + | ||
| 250 | + // // Add articles after campaigns | ||
| 251 | + bannerItems.append(contentsOf: self.carouselItems) | ||
| 228 | 252 | ||
| 229 | // Create banner section if we have any items | 253 | // Create banner section if we have any items |
| 230 | if !bannerItems.isEmpty { | 254 | if !bannerItems.isEmpty { |
| ... | @@ -779,6 +803,10 @@ extension MyRewardsViewController: MyRewardsBannerOffersScrollTableViewCellDeleg | ... | @@ -779,6 +803,10 @@ extension MyRewardsViewController: MyRewardsBannerOffersScrollTableViewCellDeleg |
| 779 | openArticleViewController(with: index) | 803 | openArticleViewController(with: index) |
| 780 | } | 804 | } |
| 781 | 805 | ||
| 806 | + func didSelectBannerCarouselItem(_ index: Int) { | ||
| 807 | + // TODO: handle carousel item tap | ||
| 808 | + } | ||
| 809 | + | ||
| 782 | // func didTapProfileButton() { | 810 | // func didTapProfileButton() { |
| 783 | // // Navigate to ProfileViewController | 811 | // // Navigate to ProfileViewController |
| 784 | // openProfileViewController() | 812 | // openProfileViewController() | ... | ... |
-
Please register or login to post a comment