diff --git a/FeaturevisorExampleIOS.xcodeproj/project.pbxproj b/FeaturevisorExampleIOS.xcodeproj/project.pbxproj index 6b1b2d9..8f01119 100644 --- a/FeaturevisorExampleIOS.xcodeproj/project.pbxproj +++ b/FeaturevisorExampleIOS.xcodeproj/project.pbxproj @@ -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 */ diff --git a/FeaturevisorExampleIOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FeaturevisorExampleIOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d64927c..b56f425 100644 --- a/FeaturevisorExampleIOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/FeaturevisorExampleIOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/featurevisor/featurevisor-swift2.git", "state" : { - "revision" : "eb550035a6b16b200fca5498824bcc4dd022f5d0", - "version" : "2.0.0" + "revision" : "4f0905ca8b13bb29f3fc68566dfd797048cc7e10", + "version" : "3.0.0" } }, { diff --git a/FeaturevisorExampleIOS/ContentView.swift b/FeaturevisorExampleIOS/ContentView.swift index 396e049..b0bad10 100644 --- a/FeaturevisorExampleIOS/ContentView.swift +++ b/FeaturevisorExampleIOS/ContentView.swift @@ -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) } diff --git a/FeaturevisorExampleIOS/FeaturevisorViewModel.swift b/FeaturevisorExampleIOS/FeaturevisorViewModel.swift index 26ecf20..9ed61ff 100644 --- a/FeaturevisorExampleIOS/FeaturevisorViewModel.swift +++ b/FeaturevisorExampleIOS/FeaturevisorViewModel.swift @@ -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? @@ -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 { diff --git a/README.md b/README.md index 928123d..fcad45c 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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/).