Toggle navigation
Toggle navigation
This project
Loading...
Sign in
open-source
/
warply_sdk_framework
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Authored by
Manos Chorianopoulos
2026-03-19 13:12:12 +0200
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
3224067dc299ffe0ceb1b3de5596769905c7b12f
3224067d
1 parent
159e00e8
new getCouponFilters request and MyRewardsViewController sections handling
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
279 additions
and
174 deletions
SwiftWarplyFramework/SwiftWarplyFramework/Core/WarplySDK.swift
SwiftWarplyFramework/SwiftWarplyFramework/Network/Endpoints.swift
SwiftWarplyFramework/SwiftWarplyFramework/models/Coupon.swift
SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift
SwiftWarplyFramework/SwiftWarplyFramework/Core/WarplySDK.swift
View file @
3224067
...
...
@@ -438,6 +438,7 @@ private final class SDKState {
var
carouselList
:
[
CampaignItemModel
]
=
[]
var
marketPassDetails
:
MarketPassDetailsModel
?
var
supermarketCampaign
:
CampaignItemModel
?
var
couponFilters
:
CouponFiltersDataModel
?
private
init
()
{}
}
...
...
@@ -2545,6 +2546,77 @@ public final class WarplySDK {
}
}
/// Get coupon filters via new endpoint
/// - Parameters:
/// - language: Optional language code (defaults to applicationLocale)
/// - completion: Returns the raw response dictionary on success
/// - failureCallback: Returns error code on failure
public
func
getCouponFilters
(
language
:
String
?
=
nil
,
completion
:
@escaping
(
CouponFiltersDataModel
?)
->
Void
,
failureCallback
:
@escaping
(
Int
)
->
Void
)
{
let
finalLanguage
=
language
??
self
.
applicationLocale
Task
{
do
{
let
endpoint
=
Endpoint
.
getCouponFilters
(
language
:
finalLanguage
)
let
response
=
try
await
networkService
.
requestRaw
(
endpoint
)
await
MainActor
.
run
{
print
(
"📥 getCouponFilters response:
\(
response
)
"
)
let
dynatraceEvent
=
LoyaltySDKDynatraceEventModel
()
dynatraceEvent
.
_eventName
=
"custom_success_couponfilters_loyalty"
dynatraceEvent
.
_parameters
=
nil
self
.
postFrameworkEvent
(
"dynatrace"
,
sender
:
dynatraceEvent
)
if
let
resultData
=
response
[
"result"
]
as?
[
String
:
Any
]
{
let
filterModel
=
CouponFiltersDataModel
(
dictionary
:
resultData
)
self
.
setCouponFilters
(
filterModel
)
completion
(
filterModel
)
}
else
{
completion
(
nil
)
}
}
}
catch
{
await
MainActor
.
run
{
let
dynatraceEvent
=
LoyaltySDKDynatraceEventModel
()
dynatraceEvent
.
_eventName
=
"custom_error_couponfilters_loyalty"
dynatraceEvent
.
_parameters
=
nil
self
.
postFrameworkEvent
(
"dynatrace"
,
sender
:
dynatraceEvent
)
if
let
networkError
=
error
as?
NetworkError
{
failureCallback
(
networkError
.
code
)
}
else
{
failureCallback
(
-
1
)
}
}
}
}
}
/// Get coupon filters (async/await variant)
/// - Parameters:
/// - language: Language code for localized content (optional, defaults to applicationLocale)
/// - Returns: Parsed filters model
/// - Throws: WarplyError if the request fails
public
func
getCouponFilters
(
language
:
String
?
=
nil
)
async
throws
->
CouponFiltersDataModel
{
return
try
await
withCheckedThrowingContinuation
{
continuation
in
getCouponFilters
(
language
:
language
,
completion
:
{
response
in
if
let
response
=
response
{
continuation
.
resume
(
returning
:
response
)
}
else
{
continuation
.
resume
(
throwing
:
WarplyError
.
networkError
)
}
},
failureCallback
:
{
errorCode
in
continuation
.
resume
(
throwing
:
WarplyError
.
unknownError
(
errorCode
))
})
}
}
/// Get available coupons (async/await variant)
/// - Returns: Dictionary of coupon availability data
/// - Throws: WarplyError if the request fails
...
...
@@ -4344,6 +4416,16 @@ public final class WarplySDK {
return
state
.
couponSets
}
/// Set coupon filters data
public
func
setCouponFilters
(
_
filters
:
CouponFiltersDataModel
)
{
state
.
couponFilters
=
filters
}
/// Get coupon filters data
public
func
getCouponFilters
()
->
CouponFiltersDataModel
?
{
return
state
.
couponFilters
}
/// Set seasonal list
public
func
setSeasonalList
(
_
seasonalCoupons
:
[
LoyaltyGiftsForYouPackage
])
{
state
.
seasonalList
=
seasonalCoupons
...
...
SwiftWarplyFramework/SwiftWarplyFramework/Network/Endpoints.swift
View file @
3224067
...
...
@@ -68,6 +68,7 @@ public enum Endpoint {
case
getCoupons
(
language
:
String
,
couponsetType
:
String
)
case
getCouponSets
(
language
:
String
,
active
:
Bool
,
visible
:
Bool
,
uuids
:
[
String
]?)
case
getCouponSetsNew
(
language
:
String
,
active
:
Bool
,
visible
:
Bool
,
region
:
String
?,
offerCategory
:
String
?)
case
getCouponFilters
(
language
:
String
)
case
getAvailableCoupons
// Market & Merchants
...
...
@@ -143,7 +144,7 @@ public enum Endpoint {
return
"/api/mobile/v2/{appUUID}/context/"
// Authenticated Context endpoints - /oauth/{appUUID}/context
case
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getCarouselContent
,
.
getCouponSetsNew
:
case
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getCarouselContent
,
.
getCouponSetsNew
,
.
getCouponFilters
:
return
"/oauth/{appUUID}/context"
// Session endpoints - /api/session/{sessionUuid}
...
...
@@ -171,7 +172,7 @@ public enum Endpoint {
public
var
method
:
HTTPMethod
{
switch
self
{
case
.
register
,
.
changePassword
,
.
resetPassword
,
.
requestOtp
,
.
verifyTicket
,
.
refreshToken
,
.
logout
,
.
getCampaigns
,
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getCouponSets
,
.
getCouponSetsNew
,
.
getAvailableCoupons
,
.
getCoupons
,
.
getCouponSets
,
.
getCouponSetsNew
,
.
get
CouponFilters
,
.
get
AvailableCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getMerchants
,
.
getMerchantCategories
,
.
getStores
,
.
getArticles
,
.
sendEvent
,
.
sendDeviceInfo
,
.
getCosmoteUser
,
.
deiLogin
,
.
getCarouselContent
:
return
.
POST
case
.
getSingleCampaign
,
.
getNetworkStatus
:
...
...
@@ -314,6 +315,14 @@ public enum Endpoint {
"coupon"
:
couponParams
]
case
.
getCouponFilters
(
let
language
):
return
[
"coupon"
:
[
"action"
:
"get_filters"
,
"language"
:
language
]
]
case
.
getAvailableCoupons
:
return
[
"coupon"
:
[
...
...
@@ -523,7 +532,7 @@ public enum Endpoint {
return
.
standardContext
// Authenticated Context - /oauth/{appUUID}/context
case
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getCarouselContent
,
.
getCouponSetsNew
:
case
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getCarouselContent
,
.
getCouponSetsNew
,
.
getCouponFilters
:
return
.
authenticatedContext
// Authentication - /oauth/{appUUID}/login, /oauth/{appUUID}/token
...
...
@@ -565,7 +574,7 @@ public enum Endpoint {
return
.
standard
// Bearer Token Authentication (loyalty headers + Authorization: Bearer)
case
.
changePassword
,
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getCarouselContent
,
.
getCouponSetsNew
:
case
.
changePassword
,
.
getCampaignsPersonalized
,
.
getCoupons
,
.
getMarketPassDetails
,
.
getProfile
,
.
addCard
,
.
getCards
,
.
deleteCard
,
.
getTransactionHistory
,
.
getPointsHistory
,
.
validateCoupon
,
.
redeemCoupon
,
.
retrieveCoupon
,
.
getCarouselContent
,
.
getCouponSetsNew
,
.
getCouponFilters
:
return
.
bearerToken
// Basic Authentication (loyalty headers + Authorization: Basic)
...
...
SwiftWarplyFramework/SwiftWarplyFramework/models/Coupon.swift
View file @
3224067
...
...
@@ -659,6 +659,72 @@ public class RedeemedSMHistoryModel {
}
}
// MARK: - Coupon Filters Models
public
class
CouponFiltersDataModel
{
private
var
offer_categories
:
[
CouponOfferCategoryModel
]?
private
var
regions
:
[
String
]?
public
init
(
dictionary
:
[
String
:
Any
])
{
if
let
regionsArray
=
dictionary
[
"regions"
]
as?
[
Any
]
{
self
.
regions
=
regionsArray
.
compactMap
{
$0
as?
String
}
}
else
{
self
.
regions
=
[]
}
if
let
categoriesArray
=
dictionary
[
"offer_categories"
]
as?
[[
String
:
Any
]]
{
var
tempCategories
:
[
CouponOfferCategoryModel
]
=
[]
for
catDict
in
categoriesArray
{
tempCategories
.
append
(
CouponOfferCategoryModel
(
dictionary
:
catDict
))
}
self
.
offer_categories
=
tempCategories
}
else
{
self
.
offer_categories
=
[]
}
}
public
var
_offer_categories
:
[
CouponOfferCategoryModel
]?
{
get
{
return
self
.
offer_categories
}
}
public
var
_regions
:
[
String
]?
{
get
{
return
self
.
regions
}
}
}
public
class
CouponOfferCategoryModel
{
private
var
uuid
:
String
?
private
var
admin_name
:
String
?
private
var
name
:
String
?
private
var
image
:
String
?
private
var
parent
:
String
?
private
var
children
:
[
CouponOfferCategoryModel
]?
public
init
(
dictionary
:
[
String
:
Any
])
{
self
.
uuid
=
dictionary
[
"uuid"
]
as?
String
self
.
admin_name
=
dictionary
[
"admin_name"
]
as?
String
self
.
name
=
dictionary
[
"name"
]
as?
String
if
let
imgString
=
dictionary
[
"image"
]
as?
String
{
self
.
image
=
imgString
.
trimmingCharacters
(
in
:
.
whitespacesAndNewlines
)
}
self
.
parent
=
dictionary
[
"parent"
]
as?
String
if
let
childrenArray
=
dictionary
[
"children"
]
as?
[[
String
:
Any
]]
{
var
tempChildren
:
[
CouponOfferCategoryModel
]
=
[]
for
childDict
in
childrenArray
{
tempChildren
.
append
(
CouponOfferCategoryModel
(
dictionary
:
childDict
))
}
self
.
children
=
tempChildren
}
else
{
self
.
children
=
[]
}
}
public
var
_uuid
:
String
{
get
{
return
self
.
uuid
??
""
}
}
public
var
_admin_name
:
String
{
get
{
return
self
.
admin_name
??
""
}
}
public
var
_name
:
String
{
get
{
return
self
.
name
??
""
}
}
public
var
_image
:
String
{
get
{
return
self
.
image
??
""
}
}
public
var
_parent
:
String
{
get
{
return
self
.
parent
??
""
}
}
public
var
_children
:
[
CouponOfferCategoryModel
]?
{
get
{
return
self
.
children
}
}
}
// MARK: - String Extension for HTML
// extension String {
...
...
SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift
View file @
3224067
...
...
@@ -53,6 +53,7 @@ import UIKit
// Coupon sets data
var
couponSets
:
[
CouponSetItemModel
]
=
[]
var
couponFilters
:
CouponFiltersDataModel
?
// Merchants data
var
merchants
:
[
MerchantModel
]
=
[]
...
...
@@ -91,7 +92,6 @@ import UIKit
// Load data
loadProfile
()
// Load Profile
loadCarouselContent
()
// loadCampaigns() // Load campaigns
loadCouponSets
()
// Load couponsets
loadCoupons
()
...
...
@@ -287,26 +287,12 @@ import UIKit
// }
private
func
loadCouponSets
()
{
// Load coupon sets from WarplySDK
// WarplySDK.shared.getCouponSets { [weak self] couponSets in
// guard let self = self, let couponSets = couponSets else { return }
// self.couponSets = couponSets
// // Load merchants after getting coupon sets
// self.loadMerchants()
// } failureCallback: { [weak self] errorCode in
// print("Failed to load coupon sets: \(errorCode)")
// // No sections added on failure - table will be empty
// }
WarplySDK
.
shared
.
getCouponSetsNew
(
completion
:
{
[
weak
self
]
couponSets
in
guard
let
self
=
self
,
let
couponSets
=
couponSets
else
{
return
}
self
.
couponSets
=
couponSets
self
?
.
createCouponSetsSection
()
self
.
loadCouponFilters
()
},
failureCallback
:
{
errorCode
in
...
...
@@ -315,6 +301,19 @@ import UIKit
)
}
private
func
loadCouponFilters
()
{
WarplySDK
.
shared
.
getCouponFilters
{
[
weak
self
]
couponFilters
in
guard
let
self
=
self
,
let
couponFilters
=
couponFilters
else
{
return
}
self
.
couponFilters
=
couponFilters
self
.
createCouponSetsSection
()
}
failureCallback
:
{
errorCode
in
print
(
"=== getCouponFilters Error:
\(
errorCode
)
"
)
self
.
createCouponSetsSection
()
}
}
// MARK: - Coupons Loading
private
func
loadCoupons
()
{
WarplySDK
.
shared
.
getCouponsUniversal
({
[
weak
self
]
couponsData
in
...
...
@@ -368,89 +367,60 @@ import UIKit
// }
// TODO: DELETE loadMerchants - No matching needed
private
func
loadMerchants
()
{
// Load merchants from WarplySDK (using enhanced getMerchants method)
WarplySDK
.
shared
.
getMerchants
{
[
weak
self
]
merchants
in
guard
let
self
=
self
,
let
merchants
=
merchants
else
{
// If merchants fail to load, still create coupon sets section without filtering
self
?
.
createCouponSetsSection
()
return
}
//
private func loadMerchants() {
//
// Load merchants from WarplySDK (using enhanced getMerchants method)
//
WarplySDK.shared.getMerchants { [weak self] merchants in
//
guard let self = self, let merchants = merchants else {
//
// If merchants fail to load, still create coupon sets section without filtering
//
self?.createCouponSetsSection()
//
return
//
}
self
.
merchants
=
merchants
print
(
"✅ [MyRewardsViewController] Loaded
\(
merchants
.
count
)
merchants"
)
//
self.merchants = merchants
//
print("✅ [MyRewardsViewController] Loaded \(merchants.count) merchants")
// Load merchant categories after merchants success
self
.
loadMerchantCategories
()
//
// Load merchant categories after merchants success
//
self.loadMerchantCategories()
}
failureCallback
:
{
[
weak
self
]
errorCode
in
print
(
"Failed to load merchants:
\(
errorCode
)
"
)
// If merchants fail, still show coupon sets without filtering
self
?
.
createCouponSetsSection
()
}
}
//
} failureCallback: { [weak self] errorCode in
//
print("Failed to load merchants: \(errorCode)")
//
// If merchants fail, still show coupon sets without filtering
//
self?.createCouponSetsSection()
//
}
//
}
// MARK: - Merchant Categories Loading
private
func
loadMerchantCategories
()
{
// Load merchant categories from WarplySDK
WarplySDK
.
shared
.
getMerchantCategories
{
[
weak
self
]
categories
in
guard
let
self
=
self
,
let
categories
=
categories
else
{
// If categories fail to load, still create coupon sets section without filtering
self
?
.
createCouponSetsSection
()
return
}
//
private func loadMerchantCategories() {
//
// Load merchant categories from WarplySDK
//
WarplySDK.shared.getMerchantCategories { [weak self] categories in
//
guard let self = self, let categories = categories else {
//
// If categories fail to load, still create coupon sets section without filtering
//
self?.createCouponSetsSection()
//
return
//
}
self
.
merchantCategories
=
categories
print
(
"✅ [MyRewardsViewController] Loaded
\(
categories
.
count
)
merchant categories"
)
//
self.merchantCategories = categories
//
print("✅ [MyRewardsViewController] Loaded \(categories.count) merchant categories")
// Create coupon sets sections with category-based filtering
self
.
createCouponSetsSection
()
//
// Create coupon sets sections with category-based filtering
//
self.createCouponSetsSection()
}
failureCallback
:
{
[
weak
self
]
errorCode
in
print
(
"Failed to load merchant categories:
\(
errorCode
)
"
)
// If categories fail, still show coupon sets without filtering
self
?
.
createCouponSetsSection
()
}
}
//
} failureCallback: { [weak self] errorCode in
//
print("Failed to load merchant categories: \(errorCode)")
//
// If categories fail, still show coupon sets without filtering
//
self?.createCouponSetsSection()
//
}
//
}
private
func
createCouponSetsSection
()
{
// TODO: no merchant match any more
print
(
"🔍 [MyRewardsViewController] Starting coupon filtering:"
)
print
(
"🔍 [MyRewardsViewController] Starting coupon filtering by new offer_categories:"
)
print
(
" - Coupon Sets:
\(
couponSets
.
count
)
"
)
print
(
" - Merchants:
\(
merchants
.
count
)
"
)
print
(
" - Categories:
\(
merchantCategories
.
count
)
"
)
// Check if we have all required data for filtering
guard
!
couponSets
.
isEmpty
,
!
merchants
.
isEmpty
,
!
merchantCategories
.
isEmpty
else
{
print
(
"⚠️ [MyRewardsViewController] Missing data for filtering - using fallback single section"
)
print
(
" - Coupon Sets Empty:
\(
couponSets
.
isEmpty
)
"
)
print
(
" - Merchants Empty:
\(
merchants
.
isEmpty
)
"
)
print
(
" - Categories Empty:
\(
merchantCategories
.
isEmpty
)
"
)
// Fallback: Create single section with all coupon sets
createSingleCouponSetsSection
()
return
}
// Group coupon sets by merchant category
var
categorySections
:
[
SectionModel
]
=
[]
var
processedCouponSets
:
Set
<
String
>
=
[]
// Track processed coupon sets to avoid duplicates
//
Extract promoted couponsets for "Top offers" section
//
1. Top offers (promoted)
let
promotedCouponSets
=
couponSets
.
filter
{
$0
.
_promoted
}
print
(
"🌟 [MyRewardsViewController] Found
\(
promotedCouponSets
.
count
)
promoted couponsets"
)
// Create "Top offers" section if we have promoted couponsets
if
!
promotedCouponSets
.
isEmpty
{
// Bind merchant data to promoted couponsets
for
couponSet
in
promotedCouponSets
{
if
let
merchant
=
merchants
.
first
(
where
:
{
$0
.
_uuid
==
couponSet
.
_merchant_uuid
})
{
couponSet
.
_merchant
=
merchant
print
(
" 🔗 Bound merchant '
\(
merchant
.
_name
)
' to promoted coupon set '
\(
couponSet
.
_name
)
'"
)
}
}
let
topOffersSection
=
SectionModel
(
sectionType
:
.
myRewardsHorizontalCouponsets
,
title
:
"Top offers"
,
...
...
@@ -461,128 +431,106 @@ import UIKit
print
(
" ✅ Created 'Top offers' section with
\(
promotedCouponSets
.
count
)
items"
)
}
print
(
"🔄 [MyRewardsViewController] Processing categories for filtering..."
)
// 2. Group by parent CouponFilter
var
groupedCouponSets
:
[
String
:
[
CouponSetItemModel
]]
=
[:]
var
unmatchedCouponSets
:
[
CouponSetItemModel
]
=
[]
let
filtersData
=
self
.
couponFilters
?
.
_offer_categories
??
[]
for
category
in
merchantCategories
{
// Find merchants in this category
let
categoryMerchants
=
merchants
.
filter
{
merchant
in
merchant
.
_category_uuid
==
category
.
_uuid
for
couponSet
in
couponSets
{
let
offerCategory
=
couponSet
.
_offer_category
if
offerCategory
.
isEmpty
{
unmatchedCouponSets
.
append
(
couponSet
)
continue
}
print
(
" - Category '
\(
category
.
displayName
)
' has
\(
categoryMerchants
.
count
)
merchants"
)
var
matchedParentName
:
String
?
=
nil
// Find coupon sets from merchants in this category
let
categoryCouponSets
=
couponSets
.
filter
{
couponSet
in
// Skip if already processed (avoid duplicates)
guard
!
processedCouponSets
.
contains
(
couponSet
.
_uuid
)
else
{
return
false
}
for
parentCategory
in
filtersData
{
let
parentName
=
parentCategory
.
_name
.
isEmpty
?
parentCategory
.
_admin_name
:
parentCategory
.
_name
// Check if this coupon set belongs to any merchant in this category
let
belongsToCategory
=
categoryMerchants
.
contains
{
merchant
in
merchant
.
_uuid
==
couponSet
.
_merchant_uuid
// Check parent
if
parentCategory
.
_uuid
==
offerCategory
||
parentCategory
.
_admin_name
==
offerCategory
||
parentCategory
.
_name
==
offerCategory
{
matchedParentName
=
parentName
break
}
if
belongsToCategory
{
processedCouponSets
.
insert
(
couponSet
.
_uuid
)
// BIND MERCHANT DATA: Find and bind the merchant to this coupon set
if
let
merchant
=
categoryMerchants
.
first
(
where
:
{
$0
.
_uuid
==
couponSet
.
_merchant_uuid
})
{
couponSet
.
_merchant
=
merchant
print
(
" 🔗 Bound merchant '
\(
merchant
.
_name
)
' to coupon set '
\(
couponSet
.
_name
)
'"
)
// Check children
if
let
children
=
parentCategory
.
_children
{
for
child
in
children
{
if
child
.
_uuid
==
offerCategory
||
child
.
_admin_name
==
offerCategory
||
child
.
_name
==
offerCategory
{
matchedParentName
=
parentName
break
}
}
return
belongsToCategory
}
print
(
" - Category '
\(
category
.
displayName
)
' has
\(
categoryCouponSets
.
count
)
coupon sets"
)
// Create section if we have coupon sets for this category
if
!
categoryCouponSets
.
isEmpty
{
let
section
=
SectionModel
(
sectionType
:
.
myRewardsHorizontalCouponsets
,
title
:
category
.
displayName
,
items
:
categoryCouponSets
,
itemType
:
.
couponSets
)
categorySections
.
append
(
section
)
print
(
" ✅ Created section for '
\(
category
.
displayName
)
' with
\(
categoryCouponSets
.
count
)
items"
)
if
matchedParentName
!=
nil
{
break
}
}
// COMMENTED OUT: Don't show unmatched couponsets - only show categorized ones
/*
// Handle any remaining unmatched coupon sets
let unmatchedCouponSets = couponSets.filter { couponSet in
!processedCouponSets.contains(couponSet._uuid)
if
let
parentName
=
matchedParentName
{
if
groupedCouponSets
[
parentName
]
==
nil
{
groupedCouponSets
[
parentName
]
=
[]
}
if !unmatchedCouponSets.isEmpty {
print(" ⚠️ Found \(unmatchedCouponSets.count) unmatched coupon sets - adding to 'Άλλες Προσφορές' section")
// BIND MERCHANT DATA for unmatched coupon sets too
for couponSet in unmatchedCouponSets {
if let merchant = merchants.first(where: { $0._uuid == couponSet._merchant_uuid }) {
couponSet._merchant = merchant
print(" 🔗 Bound merchant '\(merchant._name)' to unmatched coupon set '\(couponSet._name)'")
groupedCouponSets
[
parentName
]?
.
append
(
couponSet
)
}
else
{
unmatchedCouponSets
.
append
(
couponSet
)
}
}
let unmatchedSection = SectionModel(
// 3. Create Sections & Sort
var
dynamicSections
:
[
SectionModel
]
=
[]
for
(
parentName
,
items
)
in
groupedCouponSets
{
let
section
=
SectionModel
(
sectionType
:
.
myRewardsHorizontalCouponsets
,
title:
"Άλλες Προσφορές", // "Other Offers"
items:
unmatchedCouponSet
s,
title
:
parentName
,
items
:
item
s
,
itemType
:
.
couponSets
)
categorySections.append(unmatchedS
ection)
dynamicSections
.
append
(
s
ection
)
}
*/
// Sort sections by title for consistent ordering, but keep "Top offers" at the top
categorySections
.
sort
{
section1
,
section2
in
let
title1
=
section1
.
title
??
""
let
title2
=
section2
.
title
??
""
// Put "Top offers" at the beginning
if
title1
==
"Top offers"
{
return
true
}
if
title2
==
"Top offers"
{
return
false
}
// All other sections sorted alphabetically
return
title1
.
localizedCaseInsensitiveCompare
(
title2
)
==
.
orderedAscending
}
let
priorityCategories
=
[
"Αγορές"
,
"Φαγητό και καφές"
]
print
(
"✅ [MyRewardsViewController] Created
\(
categorySections
.
count
)
category sections:"
)
for
section
in
categorySections
{
print
(
" - '
\(
section
.
title
??
"Unknown"
)
':
\(
section
.
itemCount
)
coupon sets"
)
}
dynamicSections
.
sort
{
s1
,
s2
in
let
title1
=
s1
.
title
??
""
let
title2
=
s2
.
title
??
""
// Add category sections to main sections array
self
.
sections
.
append
(
contentsOf
:
categorySections
)
let
idx1
=
priorityCategories
.
firstIndex
(
of
:
title1
)
let
idx2
=
priorityCategories
.
firstIndex
(
of
:
title2
)
// Reload table view with new sections
DispatchQueue
.
main
.
async
{
self
.
tableView
.
reloadData
()
if
let
i1
=
idx1
,
let
i2
=
idx2
{
return
i1
<
i2
}
else
if
idx1
!=
nil
{
return
true
}
else
if
idx2
!=
nil
{
return
false
}
else
{
return
title1
.
localizedCaseInsensitiveCompare
(
title2
)
==
.
orderedAscending
}
}
private
func
createSingleCouponSetsSection
()
{
print
(
"📦 [MyRewardsViewController] Creating single fallback coupon sets section"
)
categorySections
.
append
(
contentsOf
:
dynamicSections
)
//
Fallback: Single section with all coupon set
s
if
!
self
.
c
ouponSets
.
isEmpty
{
let
couponSets
Section
=
SectionModel
(
//
4. Other Offer
s
if
!
unmatchedC
ouponSets
.
isEmpty
{
let
other
Section
=
SectionModel
(
sectionType
:
.
myRewardsHorizontalCouponsets
,
title
:
"
Προσφορές"
,
// "Offers"
items
:
self
.
c
ouponSets
,
title
:
"
Άλλες Προσφορές"
,
items
:
unmatchedC
ouponSets
,
itemType
:
.
couponSets
)
self
.
sections
.
append
(
couponSetsSection
)
print
(
"✅ [MyRewardsViewController] Created fallback section with
\(
self
.
couponSets
.
count
)
coupon sets"
)
}
else
{
print
(
"⚠️ [MyRewardsViewController] No coupon sets available - no section created"
)
categorySections
.
append
(
otherSection
)
print
(
" ✅ Created 'Άλλες Προσφορές' section with
\(
unmatchedCouponSets
.
count
)
items"
)
}
// Add category sections to main sections array
self
.
sections
.
append
(
contentsOf
:
categorySections
)
// Reload table view with new sections
DispatchQueue
.
main
.
async
{
self
.
tableView
.
reloadData
()
}
...
...
Please
register
or
login
to post a comment