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
2025-07-29 15:54:23 +0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
5dcd886dd10ed1ae327e823a326d18e46d9d9c79
5dcd886d
1 parent
224aeb55
MerchantModel, CouponSetItemModel, MyRewardsVC fixes
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
351 additions
and
18 deletions
SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsOfferCollectionViewCell/MyRewardsOfferCollectionViewCell.swift
SwiftWarplyFramework/SwiftWarplyFramework/models/Coupon.swift
SwiftWarplyFramework/SwiftWarplyFramework/models/Merchant.swift
SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift
SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsOfferCollectionViewCell/MyRewardsOfferCollectionViewCell.swift
View file @
5dcd886
...
...
@@ -19,6 +19,43 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
@IBOutlet
weak
var
expirationLabel
:
UILabel
!
@IBOutlet
weak
var
logoImage
:
UIImageView
!
var
postImageURL
:
String
?
{
didSet
{
if
let
url
=
postImageURL
{
self
.
bannerImage
.
image
=
UIImage
()
// UIImage(named: "loading")
UIImage
.
loadImageUsingCacheWithUrlString
(
url
)
{
image
in
// set the image only when we are still displaying the content for the image we finished downloading
if
url
==
self
.
postImageURL
{
self
.
bannerImage
.
image
=
image
}
}
}
else
{
self
.
bannerImage
.
image
=
nil
}
}
}
var
logoImageURL
:
String
?
{
didSet
{
if
let
url
=
logoImageURL
{
self
.
logoImage
.
image
=
UIImage
()
// UIImage(named: "loading")
UIImage
.
loadImageUsingCacheWithUrlString
(
url
)
{
image
in
// set the image only when we are still displaying the content for the image we finished downloading
if
url
==
self
.
logoImageURL
{
self
.
logoImage
.
image
=
image
}
}
}
else
{
self
.
logoImage
.
image
=
nil
}
}
}
public
override
func
awakeFromNib
()
{
super
.
awakeFromNib
()
// Initialization code
...
...
@@ -68,12 +105,7 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
func
configureCell
(
data
:
CouponSetItemModel
)
{
// Use coupon set preview image
let
imageName
=
data
.
_img_preview
if
!
imageName
.
isEmpty
{
bannerImage
.
image
=
UIImage
(
named
:
imageName
,
in
:
Bundle
.
frameworkResourceBundle
,
compatibleWith
:
nil
)
}
else
{
bannerImage
.
image
=
nil
}
self
.
postImageURL
=
data
.
_img_preview
// Default to not favorite for coupon sets
favoriteImage
.
image
=
UIImage
(
named
:
"favorite_empty"
,
in
:
Bundle
.
frameworkResourceBundle
,
compatibleWith
:
nil
)
...
...
@@ -93,7 +125,7 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
case
"plus_one"
:
return
0x007AFF
default
:
return
0x
6C75
7D
return
0x
EE41
7D
}
}()
discountView
.
backgroundColor
=
UIColor
(
rgb
:
discountColor
)
...
...
@@ -106,20 +138,16 @@ public class MyRewardsOfferCollectionViewCell: UICollectionViewCell {
subtitleLabel
.
font
=
UIFont
(
name
:
"PingLCG-Regular"
,
size
:
14
)
subtitleLabel
.
textColor
=
UIColor
(
rgb
:
0x00111B
)
expirationLabel
.
text
=
"έως "
+
data
.
formattedE
xpiration
(
format
:
"dd-MM"
)
expirationLabel
.
text
=
"έως "
+
data
.
formattedE
ndDate
(
format
:
"dd-MM"
)
expirationLabel
.
font
=
UIFont
(
name
:
"PingLCG-Regular"
,
size
:
13
)
expirationLabel
.
textColor
=
UIColor
(
rgb
:
0x00111B
)
// Use first image from img array if available
if
let
imgArray
=
data
.
_img
,
!
imgArray
.
isEmpty
{
let
logoName
=
imgArray
[
0
]
if
!
logoName
.
isEmpty
{
logoImage
.
image
=
UIImage
(
named
:
logoName
,
in
:
Bundle
.
frameworkResourceBundle
,
compatibleWith
:
nil
)
}
else
{
logoImage
.
image
=
nil
}
// Use merchant logo from bound merchant data
if
let
merchant
=
data
.
_merchant
,
!
merchant
.
_img_preview
.
isEmpty
{
// Use merchant's img_preview for logo
self
.
logoImageURL
=
merchant
.
_img_preview
}
else
{
logoImage
.
image
=
nil
self
.
logoImageURL
=
nil
}
}
...
...
SwiftWarplyFramework/SwiftWarplyFramework/models/Coupon.swift
View file @
5dcd886
...
...
@@ -78,6 +78,9 @@ public class CouponSetItemModel {
private
var
third_party_service
:
String
?
private
var
category
:
String
?
// Bound merchant data for performance
private
var
merchant
:
MerchantModel
?
public
init
(
dictionary
:
[
String
:
Any
])
{
// Existing fields
self
.
uuid
=
dictionary
[
"uuid"
]
as?
String
?
??
""
...
...
@@ -236,6 +239,12 @@ public class CouponSetItemModel {
public
var
_third_party_service
:
String
{
get
{
return
self
.
third_party_service
??
""
}
}
public
var
_category
:
String
{
get
{
return
self
.
category
??
""
}
}
// Bound merchant data accessor
public
var
_merchant
:
MerchantModel
?
{
get
{
return
self
.
merchant
}
set
(
newValue
)
{
self
.
merchant
=
newValue
}
}
// Formatted expiration date for display
public
var
_expiration_formatted
:
String
{
guard
let
expiration
=
self
.
expiration
,
!
expiration
.
isEmpty
else
{
...
...
@@ -272,6 +281,46 @@ public class CouponSetItemModel {
return
""
}
/// Format start date with custom format
/// - Parameter format: DateFormatter format string (e.g., "dd/MM/yyyy", "MMM yyyy", "dd-MM")
/// - Returns: Formatted date string or empty string if invalid
public
func
formattedStartDate
(
format
:
String
)
->
String
{
guard
let
startDate
=
self
.
start_date
,
!
startDate
.
isEmpty
else
{
return
""
}
let
inputFormatter
=
DateFormatter
()
inputFormatter
.
dateFormat
=
"yyyy-MM-dd HH:mm:ss"
if
let
date
=
inputFormatter
.
date
(
from
:
startDate
)
{
let
outputFormatter
=
DateFormatter
()
outputFormatter
.
dateFormat
=
format
return
outputFormatter
.
string
(
from
:
date
)
}
return
""
}
/// Format end date with custom format
/// - Parameter format: DateFormatter format string (e.g., "dd/MM/yyyy", "MMM yyyy", "dd-MM")
/// - Returns: Formatted date string or empty string if invalid
public
func
formattedEndDate
(
format
:
String
)
->
String
{
guard
let
endDate
=
self
.
end_date
,
!
endDate
.
isEmpty
else
{
return
""
}
let
inputFormatter
=
DateFormatter
()
inputFormatter
.
dateFormat
=
"yyyy-MM-dd HH:mm:ss"
if
let
date
=
inputFormatter
.
date
(
from
:
endDate
)
{
let
outputFormatter
=
DateFormatter
()
outputFormatter
.
dateFormat
=
format
return
outputFormatter
.
string
(
from
:
date
)
}
return
""
}
}
public
class
RedeemedMerchantDetailsModel
:
Codable
{
...
...
SwiftWarplyFramework/SwiftWarplyFramework/models/Merchant.swift
View file @
5dcd886
...
...
@@ -10,7 +10,8 @@ import Foundation
// MARK: - Merchant Models
public
class
MerchantModel
:
Codable
{
public
class
MerchantModel
{
// Existing fields
private
var
address
:
String
?
private
var
id
:
String
?
private
var
store_id
:
String
?
...
...
@@ -48,7 +49,35 @@ public class MerchantModel: Codable {
private
var
show_map
:
Bool
?
private
var
eshop
:
Bool
?
// NEW FIELDS - Missing from API response
// Image array - multiple merchant images
private
var
img
:
[
String
]?
// Business metadata - stored as JSON string for safety
private
var
merchant_metadata_raw
:
String
?
// Contact details
private
var
telephones_raw
:
String
?
// Business operations
private
var
working_hours
:
String
?
private
var
year_established
:
Int
?
private
var
currency
:
String
?
private
var
bank
:
String
?
// E-commerce fields
private
var
min_order_price
:
Double
?
private
var
min_order_price_takeaway
:
Double
?
private
var
min_price
:
Double
?
private
var
max_price
:
Double
?
// SEO and categorization
private
var
url_name
:
String
?
private
var
tags_raw
:
String
?
// Store as comma-separated string
private
var
product_raw
:
String
?
// Store as JSON string
public
init
()
{
// Existing fields
self
.
address
=
""
self
.
id
=
""
self
.
store_id
=
""
...
...
@@ -85,9 +114,26 @@ public class MerchantModel: Codable {
self
.
hidden
=
false
self
.
show_map
=
false
self
.
eshop
=
false
// New fields - initialize with appropriate defaults
self
.
img
=
[]
self
.
merchant_metadata_raw
=
""
self
.
telephones_raw
=
""
self
.
working_hours
=
""
self
.
year_established
=
nil
self
.
currency
=
""
self
.
bank
=
""
self
.
min_order_price
=
nil
self
.
min_order_price_takeaway
=
nil
self
.
min_price
=
nil
self
.
max_price
=
nil
self
.
url_name
=
""
self
.
tags_raw
=
""
self
.
product_raw
=
""
}
public
init
(
dictionary
:
[
String
:
Any
])
{
// Parse existing fields
self
.
address
=
dictionary
[
"address"
]
as?
String
?
??
""
self
.
id
=
dictionary
[
"id"
]
as?
String
?
??
""
self
.
store_id
=
dictionary
[
"store_id"
]
as?
String
?
??
""
...
...
@@ -123,6 +169,7 @@ public class MerchantModel: Codable {
self
.
default_shown
=
dictionary
[
"default_shown"
]
as?
Bool
?
??
false
self
.
hidden
=
dictionary
[
"hidden"
]
as?
Bool
?
??
false
// Parse extra_fields
if
let
extra_fields
=
dictionary
[
"extra_fields"
]
as?
[
String
:
Any
]
{
self
.
show_map
=
extra_fields
[
"show_map"
]
as?
Bool
?
??
false
self
.
eshop
=
extra_fields
[
"eshop"
]
as?
Bool
?
??
false
...
...
@@ -130,6 +177,72 @@ public class MerchantModel: Codable {
self
.
show_map
=
false
self
.
eshop
=
false
}
// Parse NEW FIELDS - with safe type handling
// Parse img array safely
if
let
imgArray
=
dictionary
[
"img"
]
as?
[
String
]
{
self
.
img
=
imgArray
}
else
{
self
.
img
=
[]
}
// Parse merchant_metadata as JSON string for safety
if
let
metadata
=
dictionary
[
"merchant_metadata"
]
as?
[
String
:
Any
]
{
do
{
let
jsonData
=
try
JSONSerialization
.
data
(
withJSONObject
:
metadata
,
options
:
[])
if
let
jsonString
=
String
(
data
:
jsonData
,
encoding
:
.
utf8
)
{
self
.
merchant_metadata_raw
=
jsonString
}
else
{
self
.
merchant_metadata_raw
=
""
}
}
catch
{
self
.
merchant_metadata_raw
=
""
}
}
else
{
self
.
merchant_metadata_raw
=
""
}
// Parse contact details
self
.
telephones_raw
=
dictionary
[
"telephones_raw"
]
as?
String
?
??
""
// Parse business operations
self
.
working_hours
=
dictionary
[
"working_hours"
]
as?
String
?
??
""
self
.
year_established
=
dictionary
[
"year_established"
]
as?
Int
?
self
.
currency
=
dictionary
[
"currency"
]
as?
String
?
??
""
self
.
bank
=
dictionary
[
"bank"
]
as?
String
?
??
""
// Parse e-commerce fields
self
.
min_order_price
=
dictionary
[
"min_order_price"
]
as?
Double
?
self
.
min_order_price_takeaway
=
dictionary
[
"min_order_price_takeaway"
]
as?
Double
?
self
.
min_price
=
dictionary
[
"min_price"
]
as?
Double
?
self
.
max_price
=
dictionary
[
"max_price"
]
as?
Double
?
// Parse SEO and categorization
self
.
url_name
=
dictionary
[
"url_name"
]
as?
String
?
??
""
// Parse tags array to comma-separated string
if
let
tagsArray
=
dictionary
[
"tags"
]
as?
[
String
]
{
self
.
tags_raw
=
tagsArray
.
joined
(
separator
:
","
)
}
else
{
self
.
tags_raw
=
""
}
// Parse product object as JSON string
if
let
product
=
dictionary
[
"product"
]
as?
[
String
:
Any
]
{
do
{
let
jsonData
=
try
JSONSerialization
.
data
(
withJSONObject
:
product
,
options
:
[])
if
let
jsonString
=
String
(
data
:
jsonData
,
encoding
:
.
utf8
)
{
self
.
product_raw
=
jsonString
}
else
{
self
.
product_raw
=
""
}
}
catch
{
self
.
product_raw
=
""
}
}
else
{
self
.
product_raw
=
""
}
}
public
var
_address
:
String
{
...
...
@@ -311,4 +424,133 @@ public class MerchantModel: Codable {
get
{
return
self
.
eshop
??
false
}
set
(
newValue
)
{
self
.
eshop
=
newValue
}
}
// MARK: - NEW FIELD ACCESSORS
// Image array accessor
public
var
_img
:
[
String
]
{
get
{
return
self
.
img
??
[]
}
set
(
newValue
)
{
self
.
img
=
newValue
}
}
// Business metadata accessor (raw JSON string)
public
var
_merchant_metadata_raw
:
String
{
get
{
return
self
.
merchant_metadata_raw
??
""
}
set
(
newValue
)
{
self
.
merchant_metadata_raw
=
newValue
}
}
// Contact details
public
var
_telephones_raw
:
String
{
get
{
return
self
.
telephones_raw
??
""
}
set
(
newValue
)
{
self
.
telephones_raw
=
newValue
}
}
// Business operations
public
var
_working_hours
:
String
{
get
{
return
self
.
working_hours
??
""
}
set
(
newValue
)
{
self
.
working_hours
=
newValue
}
}
public
var
_year_established
:
Int
?
{
get
{
return
self
.
year_established
}
set
(
newValue
)
{
self
.
year_established
=
newValue
}
}
public
var
_currency
:
String
{
get
{
return
self
.
currency
??
""
}
set
(
newValue
)
{
self
.
currency
=
newValue
}
}
public
var
_bank
:
String
{
get
{
return
self
.
bank
??
""
}
set
(
newValue
)
{
self
.
bank
=
newValue
}
}
// E-commerce fields
public
var
_min_order_price
:
Double
?
{
get
{
return
self
.
min_order_price
}
set
(
newValue
)
{
self
.
min_order_price
=
newValue
}
}
public
var
_min_order_price_takeaway
:
Double
?
{
get
{
return
self
.
min_order_price_takeaway
}
set
(
newValue
)
{
self
.
min_order_price_takeaway
=
newValue
}
}
public
var
_min_price
:
Double
?
{
get
{
return
self
.
min_price
}
set
(
newValue
)
{
self
.
min_price
=
newValue
}
}
public
var
_max_price
:
Double
?
{
get
{
return
self
.
max_price
}
set
(
newValue
)
{
self
.
max_price
=
newValue
}
}
// SEO and categorization
public
var
_url_name
:
String
{
get
{
return
self
.
url_name
??
""
}
set
(
newValue
)
{
self
.
url_name
=
newValue
}
}
public
var
_tags_raw
:
String
{
get
{
return
self
.
tags_raw
??
""
}
set
(
newValue
)
{
self
.
tags_raw
=
newValue
}
}
public
var
_product_raw
:
String
{
get
{
return
self
.
product_raw
??
""
}
set
(
newValue
)
{
self
.
product_raw
=
newValue
}
}
// MARK: - COMPUTED PROPERTIES FOR COMPLEX DATA
// Computed property to get metadata as dictionary
public
var
merchantMetadata
:
[
String
:
Any
]?
{
guard
let
rawString
=
merchant_metadata_raw
,
!
rawString
.
isEmpty
,
let
data
=
rawString
.
data
(
using
:
.
utf8
),
let
dict
=
try
?
JSONSerialization
.
jsonObject
(
with
:
data
)
as?
[
String
:
Any
]
else
{
return
nil
}
return
dict
}
// Computed property to get tags as array
public
var
tagsArray
:
[
String
]
{
guard
let
rawTags
=
tags_raw
,
!
rawTags
.
isEmpty
else
{
return
[]
}
return
rawTags
.
components
(
separatedBy
:
","
)
.
filter
{
!
$0
.
isEmpty
}
}
// Computed property to get product info as dictionary
public
var
productInfo
:
[
String
:
Any
]?
{
guard
let
rawString
=
product_raw
,
!
rawString
.
isEmpty
,
let
data
=
rawString
.
data
(
using
:
.
utf8
),
let
dict
=
try
?
JSONSerialization
.
jsonObject
(
with
:
data
)
as?
[
String
:
Any
]
else
{
return
nil
}
return
dict
}
// Computed property to check if merchant has progress completed
public
var
hasProgressCompleted
:
Bool
{
return
merchantMetadata
?[
"progressCompleted"
]
as?
Bool
??
false
}
// Computed property to get main image URL (first in img array or fallback to img_preview)
public
var
mainImageURL
:
String
{
if
let
imgArray
=
img
,
!
imgArray
.
isEmpty
{
return
imgArray
.
first
??
_img_preview
}
return
_img_preview
}
// Computed property to get pin image URL (second in img array if available)
public
var
pinImageURL
:
String
?
{
if
let
imgArray
=
img
,
imgArray
.
count
>
1
{
return
imgArray
[
1
]
}
return
nil
}
}
...
...
SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift
View file @
5dcd886
...
...
@@ -256,6 +256,12 @@ import UIKit
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
)
'"
)
}
}
return
belongsToCategory
...
...
@@ -285,6 +291,14 @@ import UIKit
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
)
'"
)
}
}
let
unmatchedSection
=
SectionModel
(
sectionType
:
.
myRewardsHorizontalCouponsets
,
title
:
"Άλλες Προσφορές"
,
// "Other Offers"
...
...
Please
register
or
login
to post a comment