Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FeaturevisorExampleIOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@
repositoryURL = "https://github.com/featurevisor/featurevisor-swift2.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
minimumVersion = 3.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 29 additions & 26 deletions FeaturevisorExampleIOS/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,47 @@ struct ContentView: View {
@StateObject private var viewModel = FeaturevisorViewModel()

var body: some View {
VStack(alignment: .leading, spacing: 20) {
Text("Featurevisor for iOS")
.font(.largeTitle)
.fontWeight(.bold)
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Text("Featurevisor for iOS")
.font(.largeTitle)
.fontWeight(.bold)

Text("Evaluating mobile_experience with the Featurevisor Swift SDK.")
.foregroundColor(.secondary)
Text("Evaluating features and global variables with the Featurevisor Swift SDK.")
.foregroundColor(.secondary)

if viewModel.isLoading {
ProgressView("Loading datafile…")
} else if let errorMessage = viewModel.errorMessage {
Text(errorMessage)
.foregroundColor(.red)
if viewModel.isLoading {
ProgressView("Loading datafile…")
} else if let errorMessage = viewModel.errorMessage {
Text(errorMessage)
.foregroundColor(.red)

Button("Try again", action: viewModel.load)
} else {
VStack(alignment: .leading, spacing: 12) {
row("Flag", viewModel.enabled ? "Enabled" : "Disabled")
row("Variation", viewModel.variation)
row("Welcome message", viewModel.welcomeMessage)
Button("Try again", action: viewModel.load)
} else {
VStack(alignment: .leading, spacing: 12) {
row("Flag", viewModel.enabled ? "Enabled" : "Disabled")
row("Variation", viewModel.variation)
row("Maximum checkout items", viewModel.maxItems)
row("Payment methods", viewModel.paymentMethods)
row("Service endpoint", viewModel.serviceEndpoint)
row("Support contact", viewModel.supportContact)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(.secondarySystemBackground))
.cornerRadius(12)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(.secondarySystemBackground))
.cornerRadius(12)
}

Spacer()
.padding(24)
}
.padding(24)
.onAppear(perform: viewModel.load)
}

private func row(_ label: String, _ value: String) -> some View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(label)
.font(.caption)
.foregroundColor(.secondary)
Spacer()
Text(value)
.fontWeight(.semibold)
}
Expand Down
47 changes: 32 additions & 15 deletions FeaturevisorExampleIOS/FeaturevisorViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ final class FeaturevisorViewModel: ObservableObject {
@Published private(set) var errorMessage: String?
@Published private(set) var enabled = false
@Published private(set) var variation = ""
@Published private(set) var welcomeMessage = ""
@Published private(set) var maxItems = ""
@Published private(set) var paymentMethods = ""
@Published private(set) var serviceEndpoint = ""
@Published private(set) var supportContact = ""

private let datafileURL = URL(
string: "https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-mobile.json"
string: "https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-sdk-v3.json"
)!
private var f: Featurevisor?

Expand Down Expand Up @@ -49,27 +52,41 @@ final class FeaturevisorViewModel: ObservableObject {
do {
let datafile = try DatafileContent.fromData(data)
let f = createFeaturevisor(
FeaturevisorOptions(datafile: datafile)
FeaturevisorOptions(
datafile: datafile,
context: [
"userId": .string("customer-123"),
"country": .string("nl"),
"locale": .string("nl-NL"),
"accountPlan": .string("pro")
],
logLevel: .error
)
)
let context: Context = [
"userId": .string("mobile-user"),
"country": .string("nl")
]

let enabled = f.isEnabled("mobile_experience", context)
let variation = f.getVariation("mobile_experience", context) ?? "none"
let welcomeMessage = f.getVariableString(
"mobile_experience",
"welcome_message",
context
) ?? "none"
let enabled = f.isEnabled("commerce_platform")
let variation = f.getVariation("checkout_experience") ?? "none"
let maxItems = f.getVariableInteger(
"checkout_experience",
"max_items"
).map(String.init) ?? "none"
let paymentMethods = f.getVariableArray(
"checkout_experience",
"payment_methods"
)?.compactMap { $0.asString() }.joined(separator: ", ") ?? "none"
let endpoints = f.getVariableObject("serviceEndpoints")
let serviceEndpoint = endpoints?["baseUrl"]?.asString() ?? "none"
let supportContact = f.getVariableString("supportContact") ?? "none"

Task { @MainActor in
self.f?.close()
self.f = f
self.enabled = enabled
self.variation = variation
self.welcomeMessage = welcomeMessage
self.maxItems = maxItems
self.paymentMethods = paymentMethods
self.serviceEndpoint = serviceEndpoint
self.supportContact = supportContact
self.isLoading = false
}
} catch {
Expand Down
42 changes: 17 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

A small SwiftUI application showing how to use the [Featurevisor Swift SDK](https://github.com/featurevisor/featurevisor-swift2).

The application downloads a Featurevisor datafile, creates an SDK instance, and evaluates the `mobile_experience` feature as a flag, variation, and string variable.
Learn more about Featurevisor [here](https://featurevisor.com).

The application evaluates the same flag, variation, feature variables, and global variables as the other Featurevisor SDK examples.

## Requirements

Expand All @@ -15,34 +17,24 @@ You need Xcode 16 or newer. The application targets iOS 14 or newer.
3. Select an iPhone simulator.
4. Run the `FeaturevisorExampleIOS` scheme.

## Featurevisor usage

The application fetches this production datafile:
The application uses this production datafile:

```text
https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-mobile.json
https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-sdk-v3.json
```

The important SDK usage is in `FeaturevisorViewModel.swift`:

```swift
let datafile = try DatafileContent.fromData(data)
let f = createFeaturevisor(
FeaturevisorOptions(datafile: datafile)
)

let context: Context = [
"userId": .string("mobile-user"),
"country": .string("nl")
]

let enabled = f.isEnabled("mobile_experience", context)
let variation = f.getVariation("mobile_experience", context)
let message = f.getVariableString(
"mobile_experience",
"welcome_message",
context
)
The direct SDK integration lives in `FeaturevisorViewModel.swift`. Change its context values to see how Featurevisor selects different rules, variations, and global variable overrides.

## Checks

```sh
xcodebuild \
-project FeaturevisorExampleIOS.xcodeproj \
-scheme FeaturevisorExampleIOS \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO \
build
```

Learn more in the [Featurevisor Swift SDK documentation](https://featurevisor.com/docs/sdks/swift/).