### Swift: Getting Started Tutorial Code Samples Source: https://github.com/soundblaster/specificationcore/blob/main/DOCS/INPROGRESS/Phase4_Tutorials_Planning.md Code samples demonstrating the basics of SpecificationCore, including context, composition, and built-in specifications. These samples are designed for progressive learning, with each step building upon the previous one. They are focused and concise, with comments explaining key concepts. ```swift // Sample code for getting-started-1.swift // Description: Basic setup and context struct User { let name: String let age: Int } protocol UserSpecification { func isSatisfied(by user: User) -> Bool } struct IsAdultSpec: UserSpecification { func isSatisfied(by user: User) -> Bool { return user.age >= 18 } } // Sample usage let user = User(name: "Alice", age: 30) let isAdult = IsAdultSpec() print("Is Alice an adult? \(isAdult.isSatisfied(by: user))") ``` ```swift // Sample code for getting-started-2.swift // Description: Composition of specifications struct IsTeenagerSpec: UserSpecification { func isSatisfied(by user: User) -> Bool { return user.age >= 13 && user.age < 18 } } struct OrSpec: UserSpecification { let spec1: T let spec2: U func isSatisfied(by user: User) -> Bool { return spec1.isSatisfied(by: user) || spec2.isSatisfied(by: user) } } // Sample usage let isAdultOrTeenager = OrSpec(spec1: IsAdultSpec(), spec2: IsTeenagerSpec()) let user1 = User(name: "Bob", age: 15) print("Is Bob an adult or teenager? \(isAdultOrTeenager.isSatisfied(by: user1))") ``` -------------------------------- ### Usage Example: Trial Limits with MaxCountSpec Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/MaxCountSpec.md Demonstrates setting up a trial period limit using MaxCountSpec. This example restricts users to a specific number of AI generations during a free trial, prompting an upgrade if the limit is reached. ```swift // Free trial: 10 AI generations let trialSpec = MaxCountSpec( counterKey: "ai_generations", maximumCount: 10 ) @Satisfies(using: trialSpec) var canGenerateAI: Bool if canGenerateAI { let result = generateAIContent() provider.incrementCounter("ai_generations") return result } else { showUpgradePrompt() } ``` -------------------------------- ### Dependency Injection for Testability in Swift Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Illustrates the importance of using dependency injection for creating testable code. The 'good' example shows injecting a `contextProvider`, making it easy to substitute mock implementations during testing. The 'avoid' example highlights the difficulty of testing hard-coded dependencies. ```swift // ✅ Good - inject provider for testability class FeatureService { let contextProvider: any ContextProviding init(contextProvider: any ContextProviding = DefaultContextProvider.shared) { self.contextProvider = contextProvider } } // ❌ Avoid - hard-coded provider class FeatureService { func checkFeature() { let context = DefaultContextProvider.shared.currentContext() // Hard to test } } ``` -------------------------------- ### Quick Example of DefaultContextProvider Usage Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DefaultContextProvider.md Demonstrates how to access the shared DefaultContextProvider instance, configure state like feature flags and counters, record events, retrieve the current context, and use it with specifications. ```swift import SpecificationCore // Use the shared instance let provider = DefaultContextProvider.shared // Configure state provider.setFlag("premium_features", to: true) provider.setCounter("api_calls", to: 50) provider.recordEvent("last_login") // Get context for specifications let context = provider.currentContext() // Use with specifications @Satisfies(using: FeatureFlagSpec(flagKey: "premium_features")) var showPremiumFeatures: Bool ``` -------------------------------- ### Usage Example: Onboarding Delay Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/TimeSinceEventSpec.md Shows how to record the completion of onboarding and use TimeSinceEventSpec to ensure at least one hour has passed before allowing advanced features to be displayed. ```swift // Record when user completes onboarding provider.recordEvent("onboarding_completed") // Show advanced features after 1 hour of app use let advancedFeaturesSpec = TimeSinceEventSpec( eventKey: "onboarding_completed", hours: 1 ) @Satisfies(using: advancedFeaturesSpec) var canShowAdvancedFeatures: Bool if canShowAdvancedFeatures { showAdvancedTutorial() } ``` -------------------------------- ### Swift: Use Descriptive Specification Names Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/SpecsMacro.md This Swift code illustrates best practices for naming specifications. It contrasts 'Good' examples with clear intent (e.g., 'PremiumUserSpec') against 'Avoid' examples with unclear purpose (e.g., 'ComboSpec'). ```swift // ✅ Good - clear intent @specs( PremiumUserSpec(), EmailVerifiedSpec(), NoViolationsSpec() ) struct PremiumAccessSpec: Specification { typealias T = EvaluationContext } // ❌ Avoid - unclear purpose @specs( Spec1(), Spec2(), Spec3() ) struct ComboSpec: Specification { typealias T = EvaluationContext } ``` -------------------------------- ### Swift: Use Built-in EvaluationContext and DefaultContextProvider Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Shows how to use the default `EvaluationContext` and `DefaultContextProvider` provided by SpecificationCore. This example demonstrates setting flags, counters, and recording events. ```swift // Use the default context provider let provider = DefaultContextProvider.shared // Set some context data provider.setFlag("premium_user", value: true) provider.setCounter("login_attempts", value: 3) provider.recordEvent("last_login") // Get current context let context = provider.currentContext() // Use with specifications let spec = MaxCountSpec( counterKey: "login_attempts", maximumCount: 5 ) let canRetry = spec.isSatisfiedBy(context) ``` -------------------------------- ### Quick Example: Using MaxCountSpec for API Call Limits Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/MaxCountSpec.md Demonstrates setting up a context, creating a MaxCountSpec to limit API calls to 100, and using it with a property wrapper to control access. ```swift import SpecificationCore // Set up context let provider = DefaultContextProvider.shared provider.setCounter("api_calls_today", to: 50) // Create spec with limit of 100 let apiLimitSpec = MaxCountSpec( counterKey: "api_calls_today", maximumCount: 100 ) // Use with property wrapper @Satisfies(using: apiLimitSpec) var canMakeAPICall: Bool if canMakeAPICall { makeAPICall() provider.incrementCounter("api_calls_today") } ``` -------------------------------- ### Swift: Define and Use Custom Context Provider Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Demonstrates how to define a custom context provider by conforming to the ContextProviding protocol and using it with specifications. This example shows defining a custom AppContext and AppContextProvider. ```swift import SpecificationCore // Define your context type struct AppContext { let userId: String let featureFlags: [String: Bool] let sessionStart: Date } // Create a context provider struct AppContextProvider: ContextProviding { func currentContext() -> AppContext { AppContext( userId: UserSession.current.userId, featureFlags: FeatureFlagManager.shared.allFlags(), sessionStart: UserSession.current.startTime ) } } // Use with specifications let provider = AppContextProvider() let context = provider.currentContext() // Specifications can now use this context let spec = FeatureFlagSpec(flagKey: "new_ui") let isEnabled = spec.isSatisfiedBy(context) ``` -------------------------------- ### Swift: Priority-Based Decision Logic Example Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/FirstMatchSpec.md This Swift example illustrates the core priority-based decision-making of `FirstMatchSpec`. Specifications are evaluated in the order they appear in the provided array. The `supportTierSpec` is configured with four different user specifications, each mapping to a support level string. The `decide` method will return the support level corresponding to the first specification that evaluates to true for the given user. This clearly shows the 'first match wins' principle. ```swift // High priority first, low priority last let supportTierSpec = FirstMatchSpec([ (EnterpriseCustomerSpec(), "priority_support"), // Checked first (PremiumSubscriberSpec(), "premium_support"), // Checked second (ActiveUserSpec(), "standard_support"), // Checked third (RegisteredUserSpec(), "basic_support") // Checked last ]) // Returns first match let tier = supportTierSpec.decide(user) ``` -------------------------------- ### Usage Example: Feature Usage Limits with MaxCountSpec Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/MaxCountSpec.md Illustrates how to apply different usage limits to features based on user tiers (e.g., premium vs. free) using MaxCountSpec. This example sets a monthly export limit that varies depending on the user's subscription status. ```swift // Premium users: 100 exports per month // Free users: 5 exports per month let provider = DefaultContextProvider.shared let isPremium = // ... determine user tier let exportLimit = isPremium ? MaxCountSpec.monthlyLimit("exports", limit: 100) : MaxCountSpec.monthlyLimit("exports", limit: 5) @Satisfies(using: exportLimit) var canExport: Bool if canExport { performExport() provider.incrementCounter("exports") } ``` -------------------------------- ### Usage Example: Loyalty Program Eligibility Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/TimeSinceEventSpec.md Illustrates checking if a user is eligible for a loyalty program by verifying that at least 30 days have passed since their registration event. ```swift // Check if user has been member for 30 days let loyaltySpec = TimeSinceEventSpec( eventKey: "user_registered", days: 30 ) @Satisfies(using: loyaltySpec) var isLoyaltyEligible: Bool func checkLoyaltyRewards() { if isLoyaltyEligible { unlockLoyaltyTier() } else { showIneligibleMessage() } } ``` -------------------------------- ### Use Builder Pattern for Test Context Creation Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/EvaluationContext.md Illustrates the builder pattern for creating test evaluation contexts, which leads to clearer and more concise test setup compared to manual, verbose construction. ```swift // ✅ Good - clear test setup func testPremiumFeatures() { let context = EvaluationContext() .withFlags(["premium": true]) .withCounters(["usage": 5]) XCTAssertTrue(premiumSpec.isSatisfiedBy(context)) } // ❌ Verbose - manual construction func testPremiumFeatures() { let context = EvaluationContext( currentDate: Date(), launchDate: Date(), userData: [:], counters: ["usage": 5], events: [:], flags: ["premium": true], segments: [] ) } ``` -------------------------------- ### Swift: Create Generic Context Provider from Closure Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Illustrates creating a context provider using a closure, allowing for concise definition of simple context providers. This is useful for quick setups or when a full struct is not needed. ```swift // Simple closure-based provider let provider = GenericContextProvider { EvaluationContext(userId: UserSession.currentUserId) } let context = provider.currentContext() ``` -------------------------------- ### Swift: DateRangeSpec for Limited-Time Features Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DateRangeSpec.md Provides an example of using DateRangeSpec to control access to a limited-time beta feature. It calculates the end date by adding a specific duration to the start date. ```swift // Beta feature available for 2 weeks let betaStart = Date() let betaEnd = Date().addingTimeInterval(14 * 86400) // 14 days let betaSpec = DateRangeSpec(start: betaStart, end: betaEnd) @Satisfies(using: betaSpec) var isBetaActive: Bool func accessBetaFeature() { guard isBetaActive else { showExpiredMessage() return } showBetaFeature() } ``` -------------------------------- ### Swift: @Satisfies for Feature Flag Management Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/Satisfies.md Provides practical examples of using the @Satisfies property wrapper in Swift for managing application features via feature flags. It shows how to declare boolean properties that reflect the state of different flags, simplifying conditional logic within the app setup. ```swift @Satisfies(using: FeatureFlagSpec(flagKey: "premium_features")) var showPremiumFeatures: Bool @Satisfies(using: FeatureFlagSpec(flagKey: "experimental_ui")) var useExperimentalUI: Bool @Satisfies(using: FeatureFlagSpec(flagKey: "analytics_enabled")) var shouldTrackAnalytics: Bool func setupApp() { if showPremiumFeatures { enablePremiumContent() } if useExperimentalUI { loadExperimentalComponents() } if shouldTrackAnalytics { startAnalyticsTracking() } } ``` -------------------------------- ### Default Implementations for Ease of Use in Swift Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Shows how to provide default implementations for dependencies, simplifying usage in production code while maintaining testability. The example demonstrates a default parameter for `contextProvider`, allowing instantiation without explicit arguments in common cases. ```swift // ✅ Good - default parameter for production use init(contextProvider: any ContextProviding = DefaultContextProvider.shared) { self.contextProvider = contextProvider } // Easy to use in production let service = FeatureService() // Uses default // Easy to test let service = FeatureService(contextProvider: mockProvider) ``` -------------------------------- ### Swift: Create Specifications Using Context Providers Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Illustrates how to create specifications directly using context providers. This includes creating specifications from predicates and dynamic specifications based on context. ```swift let provider = DefaultContextProvider.shared // Create a specification using the provider let spec = provider.predicate { context, user in context.flag(for: "premium_users_only") == true && user.subscriptionTier == "premium" } // Or create a more complex specification let dynamicSpec = provider.specification { if context.flag(for: "use_new_rules") == true { return AnySpecification(NewEligibilityRules()) } else { return AnySpecification(LegacyEligibilityRules()) } } ``` -------------------------------- ### Usage Example: Feature Unlock Timing Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/TimeSinceEventSpec.md Provides an example of unlocking advanced features after 3 days since the app's first launch, using TimeSinceEventSpec. ```swift // Unlock advanced features after 3 days of usage let featureUnlockSpec = TimeSinceEventSpec( eventKey: "app_first_launch", days: 3 ) @Satisfies(using: featureUnlockSpec) var canAccessAdvancedFeatures: Bool struct FeatureView: View { var body: some View { if canAccessAdvancedFeatures { AdvancedFeaturePanel() } else { LockedFeatureMessage() } } } ``` -------------------------------- ### Swift: Basic Specification Pattern Usage Source: https://github.com/soundblaster/specificationcore/blob/main/DOCS/INPROGRESS/Phase4_Tutorials_Planning.md Demonstrates the fundamental concepts of the Specification pattern in Swift, including defining specifications, using context providers, and composing specifications with logical operators. This covers basic and intermediate usage scenarios. ```swift // getting-started-01-first-spec.swift import SpecificationCore // Define a simple specification struct IsEvenSpec: Specification { let number: Int func isSatisfied(by object: Any?) -> Bool { guard let num = object as? Int else { return false } return num % 2 == 0 } } // Usage let evenSpec = IsEvenSpec(number: 4) print(evenSpec.isSatisfied(by: 4)) // true print(evenSpec.isSatisfied(by: 3)) // false ``` ```swift // getting-started-02-with-context.swift import SpecificationCore struct MaxCountSpec: Specification { let maxCount: Int private var currentCount: Int = 0 func isSatisfied(by object: Any?) -> Bool { currentCount += 1 return currentCount <= maxCount } } // Usage with context struct MyContext { var eventCounter: Int = 0 } struct IncrementCounterSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let context = object as? MyContext else { return false } // This is a simplified example; real context would be passed differently return true // Placeholder } } // To properly use context, EvaluationContext would be employed. // This snippet focuses on the idea of context-dependent evaluation. ``` ```swift // getting-started-03-composition.swift import SpecificationCore struct IsPositiveSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let num = object as? Int else { return false } return num > 0 } } struct IsLessThanTenSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let num = object as? Int else { return false } return num < 10 } } // Composition let positiveAndLessThanTen = IsPositiveSpec() && IsLessThanTenSpec() print(positiveAndLessThanTen.isSatisfied(by: 5)) // true print(positiveAndLessThanTen.isSatisfied(by: 15)) // false print(positiveAndLessThanTen.isSatisfied(by: -5)) // false let isEvenOrPositive = IsEvenSpec(number: 0) || IsPositiveSpec() print(isEvenOrPositive.isSatisfied(by: 4)) // true print(isEvenOrPositive.isSatisfied(by: -2)) // false (IsEvenSpec has number 0, not used in this example) print(isEvenOrPositive.isSatisfied(by: 7)) // true let notEvenSpec = !IsEvenSpec(number: 0) print(notEvenSpec.isSatisfied(by: 3)) // true print(notEvenSpec.isSatisfied(by: 4)) // false ``` ```swift // getting-started-04-built-in-specs.swift import SpecificationCore import Foundation // Assuming TimeSinceEventSpec and MaxCountSpec are available from SpecificationCore // Example usage of built-in specs let maxVisits = MaxCountSpec(maxCount: 3) _ = maxVisits.isSatisfied(by: nil) // 1st visit _ = maxVisits.isSatisfied(by: nil) // 2nd visit print(maxVisits.isSatisfied(by: nil)) // 3rd visit, returns true print(maxVisits.isSatisfied(by: nil)) // 4th visit, returns false // Assuming an event has occurred at a specific time let specificEventTime = Date().addingTimeInterval(-10) // 10 seconds ago let timeSinceEvent = TimeSinceEventSpec(eventName: "userAction", since: specificEventTime) // To evaluate TimeSinceEventSpec, you'd typically need to record the event first. // This snippet illustrates the concept and assumes event recording is handled elsewhere. // let evaluationContext = EvaluationContext() // evaluationContext.recordEvent("userAction") // print(timeSinceEvent.isSatisfied(by: evaluationContext)) // Combining built-in specs let limitedAndRecentEvent = maxVisits && timeSinceEvent // print(limitedAndRecentEvent.isSatisfied(by: evaluationContext)) ``` -------------------------------- ### Swift: Usage Example for Notification Throttling Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/CooldownIntervalSpec.md Provides a practical example of using `CooldownIntervalSpec` to throttle notification displays. It sets up a 1-hour cooldown and integrates it with a property wrapper to conditionally show notifications. ```swift let provider = DefaultContextProvider.shared // Create notification cooldown (1 hour) let notificationCooldown = CooldownIntervalSpec( eventKey: "last_notification_shown", hours: 1 ) @Satisfies(using: notificationCooldown) var canShowNotification: Bool func showImportantNotification(_ message: String) { guard canShowNotification else { print("Notification on cooldown") return } displayNotification(message) provider.recordEvent("last_notification_shown") } ``` -------------------------------- ### Swift: Provide Descriptive Predicate Specifications Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/PredicateSpec.md Demonstrates the importance of providing descriptive strings to PredicateSpec initializers for better debugging. Shows a 'good' example with a description and a 'less useful' example without one. ```swift // ✅ Good - descriptive for debugging let spec = PredicateSpec(description: "Active premium user") { user in user.isActive && user.tier == "premium" } // ❌ Less useful - no description let spec = PredicateSpec { user in user.isActive && user.tier == "premium" } ``` -------------------------------- ### Swift: Initialize MockContextProvider Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/MockContextProvider.md Shows different ways to initialize MockContextProvider. This includes creating an empty context, initializing with a pre-defined EvaluationContext, and setting initial values for the current date, flags, and counters during initialization. ```swift // Empty context let provider = MockContextProvider() // With specific context let context = EvaluationContext( flags: ["test_mode": true], counters: ["attempts": 3] ) let provider = MockContextProvider(context: context) // With builder parameters let provider = MockContextProvider( currentDate: Date(), flags: ["premium": true], counters: ["usage": 10] ) ``` -------------------------------- ### Swift: Usage Example for Banner Display Control Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/CooldownIntervalSpec.md Demonstrates how to control the frequency of displaying a promotional banner using `CooldownIntervalSpec.daily`. The example shows integration within a SwiftUI `View` and recording the event when the banner is interacted with. ```swift // Show promotional banner maximum once per day let bannerCooldown = CooldownIntervalSpec.daily("promo_banner_shown") @Satisfies(using: bannerCooldown) var shouldShowBanner: Bool struct ContentView: View { var body: some View { VStack { if shouldShowBanner { PromoBanner() .onTapGesture { DefaultContextProvider.shared .recordEvent("promo_banner_shown") } } MainContent() } } } ``` -------------------------------- ### Usage Example: Banner Display Limits with MaxCountSpec Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/MaxCountSpec.md Shows how to control the display frequency of UI elements like promotional banners using MaxCountSpec. This example limits a banner to appearing a maximum of 3 times. ```swift // Show promotional banner maximum 3 times let bannerSpec = MaxCountSpec.counter("promo_banner_shown", limit: 3) @Satisfies(using: bannerSpec) var shouldShowBanner: Bool struct ContentView: View { var body: some View { VStack { if shouldShowBanner { PromoBanner() .onAppear { DefaultContextProvider.shared .incrementCounter("promo_banner_shown") } } MainContent() } } } ``` -------------------------------- ### Swift: Usage Example for Retry Logic Control Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/CooldownIntervalSpec.md Illustrates implementing retry logic with a cooldown period using `CooldownIntervalSpec`. This example ensures a minimum time interval (5 minutes) between retry attempts for a failed operation. ```swift // Minimum 5 minutes between retry attempts let retryCooldown = CooldownIntervalSpec( eventKey: "last_retry_attempt", minutes: 5 ) @Satisfies(using: retryCooldown) var canRetry: Bool func retryFailedOperation() async { guard canRetry else { showError("Please wait before retrying") return } await performOperation() DefaultContextProvider.shared.recordEvent("last_retry_attempt") } ``` -------------------------------- ### Using Shared Instance for Application State (Swift) Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DefaultContextProvider.md Demonstrates the correct way to manage application state by using the shared instance of DefaultContextProvider. It highlights how using the shared instance ensures consistent state across the application, contrasting it with the pitfalls of creating multiple instances. ```swift // ✅ Good - shared state across app let provider = DefaultContextProvider.shared provider.setFlag("dark_mode", to: true) // ❌ Avoid - creating multiple instances fragments state let provider1 = DefaultContextProvider() let provider2 = DefaultContextProvider() // Different state! ``` -------------------------------- ### Swift: Property Wrappers Guide Code Samples Source: https://github.com/soundblaster/specificationcore/blob/main/DOCS/INPROGRESS/Phase4_Tutorials_Planning.md Code samples illustrating the use of property wrappers in SpecificationCore, including @Satisfies, @Decides, and @Maybe, along with asynchronous specifications. These samples demonstrate how property wrappers simplify the implementation and usage of specifications. ```swift // Sample code for property-wrappers-1.swift // Description: @Satisfies property wrapper @propertyWrapper struct Satisfies where Spec.Value == Value { var wrappedValue: Value let specification: Spec var projectedValue: Bool { specification.isSatisfied(by: wrappedValue) } } // Assuming a simple Specification protocol and implementation protocol Specification { associatedtype Value func isSatisfied(by value: Value) -> Bool } struct PositiveNumberSpec: Specification { func isSatisfied(by value: Int) -> Bool { return value > 0 } } struct NumberContainer { @Satisfies(specification: PositiveNumberSpec()) var number: Int } // Sample usage var container = NumberContainer(number: 5) print("Is number positive? \(container.projectedValue)") // Output: Is number positive? true ``` ```swift // Sample code for property-wrappers-2.swift // Description: @Decides property wrapper @propertyWrapper struct Decides where Spec.Value == Value { var wrappedValue: Value let specification: Spec let decision: (Value) -> String var projectedValue: String { if specification.isSatisfied(by: wrappedValue) { return "Satisfied: \(decision(wrappedValue))" } else { return "Not Satisfied" } } } struct AgeDecisionSpec: Specification { func isSatisfied(by value: Int) -> Bool { return value >= 18 } } struct Person { @Decides(specification: AgeDecisionSpec()) { "Adult" } var age: Int } // Sample usage var person = Person(age: 25) print(person.projectedValue) // Output: Satisfied: Adult ``` -------------------------------- ### Mock Providers for Unit Testing in Swift Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Demonstrates how to use a built-in mock provider for unit testing. It shows setting flags and counters on the mock provider and injecting it into a service class. The example also includes testing a service method that depends on context. ```swift let mockProvider = MockContextProvider() mockProvider.setFlag("test_feature", value: true) mockProvider.setCounter("attempts", value: 2) class UserService { let contextProvider: any ContextProviding init(contextProvider: any ContextProviding = DefaultContextProvider.shared) { self.contextProvider = contextProvider } func checkEligibility(_ user: User) -> Bool { let context = contextProvider.currentContext() let spec = EligibilitySpec() return spec.isSatisfiedBy((user, context)) } } let service = UserService(contextProvider: mockProvider) let isEligible = service.checkEligibility(testUser) ``` -------------------------------- ### Create Custom Swift Specifications with Parameters Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/Specification.md Demonstrates how to implement the `Specification` protocol to create custom specifications that can accept parameters. The example shows a `MinimumBalanceSpec` which takes a `minimumAmount` to check against a `BankAccount`'s balance. ```swift struct BankAccount { let balance: Decimal } struct MinimumBalanceSpec: Specification { let minimumAmount: Decimal func isSatisfiedBy(_ account: BankAccount) -> Bool { account.balance >= minimumAmount } } // Use with different minimum values let standardSpec = MinimumBalanceSpec(minimumAmount: 100) let premiumSpec = MinimumBalanceSpec(minimumAmount: 1000) ``` -------------------------------- ### Swift: Extract Complex Logic into Named Functions for Predicates Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/PredicateSpec.md Demonstrates extracting complex logic into a named function for better readability and maintainability when creating PredicateSpecs. Shows a 'good' example with an extracted function and a 'harder to read' example with inline logic. ```swift // ✅ Good - extract to named function func isEligibleForRefund(_ order: Order) -> Bool { let daysSincePurchase = Date().timeIntervalSince(order.purchaseDate) / 86400 return daysSincePurchase <= 30 && !order.isRefunded } let refundEligible = PredicateSpec( description: "Refund eligible", isEligibleForRefund ) // ❌ Harder to read - complex inline logic let refundEligible = PredicateSpec { order in let daysSincePurchase = Date().timeIntervalSince(order.purchaseDate) / 86400 return daysSincePurchase <= 30 && !order.isRefunded } ``` -------------------------------- ### Swift: Network-Based Feature Flags Example Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/AsyncSatisfies.md A comprehensive example showing how to use @AsyncSatisfies for network-based feature flags. It defines `NetworkContext`, `RemoteFeatureFlagSpec`, and demonstrates evaluating the feature flag status asynchronously, with error handling for network issues. ```swift struct NetworkContext { let apiClient: APIClient let userId: String } struct RemoteFeatureFlagSpec: AsyncSpecification { typealias T = NetworkContext let flagKey: String func isSatisfiedBy(_ context: NetworkContext) async throws -> Bool { let response = try await context.apiClient.get( "/feature-flags/(context.userId)" ) return response.flags[flagKey] as? Bool ?? false } } @AsyncSatisfies( provider: networkProvider, using: RemoteFeatureFlagSpec(flagKey: "experimental_features") ) var hasExperimentalFeatures: Bool? func loadFeatures() async { do { let enabled = try await $hasExperimentalFeatures.evaluate() if enabled { loadExperimentalFeatures() } else { loadStandardFeatures() } } catch { // Fall back to local configuration loadStandardFeatures() logError("Failed to fetch remote config: (error)") } } ``` -------------------------------- ### Swift: Macros for Specification Composition and Context Source: https://github.com/soundblaster/specificationcore/blob/main/DOCS/INPROGRESS/Phase4_Tutorials_Planning.md Introduces Swift macros like `@specs` for declarative composition of specifications and `@AutoContext` for simplifying provider access. This section demonstrates creating complex specifications and managing context automatically. ```swift // macros-01-specs-basic.swift import SpecificationCore // Assume User struct and IsAdminSpec, IsPositiveSpec are defined as before struct User { enum Role { case regular, admin }; let role: Role } struct IsAdminSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let user = object as? User else { return false }; return user.role == .admin }; typealias Input = User } struct IsPositiveSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let num = object as? Int else { return false }; return num > 0 }; typealias Input = Int } // Using @specs macro for composition @specs ( IsAdminSpec.self, IsPositiveSpec.self && IsAdminSpec.self ) struct ComplexUserSpec: Specification { // The macro generates the composition logic based on the provided specs. // This struct acts as a container and entry point for the generated specifications. // The actual composition is handled by the macro expansion. } // Example usage (requires providing appropriate inputs for each composed spec) let isAdminSpecInstance = ComplexUserSpec.specs[0] let user = User(role: .admin) print(isAdminSpecInstance.isSatisfied(by: user)) // true let composedSpecInstance = ComplexUserSpec.specs[1] print(composedSpecInstance.isSatisfied(by: user)) // true (user is admin and positive) print(composedSpecInstance.isSatisfied(by: User(role: .regular))) // false (regular user is not admin) ``` ```swift // macros-02-auto-context.swift import SpecificationCore // Assume User struct and IsAdminSpec are defined as before struct User { enum Role { case regular, admin }; let role: Role } struct IsAdminSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let user = object as? User else { return false }; return user.role == .admin }; typealias Input = User } // Using @AutoContext macro @AutoContext struct UserContextAwareSpec: Specification { // The macro will attempt to inject the correct context type automatically. // It assumes a provider is available to resolve the context. // The actual evaluation will use the resolved context. func isSatisfied(by context: EvaluationContext?) -> Bool { guard let user = context?.object(for: User.self) as? User else { return false } return user.role == .admin } } // Example usage with MockContextProvider struct MockContextProvider: ContextProvider { func provideContext() -> T? { // In a real scenario, this would resolve based on context needs. // Here, we provide a User object. return User(role: .admin) as? T } } let provider = MockContextProvider() let spec = UserContextAwareSpec() let context = EvaluationContext(provider: provider) print(spec.isSatisfied(by: context)) // true ``` ```swift // macros-03-ecommerce.swift import SpecificationCore // Simplified example for e-commerce scenarios struct Product { let price: Double; let category: String } struct UserProfile { let isPremium: Bool; let purchaseHistory: [String] } // Basic specs struct HighValueOrderSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let order = object as? Product else { return false }; return order.price > 100.0 }; typealias Input = Product } struct ElectronicsCategorySpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let order = object as? Product else { return false }; return order.category == "Electronics" }; typealias Input = Product } struct PremiumUserSpec: Specification { func isSatisfied(by object: Any?) -> Bool { guard let profile = object as? UserProfile else { return false }; return profile.isPremium }; typealias Input = UserProfile } // Using @specs for complex composition @specs ( HighValueOrderSpec.self, ElectronicsCategorySpec.self, PremiumUserSpec.self, (HighValueOrderSpec.self && ElectronicsCategorySpec.self), (PremiumUserSpec.self && HighValueOrderSpec.self) ) struct ECommerceSpecs: Specification { // Macro handles the creation and composition of the above specifications. } // Example usage let sampleProduct = Product(price: 150.0, category: "Electronics") let sampleProfile = UserProfile(isPremium: true, purchaseHistory: []) let highValue = ECommerceSpecs.specs[0] print("Is high value? \(highValue.isSatisfied(by: sampleProduct))") // true let electronics = ECommerceSpecs.specs[1] print("Is electronics? \(electronics.isSatisfied(by: sampleProduct))") // true let premiumUser = ECommerceSpecs.specs[2] print("Is premium user? \(premiumUser.isSatisfied(by: sampleProfile))") // true let highValueElectronics = ECommerceSpecs.specs[3] print("Is high value electronics? \(highValueElectronics.isSatisfied(by: sampleProduct))") // true let premiumHighValue = ECommerceSpecs.specs[4] print("Is premium high value? \(premiumHighValue.isSatisfied(by: sampleProduct))") // true ``` -------------------------------- ### Consistent Event Keys in Swift Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/CooldownIntervalSpec.md Illustrates the importance of using descriptive and consistent naming conventions for event keys. It shows examples of 'good' practices with clear, underscore-separated keys and 'avoid' examples with ambiguous or shortened keys. Consistent keys improve code readability and maintainability. ```swift // ✅ Good - descriptive, consistent naming "last_notification_shown" "last_api_call_made" "last_password_reset_attempt" // ❌ Avoid - ambiguous or inconsistent "notif" "api" "reset" ``` -------------------------------- ### Swift: Macros and Advanced Concepts Code Samples Source: https://github.com/soundblaster/specificationcore/blob/main/DOCS/INPROGRESS/Phase4_Tutorials_Planning.md Code samples covering advanced topics in SpecificationCore, including the use of macros, complex specification patterns, testing strategies, and best practices. These samples aim to demonstrate real-world usage and provide a progressive learning path for developers. ```swift // Sample code for macros-1.swift // Description: Basic macro usage for defining specifications // Assuming a hypothetical macro `SpecificationMacro` // This is illustrative as actual macro syntax depends on implementation. // @SpecificationMacro // protocol ComplexSpec { // associatedtype Value // func isSatisfied(by value: Value) -> Bool // } // Example of a complex specification logic that might be simplified by a macro struct User { let name: String let age: Int let isActive: Bool } struct ActiveAdultSpec { func isSatisfied(by user: User) -> Bool { return user.age >= 18 && user.isActive } } // Sample usage let user1 = User(name: "Charlie", age: 22, isActive: true) let activeAdultSpec = ActiveAdultSpec() print("Is Charlie an active adult? \(activeAdultSpec.isSatisfied(by: user1))") ``` ```swift // Sample code for macros-2.swift // Description: Testing complex specifications // Assuming a testing framework and the User struct from previous example struct TestableActiveAdultSpec: Specification { func isSatisfied(by user: User) -> Bool { return user.age >= 18 && user.isActive } } // Placeholder for test cases func testActiveAdultSpec() { let spec = TestableActiveAdultSpec() // Test case 1: Active adult let user1 = User(name: "David", age: 30, isActive: true) assert(spec.isSatisfied(by: user1), "Test Case 1 Failed: Active adult") // Test case 2: Inactive adult let user2 = User(name: "Eve", age: 25, isActive: false) assert(!spec.isSatisfied(by: user2), "Test Case 2 Failed: Inactive adult") // Test case 3: Active non-adult let user3 = User(name: "Frank", age: 17, isActive: true) assert(!spec.isSatisfied(by: user3), "Test Case 3 Failed: Active non-adult") print("All ActiveAdultSpec tests passed.") } // Sample usage: Call the test function testActiveAdultSpec() ``` -------------------------------- ### User Data Management API Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DefaultContextProvider.md APIs for setting, getting, and removing user-specific data. ```APIDOC ## User Data Management ### Description Manages user-specific data, allowing for setting, retrieval, and removal. #### setUserData(_:to:) Sets user data for a given key. #### getUserData(_:as:) Retrieves user data, specifying the expected type. #### removeUserData(_:) Removes user data associated with a key. ``` -------------------------------- ### Usage Example: Trial Period Verification Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/TimeSinceEventSpec.md Demonstrates how to use TimeSinceEventSpec to check if a trial period of 14 days has expired. ```swift // Check if trial period has expired (14 days) let trialSpec = TimeSinceEventSpec( eventKey: "trial_started", days: 14 ) @Satisfies(using: trialSpec) var isTrialExpired: Bool if isTrialExpired { showUpgradePrompt() } ``` -------------------------------- ### Creating Custom DefaultContextProvider Instances Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DefaultContextProvider.md Shows how to create isolated instances of DefaultContextProvider, which is useful for testing or managing module-specific state independently of the global shared instance. It also demonstrates dependency injection of a context provider into a class. ```swift // Create a separate instance let testProvider = DefaultContextProvider() testProvider.setFlag("test_mode", to: true) // Use in specific contexts class FeatureService { let contextProvider: DefaultContextProvider init(contextProvider: DefaultContextProvider = .shared) { self.contextProvider = contextProvider } func checkFeature() -> Bool { let context = contextProvider.currentContext() return context.flag(for: "feature_enabled") } } // Production uses shared let prodService = FeatureService() // Tests use isolated instance let testService = FeatureService(contextProvider: testProvider) ``` -------------------------------- ### Swift: Unit Test Specification with MockContextProvider Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/MockContextProvider.md Demonstrates how to use MockContextProvider to set up a specific context for unit testing a feature specification. It involves initializing the provider with flags and counters, retrieving the context, and asserting the specification's satisfaction. ```swift import SpecificationCore import XCTest class FeatureSpecTests: XCTestCase { func testPremiumFeature() { // Create mock provider with specific state let provider = MockContextProvider() .withFlag("premium", value: true) .withCounter("usage", value: 5) let context = provider.currentContext() // Test specification let spec = PremiumFeatureSpec() XCTAssertTrue(spec.isSatisfiedBy(context)) } } ``` -------------------------------- ### Swift: Basic DateRangeSpec Creation Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DateRangeSpec.md Illustrates the fundamental method of creating a DateRangeSpec by providing start and end dates. The description clarifies that the range is inclusive. ```swift // Basic creation with start and end dates let spec = DateRangeSpec( start: startDate, end: endDate ) // The range is inclusive: start <= currentDate <= end ``` -------------------------------- ### Swift: Create Static Context Provider for Fixed Context Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/ContextProviding.md Demonstrates how to create a `StaticContextProvider` to provide a fixed, unchanging context. This is particularly useful for testing scenarios or when the context is known and static. ```swift // Create a static context for testing let testContext = EvaluationContext(userId: "test-user-123") let provider = StaticContextProvider(testContext) // Always returns the same context let context1 = provider.currentContext() let context2 = provider.currentContext() // context1 === context2 (same instance) ``` -------------------------------- ### Swift: DateRangeSpec for Event Windows Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DateRangeSpec.md Illustrates how to use DateRangeSpec to manage an event window, such as conference dates. It defines precise start and end times for the event. ```swift // Conference dates: March 15-17, 2025 let conferenceStart = DateComponents( calendar: .current, year: 2025, month: 3, day: 15, hour: 9, minute: 0 ).date! let conferenceEnd = DateComponents( calendar: .current, year: 2025, month: 3, day: 17, hour: 18, minute: 0 ).date! let conferenceSpec = DateRangeSpec( start: conferenceStart, end: conferenceEnd ) @Satisfies(using: conferenceSpec) var isConferenceActive: Bool if isConferenceActive { enableLiveStreamFeatures() } ``` -------------------------------- ### Swift: @Maybe Evaluation Logic Explanation Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/Maybe.md Provides a Swift code example illustrating the evaluation logic of the @Maybe property wrapper. It shows how specifications are checked in order, returning the result of the first match or nil if none are satisfied. Dependencies include SpecificationCore. ```swift @Maybe([ (Spec1(), "result1"), // Checked first (Spec2(), "result2"), // Checked second (Spec3(), "result3") // Checked third ]) var result: String? // Evaluation logic: // 1. If Spec1 matches → returns "result1" // 2. Else if Spec2 matches → returns "result2" // 3. Else if Spec3 matches → returns "result3" // 4. Else → returns nil ``` -------------------------------- ### Feature Gate System Implementation Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/Satisfies.md Illustrates the implementation of a feature gate system using specifications. This example shows how different feature access levels can be defined using feature flags and premium access checks, controlling which features users can access. ```swift struct FeatureGate { @Satisfies(using: FeatureFlagSpec(flagKey: "experimental_ai")) var hasAIFeatures: Bool @Satisfies(using: FeatureFlagSpec(flagKey: "beta_testing")) var isBetaTester: Bool @Satisfies(predicate: { context in context.flag(for: "premium_subscription") || context.flag(for: "lifetime_access") }) var hasPremiumAccess: Bool func canAccessFeature(_ feature: Feature) -> Bool { switch feature { case .aiGeneration: return hasAIFeatures && hasPremiumAccess case .advancedAnalytics: return hasPremiumAccess case .experimentalFeatures: return isBetaTester case .basicFeatures: return true } } } ``` -------------------------------- ### Build EvaluationContext Step-by-Step (Swift) Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/EvaluationContext.md Shows how to configure an `EvaluationContext` incrementally using individual builder methods. This approach allows for fine-grained control over context properties like flags, counters, events, user data, and timestamps. ```swift let context = EvaluationContext() // Build step by step let configured = context .withFlags(["premium": true, "trial": false]) .withCounters(["logins": 5]) .withEvents(["last_action": Date()]) .withUserData(["user_id": "abc123"]) .withCurrentDate(Date()) // All builder methods return new contexts ``` -------------------------------- ### App Launch Flow Logic with Specifications Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/Satisfies.md Demonstrates a real-world application of specifications in determining the initial route of an application based on conditions like first launch, onboarding status, and user login. This showcases how specifications can drive application flow. ```swift class AppCoordinator { @Satisfies(using: TimeSinceEventSpec(eventKey: "first_launch", hours: 0).not()) var isFirstLaunch: Bool @Satisfies(using: MaxCountSpec.onlyOnce("onboarding_completed").not()) var needsOnboarding: Bool @Satisfies(predicate: { context in context.flag(for: "user_logged_in") }) var isUserLoggedIn: Bool func determineInitialRoute() -> AppRoute { if isFirstLaunch { return .welcome } else if needsOnboarding { return .onboarding } else if !isUserLoggedIn { return .login } else { return .home } } } ``` -------------------------------- ### Swift: DateRangeSpec Logic Explanation Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/DateRangeSpec.md Explains the internal logic of DateRangeSpec by showing how it evaluates dates against a defined range. It provides clear examples of satisfied and unsatisfied conditions. ```swift let start = Date(timeIntervalSince1970: 1000) let end = Date(timeIntervalSince1970: 2000) let spec = DateRangeSpec(start: start, end: end) // Current date = 900: NOT satisfied ❌ (before start) // Current date = 1000: satisfied ✅ (at start) // Current date = 1500: satisfied ✅ (in middle) // Current date = 2000: satisfied ✅ (at end) // Current date = 2100: NOT satisfied ❌ (after end) ``` -------------------------------- ### Swift: Use KeyPath Methods for Concise Predicates Source: https://github.com/soundblaster/specificationcore/blob/main/Sources/SpecificationCore/Documentation.docc/PredicateSpec.md Shows how to use KeyPath methods for creating concise PredicateSpecs, such as checking for a boolean property. Contrasts the concise KeyPath approach with a more verbose closure-based approach. ```swift // ✅ Good - concise with KeyPath let activeSpec = PredicateSpec.keyPath(\.isActive) // ❌ Verbose - unnecessary closure let activeSpec = PredicateSpec { user in user.isActive } ```