Skip to content

Commit 1a76b8b

Browse files
Merge pull request #667 from qonversion/release/6.11.0
Release 6.11.0
2 parents a2bfa94 + 2dd865e commit 1a76b8b

18 files changed

Lines changed: 231 additions & 50 deletions

Framework/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>6.10.0</string>
18+
<string>6.11.0</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
PODS:
22
- OCMock (3.9.4)
3-
- Qonversion (6.8.0):
4-
- Qonversion/Main (= 6.8.0)
5-
- Qonversion/Main (6.8.0)
3+
- Qonversion (6.9.0):
4+
- Qonversion/Main (= 6.9.0)
5+
- Qonversion/Main (6.9.0)
66

77
DEPENDENCIES:
88
- OCMock
@@ -18,7 +18,7 @@ EXTERNAL SOURCES:
1818

1919
SPEC CHECKSUMS:
2020
OCMock: 589f2c84dacb1f5aaf6e4cec1f292551fe748e74
21-
Qonversion: dc8d5a02ec83a4bfe1f2a73f08d9c00ebb254063
21+
Qonversion: bcd154ecd6c0f323971dbddbb71d2e9257b171d1
2222

2323
PODFILE CHECKSUM: 039889b49d515621e653ab1d97888c4c2744c482
2424

Qonversion.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Pod::Spec.new do |s|
33
idfa_exclude_files = ['Sources/Qonversion/IDFA']
44
s.name = 'Qonversion'
55
s.swift_version = '5.5'
6-
s.version = '6.10.0'
6+
s.version = '6.11.0'
77
s.summary = 'qonversion.io'
88
s.description = <<-DESC
99
Deep Analytics for iOS Subscriptions

Sample/Views/EntitlementsView.swift

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ struct EntitlementsView: View {
2222
Task { await appState.loadEntitlements() }
2323
}
2424

25-
ActionButton(title: "Set Entitlements Listener", color: .purple) {
26-
setEntitlementsListener()
25+
ActionButton(title: "Set Deferred Purchases Listener", color: .purple) {
26+
setDeferredPurchasesListener()
2727
}
2828

2929
ActionButton(title: "Restore Purchases", color: .green) {
@@ -45,7 +45,7 @@ struct EntitlementsView: View {
4545
HStack {
4646
Image(systemName: "checkmark.circle.fill")
4747
.foregroundColor(.green)
48-
Text("Entitlements listener is active")
48+
Text("Deferred purchases listener is active")
4949
.font(.caption)
5050
.foregroundColor(.secondary)
5151
}
@@ -108,22 +108,29 @@ struct EntitlementsView: View {
108108
}
109109
}
110110

111-
private func setEntitlementsListener() {
112-
Qonversion.shared().setEntitlementsUpdateListener(EntitlementsListenerHandler.shared)
113-
EntitlementsListenerHandler.shared.appState = appState
111+
private func setDeferredPurchasesListener() {
112+
Qonversion.shared().setDeferredPurchasesListener(DeferredPurchasesListenerHandler.shared)
113+
DeferredPurchasesListenerHandler.shared.appState = appState
114114
listenerSet = true
115-
appState.successMessage = "Entitlements listener set successfully!"
115+
appState.successMessage = "Deferred purchases listener set successfully!"
116116
}
117117
}
118118

119-
// MARK: - Entitlements Listener Handler
120-
class EntitlementsListenerHandler: NSObject, Qonversion.EntitlementsUpdateListener {
121-
static let shared = EntitlementsListenerHandler()
119+
// MARK: - Deferred Purchases Listener Handler
120+
class DeferredPurchasesListenerHandler: NSObject, Qonversion.DeferredPurchasesListener {
121+
static let shared = DeferredPurchasesListenerHandler()
122122
weak var appState: AppState?
123-
124-
func didReceiveUpdatedEntitlements(_ entitlements: [String: Qonversion.Entitlement]) {
123+
124+
func deferredPurchaseCompleted(_ purchaseResult: Qonversion.PurchaseResult) {
125125
Task { @MainActor in
126-
appState?.entitlements = entitlements
126+
if purchaseResult.isSuccessful, let entitlements = purchaseResult.entitlements {
127+
for (key, value) in entitlements {
128+
appState?.entitlements[key] = value
129+
}
130+
appState?.successMessage = "Deferred purchase completed successfully!"
131+
} else if purchaseResult.isError {
132+
appState?.errorMessage = purchaseResult.error?.localizedDescription ?? "Deferred purchase failed"
133+
}
127134
}
128135
}
129136
}

Sample/Views/NoCodesView.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ struct NoCodesView: View {
1616
@State private var animated = true
1717
@State private var listenerSet = false
1818
@State private var customizationDelegateSet = false
19+
@State private var customVariableName = ""
20+
@State private var customVariableValue = ""
21+
@State private var customVariablesDelegateSet = false
1922

2023
var body: some View {
2124
ScrollView {
@@ -92,6 +95,54 @@ struct NoCodesView: View {
9295
.background(Color(.systemGray6))
9396
.cornerRadius(12)
9497

98+
// Custom Variables Section
99+
VStack(alignment: .leading, spacing: 12) {
100+
Text("Custom Variables")
101+
.font(.headline)
102+
103+
TextField("Variable Name", text: $customVariableName)
104+
.textFieldStyle(.roundedBorder)
105+
TextField("Variable Value", text: $customVariableValue)
106+
.textFieldStyle(.roundedBorder)
107+
108+
ActionButton(title: "Add Variable", color: .orange) {
109+
guard !customVariableName.isEmpty else { return }
110+
NoCodesCustomVariablesHandler.shared.variables[customVariableName] = customVariableValue
111+
appState.successMessage = "Variable '\(customVariableName)' = '\(customVariableValue)' added"
112+
customVariableName = ""
113+
customVariableValue = ""
114+
}
115+
116+
ActionButton(title: "Set Custom Variables Delegate", color: .orange) {
117+
NoCodes.shared.set(customVariablesDelegate: NoCodesCustomVariablesHandler.shared)
118+
customVariablesDelegateSet = true
119+
appState.successMessage = "Custom variables delegate set!"
120+
}
121+
122+
if customVariablesDelegateSet {
123+
HStack {
124+
Image(systemName: "checkmark.circle.fill")
125+
.foregroundColor(.green)
126+
Text("Custom variables delegate active (\(NoCodesCustomVariablesHandler.shared.variables.count) vars)")
127+
.font(.caption)
128+
.foregroundColor(.secondary)
129+
}
130+
}
131+
132+
if !NoCodesCustomVariablesHandler.shared.variables.isEmpty {
133+
ForEach(Array(NoCodesCustomVariablesHandler.shared.variables.keys.sorted()), id: \.self) { key in
134+
HStack {
135+
Text("\(key) = \(NoCodesCustomVariablesHandler.shared.variables[key] ?? "")")
136+
.font(.caption)
137+
Spacer()
138+
}
139+
}
140+
}
141+
}
142+
.padding()
143+
.background(Color(.systemGray6))
144+
.cornerRadius(12)
145+
95146
// Close Button
96147
ActionButton(title: "Close No-Code Screen", color: .red) {
97148
NoCodes.shared.close()
@@ -290,6 +341,17 @@ class NoCodesListenerHandler: NoCodesDelegate {
290341
}
291342
}
292343

344+
// MARK: - NoCodes Custom Variables Handler
345+
class NoCodesCustomVariablesHandler: NoCodesCustomVariablesDelegate {
346+
static let shared = NoCodesCustomVariablesHandler()
347+
var variables: [String: String] = [:]
348+
349+
func customVariables(for contextKey: String) -> [String: String] {
350+
print("Custom variables requested for context key: \(contextKey), returning: \(variables)")
351+
return variables
352+
}
353+
}
354+
293355
#Preview {
294356
NavigationView {
295357
NoCodesView()

Sources/NoCodes/Assemblies/NoCodesAssembly.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final class NoCodesAssembly {
3131

3232
let noCodesService: NoCodesServiceInterface = servicesAssembly.noCodesService()
3333
let screenEventsService: ScreenEventsServiceInterface = servicesAssembly.screenEventsService()
34-
let coordinator = NoCodesFlowCoordinator(delegate: configuration.delegate, screenCustomizationDelegate: configuration.screenCustomizationDelegate, purchaseDelegate: configuration.purchaseDelegate, noCodesService: noCodesService, screenEventsService: screenEventsService, viewsAssembly: viewsAssembly(), logger: miscAssembly.loggerWrapper(), customLocale: configuration.locale, theme: configuration.theme)
34+
let coordinator = NoCodesFlowCoordinator(delegate: configuration.delegate, screenCustomizationDelegate: configuration.screenCustomizationDelegate, purchaseDelegate: configuration.purchaseDelegate, customVariablesDelegate: configuration.customVariablesDelegate, noCodesService: noCodesService, screenEventsService: screenEventsService, viewsAssembly: viewsAssembly(), logger: miscAssembly.loggerWrapper(), customLocale: configuration.locale, theme: configuration.theme)
3535
flowCoordinatorInstance = coordinator
3636

3737
return coordinator

Sources/NoCodes/Assemblies/NoCodesViewsAssembly.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ final class ViewsAssembly {
2020
self.servicesAssembly = servicesAssembly
2121
}
2222

23-
func viewController(with screenId: String, delegate: NoCodesViewControllerDelegate, purchaseDelegate: NoCodesPurchaseDelegate?, screenCustomizationDelegate: NoCodesScreenCustomizationDelegate?, presentationConfiguration: NoCodesPresentationConfiguration, customLocale: String? = nil, theme: NoCodesTheme = .auto) -> NoCodesViewController {
24-
return NoCodesViewController(screenId: screenId, contextKey: nil, delegate: delegate, purchaseDelegate: purchaseDelegate, screenCustomizationDelegate: screenCustomizationDelegate, noCodesMapper: miscAssembly.noCodesMapper(), noCodesService: servicesAssembly.noCodesService(), screenEventsService: servicesAssembly.screenEventsService(), viewsAssembly: self, logger: miscAssembly.loggerWrapper(), presentationConfiguration: presentationConfiguration, contextBuilder: miscAssembly.contextBuilder(), htmlInjector: miscAssembly.htmlInjector(), customLocale: customLocale, theme: theme)
23+
func viewController(with screenId: String, delegate: NoCodesViewControllerDelegate, purchaseDelegate: NoCodesPurchaseDelegate?, screenCustomizationDelegate: NoCodesScreenCustomizationDelegate?, customVariablesDelegate: NoCodesCustomVariablesDelegate?, presentationConfiguration: NoCodesPresentationConfiguration, customLocale: String? = nil, theme: NoCodesTheme = .auto) -> NoCodesViewController {
24+
return NoCodesViewController(screenId: screenId, contextKey: nil, delegate: delegate, purchaseDelegate: purchaseDelegate, screenCustomizationDelegate: screenCustomizationDelegate, customVariablesDelegate: customVariablesDelegate, noCodesMapper: miscAssembly.noCodesMapper(), noCodesService: servicesAssembly.noCodesService(), screenEventsService: servicesAssembly.screenEventsService(), viewsAssembly: self, logger: miscAssembly.loggerWrapper(), presentationConfiguration: presentationConfiguration, contextBuilder: miscAssembly.contextBuilder(), htmlInjector: miscAssembly.htmlInjector(), customLocale: customLocale, theme: theme)
2525
}
2626

27-
func viewController(withContextKey contextKey: String, delegate: NoCodesViewControllerDelegate, purchaseDelegate: NoCodesPurchaseDelegate?, screenCustomizationDelegate: NoCodesScreenCustomizationDelegate?, presentationConfiguration: NoCodesPresentationConfiguration, customLocale: String? = nil, theme: NoCodesTheme = .auto) -> NoCodesViewController {
28-
return NoCodesViewController(screenId: nil, contextKey: contextKey, delegate: delegate, purchaseDelegate: purchaseDelegate, screenCustomizationDelegate: screenCustomizationDelegate, noCodesMapper: miscAssembly.noCodesMapper(), noCodesService: servicesAssembly.noCodesService(), screenEventsService: servicesAssembly.screenEventsService(), viewsAssembly: self, logger: miscAssembly.loggerWrapper(), presentationConfiguration: presentationConfiguration, contextBuilder: miscAssembly.contextBuilder(), htmlInjector: miscAssembly.htmlInjector(), customLocale: customLocale, theme: theme)
27+
func viewController(withContextKey contextKey: String, delegate: NoCodesViewControllerDelegate, purchaseDelegate: NoCodesPurchaseDelegate?, screenCustomizationDelegate: NoCodesScreenCustomizationDelegate?, customVariablesDelegate: NoCodesCustomVariablesDelegate?, presentationConfiguration: NoCodesPresentationConfiguration, customLocale: String? = nil, theme: NoCodesTheme = .auto) -> NoCodesViewController {
28+
return NoCodesViewController(screenId: nil, contextKey: contextKey, delegate: delegate, purchaseDelegate: purchaseDelegate, screenCustomizationDelegate: screenCustomizationDelegate, customVariablesDelegate: customVariablesDelegate, noCodesMapper: miscAssembly.noCodesMapper(), noCodesService: servicesAssembly.noCodesService(), screenEventsService: servicesAssembly.screenEventsService(), viewsAssembly: self, logger: miscAssembly.loggerWrapper(), presentationConfiguration: presentationConfiguration, contextBuilder: miscAssembly.contextBuilder(), htmlInjector: miscAssembly.htmlInjector(), customLocale: customLocale, theme: theme)
2929
}
3030
}
3131

Sources/NoCodes/Delegate.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ public protocol NoCodesScreenCustomizationDelegate {
7070
func noCodesCustomLoadingView() -> NoCodesLoadingView?
7171
}
7272

73+
/// Delegate responsible for providing custom variables for No-Codes screens.
74+
/// Custom variables are injected into the screen's JavaScript context
75+
/// and can be used to influence content displayed on the screen.
76+
public protocol NoCodesCustomVariablesDelegate: AnyObject {
77+
/// Provide custom variables for a specific screen identified by context key.
78+
/// Called each time a screen is about to be displayed.
79+
/// - Parameter contextKey: the context key of the screen being loaded.
80+
/// - Returns: a dictionary of custom variables to inject into the screen.
81+
func customVariables(for contextKey: String) -> [String: String]
82+
}
83+
7384
/// Delegate responsible for custom purchase and restore handling.
7485
/// When this delegate is provided, it replaces the default Qonversion SDK purchase flow.
7586
public protocol NoCodesPurchaseDelegate {

Sources/NoCodes/NoCodes.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ public final class NoCodes {
4848
flowCoordinator?.set(screenCustomizationDelegate: screenCustomizationDelegate)
4949
}
5050

51+
/// Use this function to set the custom variables delegate.
52+
/// This delegate will be called each time a screen is about to be displayed
53+
/// to get custom variables that will be injected into the screen's JavaScript context.
54+
/// - Parameters:
55+
/// - delegate: ``NoCodesCustomVariablesDelegate`` object.
56+
public func set(customVariablesDelegate: NoCodesCustomVariablesDelegate) {
57+
flowCoordinator?.set(customVariablesDelegate: customVariablesDelegate)
58+
}
59+
5160
/// Use this function to set the purchase delegate.
5261
/// This delegate should be used if you want to handle purchases and restore operations on your end.
5362
/// If this delegate is provided, it will be used instead of the default Qonversion SDK purchase flow.

Sources/NoCodes/NoCodesConfiguration.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ public struct NoCodesConfiguration {
2323

2424
/// Purchase delegate. If provided, it will be used instead of the default Qonversion SDK purchase flow.
2525
public var purchaseDelegate: NoCodesPurchaseDelegate?
26+
27+
/// Custom variables delegate. If provided, it will be called each time a screen is about to be displayed
28+
/// to get custom variables for the screen.
29+
public weak var customVariablesDelegate: NoCodesCustomVariablesDelegate?
2630

2731
/// Optional custom fallback file name. If not provided, defaults to "nocodes_fallbacks.json"
2832
public var fallbackFileName: String?
@@ -51,11 +55,12 @@ public struct NoCodesConfiguration {
5155
/// - proxyURL: Optional proxy URL for API requests. If not provided, uses default API endpoint
5256
/// - locale: Optional custom locale for No-Code screens localization. If not provided, uses system default
5357
/// - theme: Theme mode for No-Code screens. Defaults to `.auto` which follows device settings
54-
public init(projectKey: String, delegate: NoCodesDelegate? = nil, screenCustomizationDelegate: NoCodesScreenCustomizationDelegate? = nil, purchaseDelegate: NoCodesPurchaseDelegate? = nil, fallbackFileName: String? = nil, proxyURL: String? = nil, locale: String? = nil, theme: NoCodesTheme = .auto) {
58+
public init(projectKey: String, delegate: NoCodesDelegate? = nil, screenCustomizationDelegate: NoCodesScreenCustomizationDelegate? = nil, purchaseDelegate: NoCodesPurchaseDelegate? = nil, customVariablesDelegate: NoCodesCustomVariablesDelegate? = nil, fallbackFileName: String? = nil, proxyURL: String? = nil, locale: String? = nil, theme: NoCodesTheme = .auto) {
5559
self.projectKey = projectKey
5660
self.delegate = delegate
5761
self.screenCustomizationDelegate = screenCustomizationDelegate
5862
self.purchaseDelegate = purchaseDelegate
63+
self.customVariablesDelegate = customVariablesDelegate
5964
self.fallbackFileName = fallbackFileName
6065
self.proxyURL = proxyURL
6166
self.locale = locale

0 commit comments

Comments
 (0)