EasyApp

Main App Entry

Learn what EasyAppSwiftUI's main app entry does

Main App Entry - ContentView

ContentView is the main entry point of the EasyAppSwiftUI application, responsible for initializing and configuring the application's core components, state managers, and environment objects.

Core Architecture

State Manager Initialization

ContentView initializes multiple key state managers:

struct ContentView: View {
    /// App state - Application global state
    @StateObject private var appState = AppState()

    /// Auth Manager - Authentication manager
    @StateObject private var authManager = AuthManager.shared

    /// RevenueCat - In-app purchase management (RevenueCat)
    @StateObject private var revenueCatManager = RevenueCatManager.shared

    /// StoreKit2 - In-app purchase management (StoreKit2)
    @StateObject private var storeKit2Manager = StoreKit2Manager.shared
}

Main Function Modules

  1. Application State Management (AppState)

    • Manages the application's global state
    • Coordinates data sharing between various modules
  2. User Authentication (AuthManager)

    • Handles user login, registration, and authentication
    • Manages user session state
  3. In-App Purchase System

    • RevenueCatManager: In-app purchase management based on RevenueCat
    • StoreKit2Manager: In-app purchase management based on StoreKit2
    • Supports dual in-app purchase solutions, providing better compatibility

View Structure

Main View Configuration

var body: some View {
    MainView()
        .showAuthView()                    // Show authentication view
        .showPaywallSheetWhenCalled()     // Show paywall
        .environmentObject(appState)       // Inject app state
        .environmentObject(authManager)    // Inject authentication manager
        .environment(
            \.whatsNew,
            .init(
                versionStore: UserDefaultsWhatsNewVersionStore(),
                whatsNewCollection: self
            )
        )                                 // Configure new feature introduction
        .environmentObject(revenueCatManager)  // Inject RevenueCat manager
        .environmentObject(storeKit2Manager)   // Inject StoreKit2 manager
}

View Modifier Explanation

  • .showAuthView(): Automatically shows authentication interface when user is not logged in
  • .showPaywallSheetWhenCalled(): Responds to paywall calls, displays subscription interface
  • Environment Object Injection: Injects managers into the entire view hierarchy through environmentObject

Core Modifier Details

showAuthView() - Authentication View Manager

showAuthView() is a key view modifier responsible for managing the entire application's user authentication flow.

Implementation Principle

extension View {
    /// Show Auth View
    /// This function is used to show the auth view.
    /// - Returns: A view with a auth view.
    func showAuthView() -> some View {
        modifier(AuthViewModifier())
    }
}

Main Functions

  1. Automatic Authentication Detection

    • Monitors user login status changes
    • Automatically pops up login interface when user is not authenticated
    • Automatically hides login interface after successful authentication
  2. Seamless User Experience

    • Silently checks authentication status in the background
    • Maintains continuity of user operations
    • Avoids unnecessary interface jumps
  3. State Management Integration

    • Deep integration with AuthManager
    • Responds to real-time changes in authentication status
    • Supports automatic refresh and re-authentication

showPaywallSheetWhenCalled() - Global Paywall Listener

showPaywallSheetWhenCalled() is a global paywall listener that works with AuthManager.actionGoPaywall() through the notification system.

Implementation Principle

extension View {
    /// Show Paywall Sheet When Called
    /// This function is used to show the paywall sheet when called.
    /// - Returns: A view with a paywall sheet.
    func showPaywallSheetWhenCalled() -> some View {
        self.modifier(ShowPaywallSheetWhenCalledModifier())
    }
}

Working Mechanism

  1. Notification Listening

    • Listens to Constants.Notifications.showPaywall notification
    • Set at ContentView root level for global coverage
    • Responds to paywall calls through NotificationCenter
  2. Integration with AuthManager

    • Works with AuthManager.actionGoPaywall() method
    • When user has no subscription, actionGoPaywall sends notification
    • Listener receives notification and displays corresponding paywall

AuthManager.actionGoPaywall() Method

extension AuthManager {
    public func actionGoPaywall(
        subscriptionType: SubscriptionType = .revenueCat,
        sheetOrFullScreenCover: SheetOrFullScreenCover = .sheet,
        _ closure: () -> Void
    ) {
        if subscriptionActive {
            closure() // User is subscribed, execute business logic
        } else {
            // Send notification to show paywall
            let notificationData = PaywallNotificationData(
                subscriptionType: subscriptionType,
                sheetOrFullScreenCover: sheetOrFullScreenCover
            )
            NotificationCenter.default.post(
                name: Constants.Notifications.showPaywall,
                object: nil,
                userInfo: notificationData.userInfo
            )
        }
    }
}

Usage Method

You can call the paywall from anywhere in the application using the following method:

// Use in any view
Button("Use Advanced Features") {
    authManager.actionGoPaywall(
        subscriptionType: .revenueCat,
        sheetOrFullScreenCover: .sheet
    ) {
        // Business logic executed when user is subscribed
        print("Execute advanced features")
        performPremiumAction()
    }
}

Architectural Advantages

  • Global Management: Unified handling of all paywall displays at root view
  • Decoupled Design: Complete separation of business logic from UI display
  • Flexible Calling: Can trigger paywall from anywhere in the application

New Feature Guide (WhatsNewKit)

ContentView implements the WhatsNewCollectionProvider protocol, providing new feature introductions when the app updates:

Protocol Implementation

extension ContentView: WhatsNewCollectionProvider {
    var whatsNewCollection: WhatsNewCollection {
        // Current version's new feature introduction
        WhatsNew(
            version: .current(),
            title: "News App2",
            features: [
                .init(
                    image: .init(
                        systemName: "apps.iphone",
                        foregroundColor: .black
                    ),
                    title: "Welcome to the News App",
                    subtitle: "This is a news app that allows you to read news articles"
                )
            ],
            primaryAction: .init(
                title: "OK",
                hapticFeedback: .notification(.success)
            ),
            secondaryAction: WhatsNew.SecondaryAction(
                title: "Learn more",
                foregroundColor: .accentColor,
                hapticFeedback: .selection,
                action: .openURL(
                    .init(string: "https://github.com/SvenTiigi/WhatsNewKit")
                )
            )
        )
        // Historical version's new feature introduction
        WhatsNew(
            version: "1.0.0",
            title: "Welcome to the News App",
            features: [
                .init(
                    image: .init(
                        systemName: "newspaper",
                        foregroundColor: .black
                    ),
                    title: "Welcome to the News App",
                    subtitle: "This is a news app that allows you to read news articles"
                )
            ],
            primaryAction: .init(
                title: "OK",
                hapticFeedback: .notification(.success)
            )
        )
    }
}

New Feature Guide Characteristics

  • Version Management: Uses UserDefaultsWhatsNewVersionStore to store displayed versions
  • Haptic Feedback: Integrates haptic feedback system to enhance user experience
  • Multi-version Support: Can define new feature introductions for multiple versions
  • Custom Actions: Supports primary and secondary action buttons

Key Features

1. Dependency Injection Architecture

Through @StateObject and environmentObject, a clear dependency injection pattern is implemented, ensuring:

  • Single data source
  • State sharing
  • Module decoupling

2. Multiple In-App Purchase Methods Support

Simultaneously integrates RevenueCat and StoreKit2, providing:

  • Better platform compatibility
  • Flexible monetization strategies
  • Backup solution support

3. User Experience Optimization

  • Automatic authentication flow
  • Smooth paywall experience
  • New feature guide system
  • Haptic feedback integration

Usage Recommendations

1. Authentication Flow Best Practices

// Use in views that need authentication protection
NavigationStack {
    TabView {
        // Main feature pages
        HomeView()
            .tabItem {
                Label("Home", systemImage: "house")
            }
        
        ProfileView()
            .tabItem {
                Label("Profile", systemImage: "person")
            }
    }
}
.showAuthView() // Add authentication protection at the outermost layer

2. Paywall Integration Strategy

// Use actionGoPaywall for payment protection
VStack {
    Text("Advanced Features")
    
    Button("Use AI Features") {
        authManager.actionGoPaywall(
            subscriptionType: .revenueCat,
            sheetOrFullScreenCover: .sheet
        ) {
            // AI feature logic executed when user is subscribed
            performAIFeature()
        }
    }
    
    Button("Use Professional Tools") {
        authManager.actionGoPaywall(
            subscriptionType: .storeKit2,
            sheetOrFullScreenCover: .fullScreenCover
        ) {
            // Professional tools logic executed when user is subscribed
            openProfessionalTools()
        }
    }
}

// Global paywall listener - add to root view
ContentView()
    .showPaywallSheetWhenCalled()

3. Two Payment Protection Mechanisms

EasyAppSwiftUI provides two different payment protection mechanisms:

// Through AuthManager's actionGoPaywall method
@EnvironmentObject private var authManager: AuthManager

Button("Advanced Features") {
    authManager.actionGoPaywall(
        subscriptionType: .revenueCat, // or .storeKit2
        sheetOrFullScreenCover: .sheet // or .fullScreenCover
    ) {
        // Business logic for subscribed users
        executeAdvancedFeature()
    }
}

requirePremium (View-level Protection)

// Apply payment protection modifier directly on view
AdvancedFeatureView()
    .requirePremium(
        subscriptionType: .revenueCat,
        sheetOrFullScreenCover: .sheet,
        onCancelPaywall: {
            print("User cancelled payment")
        },
        onPaySuccess: {
            print("User subscription successful")
        }
    )

Differences Between the Two Methods

  • actionGoPaywall: Based on notification system, globally unified management, works with showPaywallSheetWhenCalled()
  • requirePremium: View-level protection, applied directly on view, works independently

4. Development and Debugging Recommendations

Basic Configuration

  • Custom New Feature Introduction: Modify content in whatsNewCollection to adapt to your app
  • Environment Configuration: Add or remove environment objects as needed
  • State Management: Use injected managers for state operations
  • Modular Development: Access these managers through @EnvironmentObject in child views

Authentication and Payment Testing

  • Test Authentication Flow: Test login, logout, and session expiration scenarios during development
  • actionGoPaywall Testing: Verify logic branches under different subscription statuses
  • Notification System Verification: Ensure showPaywallSheetWhenCalled correctly responds to notifications
  • Paywall Display Testing: Verify sheet and fullScreenCover display methods

Debugging Tips

// Add debug information in actionGoPaywall
authManager.actionGoPaywall { 
    print("✅ User is subscribed, executing advanced features")
    // Business logic
}

// Listen to paywall notifications (for debugging)
NotificationCenter.default.addObserver(
    forName: Constants.Notifications.showPaywall,
    object: nil,
    queue: .main
) { notification in
    print("🔔 Received paywall display notification: \(notification.userInfo ?? [:])")
}

Architectural Advantages Summary

Unified Management of Authentication and Payment

The application of showAuthView() and showPaywallSheetWhenCalled() modifiers in ContentView demonstrates the following architectural advantages:

  1. Centralized Control

    • showAuthView(): Unified management of user authentication status at application entry
    • showPaywallSheetWhenCalled(): Global monitoring of paywall display requests through notification system
    • Simplifies implementation complexity of various sub-modules, providing consistent user experience
  2. Loose Coupling Design

    • Authentication protection automatically applied through modifiers, child views don't need to care about specific implementation
    • Paywall decoupled from UI display through AuthManager.actionGoPaywall() and notification system
    • Complete separation of business logic from UI display, facilitating testing and maintenance
  3. Flexible Payment Protection Strategy

    • Global Notification Method: actionGoPaywall() + showPaywallSheetWhenCalled()
    • View-level Protection: requirePremium() modifier
    • Supports dual RevenueCat and StoreKit2 solutions
    • Can choose different display methods (sheet/fullScreenCover) based on scenarios

Development Efficiency Improvement

  • Quick Integration: Just add two modifiers at root view to get complete authentication and payment infrastructure
  • Unified Management: All authentication and payment-related states managed centrally through managers
  • Code Reuse: Modifiers and managers can be reused across different projects
  • Debug-friendly: Easily track paywall triggers and displays through notification system

ContentView, as the core entry point of the application, provides authentication protection through showAuthView(), establishes global paywall monitoring mechanism through showPaywallSheetWhenCalled(), and implements complete user authentication and monetization infrastructure in conjunction with AuthManager.actionGoPaywall(), laying a solid architectural foundation for the entire application's functional modules.

Last updated on