Extending StarterAppKit

As you begin customizing StarterAppKit for your own purposes, you’ll likely want to tailor the initial views presented to your users. One of the first places you might start is by modifying or replacing the Base/Home view.

Replacing the Base/Home View

By default, the Base view is included as an example entry point to various pre-built views and features in StarterAppKit. If a user is authenticated, this view acts as the “home” screen; if not, the user sees the authentication screen. The Base view also serves as the primary entry point for authenticated deep links, helping to route the user to the correct destination based on incoming URLs.

Where the Base View Is Introduced

The Base view is introduced in InitialView.swift, which determines what to present based on the user’s authentication state. Here’s the relevant snippet:

// InitialView.swift struct InitialView: View { @StateObject var vm: InitialViewModel var body: some View { ZStack { Color.clear Group { switch vm.authState { case .undetermined: EmptyView() case .loggedIn: if let paths = vm.navigationPaths, !paths.isEmpty, paths[0] == "home" { // If deep linking to "home" BaseView(remainingPaths: Array(paths.dropFirst())) } else if let directPath = vm.currentPath { // Direct navigation to a specific view based on deep link destinationView(for: directPath) } else { // Default home screen if user is logged in BaseView(remainingPaths: nil) } case .loggedOut: // If not logged in, show SignInView or handle non-auth deep links if let path = vm.currentPath, !(AppDeepLink(rawValue: path)?.requiresAuth ?? false) { destinationView(for: path) } else { SignInView(vm: AuthViewModel()) } } } } .toast(manager: vm.getAppManager()) } @ViewBuilder private func destinationView(for path: String) -> some View { switch path { case AppDeepLink.confirmAuth.rawValue: ConfirmAuthView(vm: ConfirmAuthViewModel(token: vm.parameters["token"] ?? "")) case AppDeepLink.resetPassword.rawValue: ResetPasswordView(vm: AuthViewModel(token: vm.parameters["token"] ?? nil)) default: EmptyView() } } }

The Role of the Base View

  1. Authenticated Home Screen: The Base view is what the user sees if they are already logged in. By default, it acts as your "home" screen.
  2. Deep Linking Entry Point: When a user opens a deep link that requires authentication (like myapp://home/profile), the Base view may be responsible for initiating navigation to the correct subview.
  3. Customization Opportunity: If the default layout or navigation logic in BaseView.swift does not align with your desired architecture, you can modify it directly or replace it entirely.

Two Ways to Replace the Base/Home View

  1. Edit BaseView.swift directly: If you’re mostly satisfied with the current setup, you can open BaseView.swift and remove or adjust code that’s not needed.

  2. Create Your Own View and Update InitialView.swift: If you’d prefer a fresh start, create a new SwiftUI view (e.g. MyCustomHomeView.swift) and then replace BaseView(remainingPaths: ...) calls in InitialView.swift with your custom view.

Example: Replacing the BaseView

Let’s assume you’ve created a new home screen view called MyCustomHomeView.swift.

  • remainingPaths: An array of path segments extracted from a deep link (e.g. myapp://home/profile["home", "profile"])
  • nextDestination: A computed property that identifies the next path segment and the remaining segments after it.
  • shouldNavigate: A state variable that triggers navigation once the view appears and a next destination is detected.
  • CustomNavigationLink: Used to initiate navigation to the target view identified by the next deep link path.
// MyCustomHomeView.swift import SwiftUI struct MyCustomHomeView: View { var remainingPaths: [String]? @State private var shouldNavigate = false // Determine if there's another destination in the deep link paths var nextDestination: (path: String, remaining: [String])? { guard let paths = remainingPaths, !paths.isEmpty else { return nil } // Take the first path as the next destination, and pass along the rest let next = (paths[0], Array(paths.dropFirst())) return next } var body: some View { VStack(spacing: 20) { Text("Welcome to My Custom Home!") .font(.largeTitle) .padding() // Add your own UI elements here (buttons, lists, etc.) } .onAppear { // If there's a next destination (e.g., "profile"), prepare to navigate if nextDestination != nil { shouldNavigate = true } } .background( Group { if let next = nextDestination { CustomNavigationLink( shouldNavigate: $shouldNavigate, destination: { // Navigate to the appropriate screen based on the next path switch next.path { case "profile": return AnyView( UserProfileView( vm: UserProfileViewModel(), remainingPaths: next.remaining ) ) // Add more cases here for other deep link paths default: // If the path doesn't match a known route, return a fallback view return nil } }, label: { EmptyView() } ) } } ) } }

Integrating Your Custom Home View

In InitialView.swift, replace the references to BaseView with MyCustomHomeView. If the user is authenticated and there are no deep link paths, your custom home view will appear by default. If there are deep link paths, MyCustomHomeView uses the logic above to navigate to the correct destination.

// InitialView.swift (replacing BaseView with MyCustomHomeView) case .loggedIn: if let paths = vm.navigationPaths, !paths.isEmpty, paths[0] == "home" { MyCustomHomeView(remainingPaths: Array(paths.dropFirst())) } else if let directPath = vm.currentPath { destinationView(for: directPath) } else { MyCustomHomeView(remainingPaths: nil) }

Testing Your Changes

  1. Run in Simulator: If the user is authenticated, the simulator should now show MyCustomHomeView.
  2. Deep Link Testing: Trigger a deep link such as myapp://home/profile. Your custom home view should automatically navigate to UserProfileView, thanks to the deep linking logic you replicated.
  3. Auth Flow Check: Sign out and back in to ensure that the authentication and navigation flows work as intended.