NeoDay iOS SDK
The NeoDay SDK provides a convenient way of creating the screens for components like campaigns and coupons that are set up in your NeoDay backoffice.
Setting up NeoDaySDK xcframework file
Minimum iOS version: 12.0
Inside your Xcode project select the App target and go to the General tab.
Within the Frameworks, Libraries, and Embedded Content section either drag the NeoDaySDK.xcframework file or tap the + button in order to import the NeoDaySDK.xcframework
On the Build Phases tab make sure the NeoDaySDK.xcframework is added to the sections "Link Binary With Libraries" and "Embed Frameworks".
Add the "import NeoDaySDK" in the files where you want to use the NeoDay SDK.
Implementing the NeoDay SDK
sdk version: 1.2.0
import NeoDay
func initializeSDK() {
let config = NeoDaySDKConfig(
preferredDateFormat: "dd-MM-YYYY",
gatewayConfig: GatewayConfig(
gateway: URL(string: "https://gateway.url")!,
clientWebURL: URL(string: "https://web.client.url")!,
xSiteToken: "x-site-token",
clientID: "client-id",
siteStyleID: "site-style-id",
userSettingsID: "user-settings-id"
),
campaignConfig: CampaignConfig(
placeholderImage: UIImage(named: "campaign-placeholder")!,
transactionsHeaderImage: UIImage(named: "header")!,
buttons: .init(
backButton: nil,
closeButton: nil,
deleteButton: nil
),
scannerConfig: .init(
scanTitle: "scanTitle",
scanDescription: "scanDescription",
manualTitle: "manualTitle",
manualDescription: "manualDescription"
)
),
couponConfig: CouponConfig(
placeholderImage: UIImage(named: "coupon-placeholder")!,
noDataImage: nil
)
)
sdk = NeoDaySDK(config: config)
sdk.deepLinkDelegate = self
sdk.dataLoadingDelegate = self
sdk.needToAuthenticateDelegate = self
let customerAuthenticationEndpoint = CustomerAuthenticationEndpoint(
url: URL(string: "https://example.url")!,
headers: nil,
parameters: nil
)
sdk.initialize(with: customerAuthenticationEndpoint) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success:
// SDK is initialized
case let .failure(error):
// SDK is not initialized, handle the error
}
}
}
sdk version: 1.3.0
import NeoDay
func initializeSDK() {
let config = NeoDaySDKConfig(
preferredDateFormat: "dd-MM-YYYY",
gatewayConfig: GatewayConfig(
gateway: URL(string: "https://gateway.url")!,
clientWebURL: URL(string: "https://web.client.url")!,
xSiteToken: "x-site-token",
clientID: "client-id",
siteStyleID: "site-style-id"
),
campaignConfig: CampaignConfig(
placeholderImage: UIImage(named: "campaign-placeholder")!,
transactionsHeaderImage: UIImage(named: "header")!,
buttonFactory: { buttonType, siteStyle in },
scannerConfig: .init(
scanTitle: "scanTitle",
scanDescription: "scanDescription",
manualTitle: "manualTitle",
manualDescription: "manualDescription"
)
),
couponConfig: CouponConfig(
placeholderImage: UIImage(named: "coupon-placeholder")!,
noDataImage: nil
),
loyaltyCardConfig: .init(
avatarPlaceholder: UIImage(named: "loyalty-avatar-placeholder")!
)
)
sdk = NeoDaySDK(config: config)
sdk.deepLinkDelegate = self
sdk.dataLoadingDelegate = self
sdk.needToAuthenticateDelegate = self
sdk.analyticsTrackingDelegate = self
let customerAuthenticationEndpoint = CustomerAuthenticationEndpoint(
url: URL(string: "https://example.url")!,
headers: nil,
parameters: nil
)
sdk.authenticateUser(endPoint: customerAuthenticationEndpoint) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success:
// SDK is initialized
case let .failure(error):
// SDK is not initialized, handle the error
}
}
}
::
sdk version: 1.3.1
import NeoDay
func initializeSDK() {
let config = NeoDaySDKConfig(
preferredLocale: NeoDayLocale(languageCode: "en", regionCode: "US"),
preferredDateFormat: "dd-MM-YYYY",
gatewayConfig: GatewayConfig(
gateway: URL(string: "https://gateway.url")!,
clientWebURL: URL(string: "https://web.client.url")!,
xSiteToken: "x-site-token",
clientID: "client-id",
siteStyleID: "site-style-id"
),
campaignConfig: CampaignConfig(
placeholderImage: UIImage(named: "campaign-placeholder")!,
transactionsHeaderImage: UIImage(named: "header")!,
buttonFactory: { buttonType, siteStyle in },
scannerConfig: .init(
scanTitle: "scanTitle",
scanDescription: "scanDescription",
manualTitle: "manualTitle",
manualDescription: "manualDescription"
)
),
couponConfig: CouponConfig(
placeholderImage: UIImage(named: "coupon-placeholder")!,
noDataImage: nil
),
loyaltyCardConfig: .init(
avatarPlaceholder: UIImage(named: "loyalty-avatar-placeholder")!
)
)
// ...
}
Requesting a Screen from the NeoDaySDK
The NeoDaySDK factory methods are executed asynchronously in order to fetch the data and return a UIViewController.
sdk.makeCampaignsOverview(filter: .showOnly([.challenge, .gamification])) { [weak self] result in
guard let self = self else {
return
}
switch result {
case let .success(viewController):
// Display the viewController
case let .failure(error):
// Handle the error
}
}
sdk.makeCouponsOverview { [weak self] result in
guard let self = self else {
return
}
switch result {
case let .success(viewController):
// Display the viewController
case let .failure(error):
// Handle the error
}
}
sdk.makeRewardShop { [weak self] result in
guard let self = self else {
return
}
switch result {
case let .success(viewController):
// Display the viewController
case let .failure(error):
// Handle the error
}
}
Directly requesting a Campaign detail screen
sdk.makeCampaignDetail(campaignID: "campaign-id", campaignInstanceID: "campaign-instance-id") { [weak self] result in
guard let self = self else {
return
}
switch result {
case let .success(viewController):
// Display the viewController
case let .failure(error):
// Handle the error
}
}
Directly requesting a Coupon detail screen
sdk.makeCouponDetail(couponSlipID: "coupon-slip-id") { [weak self] result in
guard let self = self else {
return
}
switch result {
case let .success(viewController):
// Display the viewController
case let .failure(error):
// Handle the error
}
}
Handling DeepLink views with NeoDayDeepLinkDelegate
Sometimes the SDK needs to display a screen that is part of a user flow, for example when a user taps one of the campaigns on the campaign overview screen the SDK will invoke the NeoDayDeepLinkDelegate that returns the campaign detail screen that needs to be shown.
It is important to set this delegate in order to go from an Overview screen in to a Detail screen.
import NeoDay
class ExampleViewController: UIViewController {
func deepLinkExample() {
...
// Assuming the sdk is already created.
sdk.deepLinkDelegate = self
...
}
}
extension ExampleViewController: NeoDayDeepLinkDelegate {
func handleDeepLink(_ deepLink: DeepLink, factoryMethod: (@escaping (Result<UIViewController, Error>) -> Void) -> Void?) {
// You can try to first determine based on the deepLink.identifier if you want to try to make the viewController.
switch deepLink.identifier {
case .campaignDetail:
factoryMethod { result in
guard let viewController = try? result.get() else {
return
}
// Display the viewController
}
case .gamificationCampaign:
factoryMethod { result in
guard let viewController = try? result.get() else {
return
}
// Display the viewController
}
case .couponDetail:
factoryMethod { result in
guard let viewController = try? result.get() else {
return
}
// Display the viewController
}
default:
break
}
}
}
How to handle re-authentication when the AuthenticationToken can no longer be refreshed
When the SDK can no longer refresh the token it will invoke the NeedToAuthenticateDelegate so your app can reinitialize the SDK.
It's important to set this delegate when using the NeoDay SDK.
import NeoDay
class ExampleViewController: UIViewController {
func authenticationExample() {
...
// Assuming the sdk is already created.
sdk.needToAuthenticateDelegate = self
...
}
}
sdk version: 1.2.0
extension ExampleViewController: NeedToAuthenticateDelegate {
func authenticationRequired() {
...
// Assuming the customerAuthenticationEndpoint is already defined
sdk.initialize(with: customerAuthenticationEndpoint) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success:
// SDK is initialized
case let .failure(error):
// SDK is not initialized, handle the error
}
}
}
}
sdk version: 1.4.2
After the AuthenticationToken has been refreshed by the NeoDay SDK, the new token is returned by this function. It can be stored and used with the authenticateUser(token: completion:) if needed. When using the authenticateUser(endPoint: completion) to authenticate with NeoDay, it's advised to use the authenticationRequired function of the NeedToAuthenticateDelegate
extension ExampleViewController: NeedToAuthenticateDelegate {
func authenticationRefreshed(token: AuthenticationToken) {
...
}
}
How to handle when the SDK is processing data
When the SDK is fetching from or submitting data to the NeodDay Gateway, it will invoke the SDKDataLoadingDelegate to notify when data is being loaded or not. That way you know when to show or hide a loading indicator while data is being processed.
It's important to set this delegate when using the NeoDay SDK.
sdk version: 1.2.0, 1.3.0
import NeoDay
class ExampleViewController: UIViewController {
func dataLoadingExample() {
...
// Assuming the sdk is already created.
sdk.dataLoadingDelegate = self
...
}
}
extension ExampleViewController: SDKDataLoadingDelegate {
func startedLoadingData() {
// Show a loading indicator
}
func finishedLoadingData() {
// Hide the loading indicator
}
}
sdk version: 1.2.0
/// Configures the NeoDay SDK in order to communicate with the Backoffice
///
/// - parameter gateway: The URL used to communicate with the backend
/// - parameter clientWebURL: The URL of the NeoDay web client
/// - parameter xSiteToken: Used for identification
/// - parameter clientID: Used to identify the client
/// - parameter: siteStyleID: The id used to get SiteStyle that is configured inside the Backoffice
struct GatewayConfig {
let gateway: URL
let clientWebURL: URL
let xSiteToken: String
let clientID: String
let siteStyleID: String
}
struct CampaignConfig {
let placeholderImage: UIImage
let transactionsHeaderImage: UIImage
let noDataImage: UIImage?
let buttons: CampaignButtons
let scannerConfig: ScannerConfig
}
struct CampaignButtons {
let backButton: UIButton?
let closeButton: UIButton?
let deleteButton: UIButton?
}
struct ScannerConfig {
let scanTitle: String
let scanDescription: String
let manualTitle: String
let manualDescription: String
}
struct CouponConfig {
let placeholderImage: UIImage
let noDataImage: UIImage?
}
struct NeoDaySDKConfig {
let preferredDateFormat: String
let gatewayConfig: GatewayConfig
let campaignConfig: CampaignConfig
let couponConfig: CouponConfig?
}
struct AuthenticationToken {
let accessToken: String
let accessTokenExpirationDate: Date
let refreshToken: String
}
struct CustomerAuthenticationEndpoint {
let baseURL: URL
let headers: [String: String]?
let parameters: [String: Any]?
}
class NeoDaySDK {
private(set) public var siteStyle: SiteStyle?
// When set, it means the client will be notified when data is being loaded and a loading indicator should be shown or not in the process
public weak var dataLoadingDelegate: SDKDataLoadingDelegate? {
// When set, it means the client will be responsible for showing the Feedback
public weak var feedbackDelegate: FeedbackDelegate?
// When set, it means the client will be responsible for showing the Survey
public weak var surveyDelegate: SurveyDelegate?
// When set, it means the client will be alerted when authentication is required again
public weak var needToAuthenticateDelegate: NeedToAuthenticateDelegate?
/// Configures the NeoDay SDK.
///
/// - parameter config: A configuration object for configuration the NeoDay SDK.
init(config: NeoDaySDKConfig)
/// Initializes the NeoDay SDK by using a signed token for the bearing user.
///
/// - parameter endPoint: The endpoint that will return the signed token for the bearing user.
/// - parameter completion: A success or an Error.
public func initialize(
with endPoint: CustomerAuthenticationEndpoint,
_ completion: @escaping (Result<Void, Error>) -> Void
)
/// Initializes the NeoDay SDK by using an AuthenticationToken.
///
/// - parameter token: The AuthenticationToken that will be used for the user.
/// - parameter completion: The result of using the AuthenticationToken.
public func initialize(
with token: AuthenticationToken,
_ completion: @escaping (Result<Void, Error>) -> Void
)
/// Retrieves the AuthenticationToken that is used by the NeoDay SDK.
///
/// - returns: Optionally the AuthenticationToken that the NeoDay SDK uses.
public func retrieveStoredToken() throws -> AuthenticationToken?
/// Removes the AuthenticationToken stored by the NeoDay SDK.
///
public func removeToken()
sdk version: 1.3.0
/// Configures the NeoDay SDK in order to communicate with the Backoffice
///
/// - parameter gateway: The URL used to communicate with the backend
/// - parameter clientWebURL: The URL of the NeoDay web client
/// - parameter xSiteToken: Used for identification
/// - parameter clientID: Used to identify the client
/// - parameter: siteStyleID: The id used to get SiteStyle that is configured inside the Backoffice
struct GatewayConfig {
let gateway: URL
let clientWebURL: URL
let xSiteToken: String
let clientID: String
let siteStyleID: String
}
struct CampaignConfig {
let placeholderImage: UIImage
let transactionsHeaderImage: UIImage
let noDataImage: UIImage?
let buttonFactory: (NDButtonType, SiteStyle) -> UIButton?
let scannerConfig: ScannerConfig
}
enum NDButtonType {
case back
case close
case delete
}
struct ScannerConfig {
let scanTitle: String
let scanDescription: String
let manualTitle: String
let manualDescription: String
}
struct CouponConfig {
let placeholderImage: UIImage
let noDataImage: UIImage?
}
struct NeoDaySDKConfig {
let preferredLocale: NeoDayLocale
let preferredDateFormat: String
let webViewTimeoutDuration: TimeInterval
let gatewayConfig: GatewayConfig
let campaignConfig: CampaignConfig
let couponConfig: CouponConfig?
let loyaltyCardConfig: LoyaltyCardConfig?
}
struct AuthenticationToken {
let accessToken: String
let accessTokenExpirationDate: Date
let refreshToken: String
}
struct CustomerAuthenticationEndpoint {
let baseURL: URL
let headers: [String: String]?
let parameters: [String: Any]?
}
class NeoDaySDK {
private(set) public var siteStyle: SiteStyle?
// When set, it means the client will be notified when data is being loaded and a loading indicator should be shown or not in the process
public weak var dataLoadingDelegate: SDKDataLoadingDelegate? {
// When set, it means the client will be responsible for showing the Feedback
public weak var feedbackDelegate: FeedbackDelegate?
// When set, it means the client will be responsible for showing the Survey
public weak var surveyDelegate: SurveyDelegate?
// When set, it means the client will be alerted when authentication is required again
public weak var needToAuthenticateDelegate: NeedToAuthenticateDelegate?
public weak var analyticsTrackingDelegate: NeoDayAnalyticsTrackingDelegate?
/// Configures the NeoDay SDK.
///
/// - parameter config: A configuration object for configuration the NeoDay SDK.
init(config: NeoDaySDKConfig)
/// Authenticate with NeoDay by using a signed token for the bearing user.
///
/// - parameter endPoint: The endpoint that will return the signed token for the bearing user.
/// - parameter completion: The AuthenticationToken that will be used or an Error.
public func authenticateUser(
endPoint: CustomerAuthenticationEndpoint,
completion: @escaping (Result<Void, Error>) -> Void
)
/// Authenticate with NeoDay by using an AuthenticationToken.
///
/// - parameter token: The AuthenticationToken that will be used for the user.
/// - parameter completion: The result of using the AuthenticationToken.
public func authenticateUser(
token: AuthenticationToken,
completion: @escaping (Result<Void, Error>) -> Void
)
/// Retrieves the AuthenticationToken that is used by the NeoDay SDK.
///
/// - returns: Optionally the AuthenticationToken that the NeoDay SDK uses.
public func retrieveStoredToken() throws -> AuthenticationToken?
/// Removes the AuthenticationToken stored by the NeoDay SDK.
///
public func removeToken()
sdk version: 1.3.1
The preferredLocale was added to the NeoDaySDKConfig. The default value for this field is the current device's locale.
struct NeoDaySDKConfig {
let preferredLocale: NeoDayLocale
let preferredDateFormat: String
let webViewTimeoutDuration: TimeInterval
let gatewayConfig: GatewayConfig
let campaignConfig: CampaignConfig
let couponConfig: CouponConfig?
let loyaltyCardConfig: LoyaltyCardConfig?
}
sdk version: 1.5.0
The optional pointsIcon is added to the CampaignConfig which can be seen on the screen created by makeCampaignTransactionsHistory(campaignID: blockID: limit: _ completion:)
struct CampaignConfig {
let placeholderImage: UIImage
let transactionsHeaderImage: UIImage
let noDataImage: UIImage?
let pointsIcon: UIImage?
let buttonFactory: (NDButtonType, SiteStyle) -> UIButton?
let scannerConfig: ScannerConfig
}
The NeoDaySDKDebugDelegate is added:
class NeoDaySDK {
...
public weak var debugDelegate: NeoDaySDKDebugDelegate?
...
}
It can return the following types of debug messages related to Site Style colors or fonts:
When the Backoffice configured Site Style color, cannot be interpreted:
"SiteStyle color cannot be interpreted, a 'clear' color is used instead."
When the Backoffice configured Site Style font family cannot be interpreted:
"SiteStyle font family cannot be interpreted, default font <font family name> is used instead."
When the Backoffice configured Site Style font for a font tag and weight cannot be interpreted:
"SiteStyle font cannot be interpreted for fontTag: <fontTag> and weight: <font weight>, default font <default font> is used instead."
sdk version: 1.7.0
The Campaign Counter is introduced on screens created by makeCampaignDetail(campaignID: campaignInstanceID: _ completion:)