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-21 12:55:59 +0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
06b95440c7a88cacc08a49a73954b2d939b693ef
06b95440
1 parent
9e2e0442
dynamic campaigns at MyRewardsVC
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
247 additions
and
88 deletions
NETWORK_TESTING_AUTHORIZATION.md
SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsBannerOfferCollectionViewCell/MyRewardsBannerOfferCollectionViewCell.swift
SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsBannerOffersScrollTableViewCell/MyRewardsBannerOffersScrollTableViewCell.swift
SwiftWarplyFramework/SwiftWarplyFramework/models/Campaign.swift
SwiftWarplyFramework/SwiftWarplyFramework/models/SectionModel.swift
SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift
NETWORK_TESTING_AUTHORIZATION.md
View file @
06b9544
This diff is collapsed. Click to expand it.
SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsBannerOfferCollectionViewCell/MyRewardsBannerOfferCollectionViewCell.swift
View file @
06b9544
...
...
@@ -16,7 +16,14 @@ public class MyRewardsBannerOfferCollectionViewCell: UICollectionViewCell {
// Initialization code
}
func
configureCell
(
data
:
OfferModel
)
{
backgroundImage
.
image
=
UIImage
(
named
:
data
.
bannerImage
,
in
:
Bundle
.
frameworkResourceBundle
,
compatibleWith
:
nil
)
func
configureCell
(
data
:
CampaignItemModel
)
{
// Use campaign's banner image - no hardcoded defaults
let
imageName
=
data
.
_banner_img_mobile
??
""
if
!
imageName
.
isEmpty
{
backgroundImage
.
image
=
UIImage
(
named
:
imageName
,
in
:
Bundle
.
frameworkResourceBundle
,
compatibleWith
:
nil
)
}
else
{
backgroundImage
.
image
=
nil
}
}
}
...
...
SwiftWarplyFramework/SwiftWarplyFramework/cells/MyRewardsBannerOffersScrollTableViewCell/MyRewardsBannerOffersScrollTableViewCell.swift
View file @
06b9544
...
...
@@ -93,8 +93,8 @@ public class MyRewardsBannerOffersScrollTableViewCell: UITableViewCell {
func
configureCell
(
data
:
SectionModel
?)
{
self
.
data
=
data
// Configure page control
let
numberOfPages
=
self
.
data
?
.
offers
.
c
ount
??
0
// Configure page control
based on new SectionModel structure
let
numberOfPages
=
self
.
data
?
.
itemC
ount
??
0
pageControl
.
numberOfPages
=
numberOfPages
pageControl
.
currentPage
=
0
...
...
@@ -115,24 +115,36 @@ extension MyRewardsBannerOffersScrollTableViewCell: UICollectionViewDataSource,
}
public
func
collectionView
(
_
collectionView
:
UICollectionView
,
numberOfItemsInSection
section
:
Int
)
->
Int
{
return
self
.
data
?
.
offers
.
c
ount
??
0
return
self
.
data
?
.
itemC
ount
??
0
}
public
func
collectionView
(
_
collectionView
:
UICollectionView
,
cellForItemAt
indexPath
:
IndexPath
)
->
UICollectionViewCell
{
let
cell
=
collectionView
.
dequeueReusableCell
(
withReuseIdentifier
:
"MyRewardsBannerOfferCollectionViewCell"
,
for
:
indexPath
)
as!
MyRewardsBannerOfferCollectionViewCell
// cell.configureCell(offer: self.data?.offers[indexPath.row])
if
let
offer
=
self
.
data
?
.
offers
[
indexPath
.
row
]
{
cell
.
configureCell
(
data
:
offer
)
// Handle different item types
guard
let
data
=
self
.
data
,
let
itemType
=
data
.
itemType
,
let
items
=
data
.
items
,
indexPath
.
row
<
items
.
count
else
{
return
cell
}
switch
itemType
{
case
.
campaigns
:
if
let
campaign
=
items
[
indexPath
.
row
]
as?
CampaignItemModel
{
cell
.
configureCell
(
data
:
campaign
)
}
default
:
// Handle other types if needed in the future
break
}
return
cell
;
return
cell
}
public
func
collectionView
(
_
collectionView
:
UICollectionView
,
didSelectItemAt
indexPath
:
IndexPath
)
{
// Get the selected offer
if
let
offer
=
self
.
data
?
.
offers
[
indexPath
.
row
]
{
// Call the delegate method to notify the parent
delegate
?
.
didSelectBannerOffer
(
indexPath
.
row
)
}
// Call the delegate method to notify the parent
delegate
?
.
didSelectBannerOffer
(
indexPath
.
row
)
}
// MARK: - UICollectionViewDelegateFlowLayout
...
...
SwiftWarplyFramework/SwiftWarplyFramework/models/Campaign.swift
View file @
06b9544
...
...
@@ -42,6 +42,17 @@ public class CampaignItemModel: Codable {
private
var
show_expiration
:
String
?
private
var
coupon_img
:
String
?
// New fields from getCampaigns response
private
var
communication_name
:
String
?
private
var
category
:
String
?
private
var
delivery_method
:
String
?
private
var
display_type
:
String
?
private
var
audience
:
String
?
private
var
description
:
String
?
private
var
workflow_settings
:
[
String
:
Any
]?
private
var
campaign_url
:
String
?
// from extra_fields
private
var
banner_img_mobile
:
String
?
// from extra_fields
public
init
()
{
self
.
index_url
=
""
self
.
logo_url
=
""
...
...
@@ -73,6 +84,17 @@ public class CampaignItemModel: Codable {
self
.
filter
=
""
self
.
show_expiration
=
"false"
self
.
coupon_img
=
""
// Initialize new fields
self
.
communication_name
=
""
self
.
category
=
""
self
.
delivery_method
=
""
self
.
display_type
=
""
self
.
audience
=
""
self
.
description
=
""
self
.
workflow_settings
=
nil
self
.
campaign_url
=
""
self
.
banner_img_mobile
=
""
}
public
init
(
dictionary
:
[
String
:
Any
])
{
...
...
@@ -90,6 +112,15 @@ public class CampaignItemModel: Codable {
self
.
ccms
=
nil
self
.
coupon_availability
=
nil
// Parse new fields from getCampaigns response
self
.
communication_name
=
dictionary
[
"communication_name"
]
as?
String
?
??
""
self
.
category
=
dictionary
[
"category"
]
as?
String
?
??
""
self
.
delivery_method
=
dictionary
[
"delivery_method"
]
as?
String
?
??
""
self
.
display_type
=
dictionary
[
"display_type"
]
as?
String
?
??
""
self
.
audience
=
dictionary
[
"audience"
]
as?
String
?
??
""
self
.
description
=
dictionary
[
"description"
]
as?
String
?
??
""
self
.
workflow_settings
=
dictionary
[
"workflow_settings"
]
as?
[
String
:
Any
]
let
startDateString
=
dictionary
[
"start_date"
]
as?
String
?
??
""
let
dateFormatter
=
DateFormatter
()
dateFormatter
.
dateFormat
=
"yyyy-MM-dd HH:mm:ss"
...
...
@@ -127,6 +158,10 @@ public class CampaignItemModel: Codable {
self
.
filter
=
extra_fields
[
"filter"
]
as?
String
??
""
self
.
show_expiration
=
extra_fields
[
"show_expiration"
]
as?
String
?
??
"false"
self
.
coupon_img
=
extra_fields
[
"coupon_img"
]
as?
String
?
??
""
// Parse new extra_fields
self
.
campaign_url
=
extra_fields
[
"campaign_url"
]
as?
String
?
??
""
self
.
banner_img_mobile
=
extra_fields
[
"banner_img_mobile"
]
as?
String
?
??
""
}
else
{
self
.
subcategory
=
""
self
.
loyaltyCampaignId
=
""
...
...
@@ -141,6 +176,10 @@ public class CampaignItemModel: Codable {
self
.
filter
=
""
self
.
show_expiration
=
"false"
self
.
coupon_img
=
""
// Initialize new extra_fields when no extra_fields exist
self
.
campaign_url
=
""
self
.
banner_img_mobile
=
""
}
// campaign_type_settings
...
...
@@ -260,6 +299,53 @@ public class CampaignItemModel: Codable {
get
{
return
self
.
coupon_img
}
set
(
newValue
)
{
self
.
coupon_img
=
newValue
}
}
// MARK: - New field accessors
public
var
_communication_name
:
String
?
{
get
{
return
self
.
communication_name
}
set
(
newValue
)
{
self
.
communication_name
=
newValue
}
}
public
var
_category
:
String
?
{
get
{
return
self
.
category
}
set
(
newValue
)
{
self
.
category
=
newValue
}
}
public
var
_delivery_method
:
String
?
{
get
{
return
self
.
delivery_method
}
set
(
newValue
)
{
self
.
delivery_method
=
newValue
}
}
public
var
_display_type
:
String
?
{
get
{
return
self
.
display_type
}
set
(
newValue
)
{
self
.
display_type
=
newValue
}
}
public
var
_audience
:
String
?
{
get
{
return
self
.
audience
}
set
(
newValue
)
{
self
.
audience
=
newValue
}
}
public
var
_description
:
String
?
{
get
{
return
self
.
description
}
set
(
newValue
)
{
self
.
description
=
newValue
}
}
public
var
_workflow_settings
:
[
String
:
Any
]?
{
get
{
return
self
.
workflow_settings
}
set
(
newValue
)
{
self
.
workflow_settings
=
newValue
}
}
public
var
_campaign_url
:
String
?
{
get
{
return
self
.
campaign_url
}
set
(
newValue
)
{
self
.
campaign_url
=
newValue
}
}
public
var
_banner_img_mobile
:
String
?
{
get
{
return
self
.
banner_img_mobile
}
set
(
newValue
)
{
self
.
banner_img_mobile
=
newValue
}
}
}
// MARK: - Loyalty Contextual Offer Model
...
...
SwiftWarplyFramework/SwiftWarplyFramework/models/SectionModel.swift
View file @
06b9544
...
...
@@ -8,8 +8,61 @@
import
Foundation
// MARK: - Section Types
enum
SectionType
{
case
myRewardsBannerOffers
// MyRewardsBannerOffersScrollTableViewCell
case
myRewardsHorizontalCouponsets
// MyRewardsOffersScrollTableViewCell
case
profileHeader
// ProfileHeaderTableViewCell (no items)
case
profileQuestionnaire
// ProfileQuestionnaireTableViewCell (no items)
case
profileCouponFilters
// ProfileCouponFiltersTableViewCell (no items)
case
staticContent
// Any cell that displays static content
}
enum
ItemType
{
case
campaigns
// [CampaignItemModel]
case
couponSets
// [CouponSetItemModel]
case
coupons
// [CouponItemModel]
case
filters
// [CouponFilterModel]
case
none
// For sections with no items
}
// MARK: - SectionModel
struct
SectionModel
{
let
title
:
String
let
count
:
Int
let
offers
:
[
OfferModel
]
let
sectionType
:
SectionType
// MANDATORY - defines which cell to use
let
title
:
String
?
// OPTIONAL - section title
let
items
:
[
Any
]?
// OPTIONAL - array of items (nil for sections with no items)
let
itemType
:
ItemType
?
// OPTIONAL - type of items in the array
let
count
:
Int
?
// OPTIONAL - explicit count (computed from items.count if nil)
let
metadata
:
[
String
:
Any
]?
// OPTIONAL - additional section-specific data
}
// MARK: - SectionModel Extensions
extension
SectionModel
{
// Convenience initializer for sections with items
init
(
sectionType
:
SectionType
,
title
:
String
?
=
nil
,
items
:
[
Any
],
itemType
:
ItemType
,
metadata
:
[
String
:
Any
]?
=
nil
)
{
self
.
sectionType
=
sectionType
self
.
title
=
title
self
.
items
=
items
self
.
itemType
=
itemType
self
.
count
=
items
.
count
self
.
metadata
=
metadata
}
// Convenience initializer for sections without items
init
(
sectionType
:
SectionType
,
title
:
String
?
=
nil
,
count
:
Int
=
1
,
metadata
:
[
String
:
Any
]?
=
nil
)
{
self
.
sectionType
=
sectionType
self
.
title
=
title
self
.
items
=
nil
self
.
itemType
=
.
none
self
.
count
=
count
self
.
metadata
=
metadata
}
// Computed property for safe count access
var
itemCount
:
Int
{
return
count
??
items
?
.
count
??
0
}
}
...
...
SwiftWarplyFramework/SwiftWarplyFramework/screens/MyRewardsViewController/MyRewardsViewController.swift
View file @
06b9544
...
...
@@ -293,15 +293,11 @@ import UIKit
"https://warply.s3.amazonaws.com/dei/campaigns/EnergySaverContest_dev/index.html"
]
var
bannerOffersSection
:
SectionModel
?
var
topOffersSection
:
SectionModel
?
var
favoriteOffersSection
:
SectionModel
?
var
sustainableOffersSection
:
SectionModel
?
var
familyOffersSection
:
SectionModel
?
var
foodOffersSection
:
SectionModel
?
var
escapeOffersSection
:
SectionModel
?
var
childOffersSection
:
SectionModel
?
var
shoppingOffersSection
:
SectionModel
?
// Dynamic sections array - populated by API calls
var
sections
:
[
SectionModel
]
=
[]
// Campaign data for banners
var
bannerCampaigns
:
[
CampaignItemModel
]
=
[]
public
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
...
...
@@ -319,7 +315,8 @@ import UIKit
tableView
.
estimatedRowHeight
=
200
tableView
.
rowHeight
=
UITableView
.
automaticDimension
initializeSections
()
// Start with empty sections - will be populated dynamically by API calls
loadCampaigns
()
}
// NEW: Safe XIB registration method
...
...
@@ -432,22 +429,53 @@ import UIKit
self
.
tableView
.
reloadData
()
}
// MARK: - Campaign Loading
private
func
loadCampaigns
()
{
// Load campaigns from WarplySDK
WarplySDK
.
shared
.
getCampaigns
{
[
weak
self
]
campaigns
in
guard
let
self
=
self
,
let
campaigns
=
campaigns
else
{
return
}
// Filter campaigns for banner display (contest campaigns)
self
.
bannerCampaigns
=
campaigns
.
filter
{
campaign
in
// Filter by category "contest" or campaign_type "contest"
return
campaign
.
_category
==
"contest"
||
campaign
.
_campaign_type
==
"contest"
}
// Create banner section with real campaign data
if
!
self
.
bannerCampaigns
.
isEmpty
{
let
bannerSection
=
SectionModel
(
sectionType
:
.
myRewardsBannerOffers
,
title
:
"Διαγωνισμός"
,
items
:
self
.
bannerCampaigns
,
itemType
:
.
campaigns
)
self
.
sections
.
append
(
bannerSection
)
}
// Reload table view with new sections
DispatchQueue
.
main
.
async
{
self
.
tableView
.
reloadData
()
}
}
failureCallback
:
{
[
weak
self
]
errorCode
in
print
(
"Failed to load campaigns:
\(
errorCode
)
"
)
// No sections added on failure - table will be empty
}
}
private
func
openCampaignViewController
(
with
index
:
Int
)
{
// let storyboard = UIStoryboard(name: "Main", bundle: Bundle.frameworkBundle)
// if let vc = storyboard.instantiateViewController(withIdentifier: "CampaignViewController") as? SwiftWarplyFramework.CampaignViewController {
// // vc.campaignUrl = "https://warply.s3.amazonaws.com/dei/campaigns/DehEasterContest_stage/index.html"
// vc.campaignUrl = contestUrls[index]
// vc.showHeader = false
// self.navigationController?.pushViewController(vc, animated: true)
// // self.present(vc, animated: true)
// }
let
vc
=
SwiftWarplyFramework
.
CampaignViewController
(
nibName
:
"CampaignViewController"
,
bundle
:
Bundle
.
frameworkBundle
)
// vc.campaignUrl = "https://warply.s3.amazonaws.com/dei/campaigns/DehEasterContest_stage/index.html"
vc
.
campaignUrl
=
contestUrls
[
index
]
vc
.
showHeader
=
false
// Use real campaign URL if available, otherwise fallback to static URLs
if
index
<
bannerCampaigns
.
count
{
// Use campaign URL from real campaign data
let
campaign
=
bannerCampaigns
[
index
]
vc
.
campaignUrl
=
campaign
.
_campaign_url
??
campaign
.
index_url
??
contestUrls
[
min
(
index
,
contestUrls
.
count
-
1
)]
}
else
{
// Fallback to static contest URLs
vc
.
campaignUrl
=
contestUrls
[
min
(
index
,
contestUrls
.
count
-
1
)]
}
vc
.
showHeader
=
false
self
.
navigationController
?
.
pushViewController
(
vc
,
animated
:
true
)
}
...
...
@@ -469,7 +497,7 @@ import UIKit
extension
MyRewardsViewController
:
UITableViewDelegate
,
UITableViewDataSource
{
public
func
numberOfSections
(
in
tableView
:
UITableView
)
->
Int
{
return
9
return
sections
.
count
}
public
func
tableView
(
_
tableView
:
UITableView
,
numberOfRowsInSection
section
:
Int
)
->
Int
{
...
...
@@ -489,7 +517,6 @@ extension MyRewardsViewController: UITableViewDelegate, UITableViewDataSource{
}
public
func
tableView
(
_
tableView
:
UITableView
,
heightForFooterInSection
section
:
Int
)
->
CGFloat
{
// return CGFloat.leastNormalMagnitude
return
0.0
}
...
...
@@ -498,62 +525,36 @@ extension MyRewardsViewController: UITableViewDelegate, UITableViewDataSource{
}
public
func
tableView
(
_
tableView
:
UITableView
,
cellForRowAt
indexPath
:
IndexPath
)
->
UITableViewCell
{
if
(
indexPath
.
section
==
0
)
{
guard
indexPath
.
section
<
sections
.
count
else
{
return
UITableViewCell
()
// Return empty cell if section doesn't exist
}
let
sectionModel
=
sections
[
indexPath
.
section
]
switch
sectionModel
.
sectionType
{
case
.
myRewardsBannerOffers
:
let
cell
=
tableView
.
dequeueReusableCell
(
withIdentifier
:
"MyRewardsBannerOffersScrollTableViewCell"
,
for
:
indexPath
)
as!
MyRewardsBannerOffersScrollTableViewCell
cell
.
delegate
=
self
// Set the banner offers delegate
cell
.
configureCell
(
data
:
self
.
bannerOffersSection
)
// cell.parent = self
cell
.
delegate
=
self
cell
.
configureCell
(
data
:
sectionModel
)
return
cell
}
else
{
case
.
myRewardsHorizontalCouponsets
:
let
cell
=
tableView
.
dequeueReusableCell
(
withIdentifier
:
"MyRewardsOffersScrollTableViewCell"
,
for
:
indexPath
)
as!
MyRewardsOffersScrollTableViewCell
cell
.
delegate
=
self
cell
.
configureCell
(
data
:
sectionModel
)
return
cell
cell
.
delegate
=
self
// Set the offers delegate
if
(
indexPath
.
section
==
1
)
{
cell
.
configureCell
(
data
:
self
.
topOffersSection
)
}
else
if
(
indexPath
.
section
==
2
)
{
cell
.
configureCell
(
data
:
self
.
favoriteOffersSection
)
}
else
if
(
indexPath
.
section
==
3
)
{
cell
.
configureCell
(
data
:
self
.
sustainableOffersSection
)
}
else
if
(
indexPath
.
section
==
4
)
{
cell
.
configureCell
(
data
:
self
.
familyOffersSection
)
}
else
if
(
indexPath
.
section
==
5
)
{
cell
.
configureCell
(
data
:
self
.
foodOffersSection
)
}
else
if
(
indexPath
.
section
==
6
)
{
cell
.
configureCell
(
data
:
self
.
escapeOffersSection
)
}
else
if
(
indexPath
.
section
==
7
)
{
cell
.
configureCell
(
data
:
self
.
childOffersSection
)
}
else
{
cell
.
configureCell
(
data
:
self
.
shoppingOffersSection
)
}
case
.
profileHeader
,
.
profileQuestionnaire
,
.
profileCouponFilters
,
.
staticContent
:
// These will be implemented later when needed
let
cell
=
UITableViewCell
()
cell
.
textLabel
?
.
text
=
sectionModel
.
title
??
"Section"
return
cell
}
}
public
func
tableView
(
_
tableView
:
UITableView
,
didSelectRowAt
indexPath
:
IndexPath
)
{
if
(
indexPath
.
section
==
0
)
{
// Do nothing - Each button is handled differently
}
else
if
(
indexPath
.
section
==
1
)
{
// Do nothing
}
else
if
(
indexPath
.
section
==
2
)
{
// Do nothing
}
else
if
(
indexPath
.
section
==
3
)
{
// Do nothing
}
else
if
(
indexPath
.
section
==
4
)
{
// Do nothing
}
else
if
(
indexPath
.
section
==
5
)
{
// Do nothing
}
else
if
(
indexPath
.
section
==
6
)
{
// Do nothing
}
else
if
(
indexPath
.
section
==
7
)
{
// Do nothing
}
else
{
// Do nothing
}
// Handle selection if needed - currently no action required
tableView
.
deselectRow
(
at
:
indexPath
,
animated
:
true
)
}
}
...
...
Please
register
or
login
to post a comment