StarterAppKit comes pre-configured with deep linking capabilities. To leverage this functionality for your own app, follow the steps below.
Update Constants.swift
In your Constants.swift
, locate the deepLinkConfig
and update the values to match your app’s details:
static let deepLinkConfig = DeepLinkConfiguration( customURLScheme: "yourscheme", // Your custom URL scheme (e.g., "myapp") universalDomain: "yourdomain.com", // Your website domain paths: AppDeepLink.allCases, // No need to change this bundleIdentifier: "your.bundle.id" // Your app's bundle identifier )
info.plist
Open your info.plist
file and adjust the URL types
:
Item 0
to match your customURLScheme
in Constants.swift
.URL identifier
to match your universalDomain
in Constants.swift
.Adding custom deep link paths allows you to navigate users to specific content or features within your app.
AppDeepLink
Open your AppDeepLink
enum and add a new case for your custom path. For example, products:
public enum AppDeepLink: String, DeepLinkPath, CaseIterable { case home = "home" case profile = "profile" case settings = "settings" // Add your new path here case products = "products" public var requiredParameters: Set<String> { switch self { case .resetPassword, .verifyEmail, .confirmAuth: return ["token"] // Add required parameters for your new path case .products: return ["id"] case .home, .settings, .profile: return [] } } public var requiresAuth: Bool { switch self { case .resetPassword, .verifyEmail, .confirmAuth: return false // Specify if your new path requires authentication case .products: return true case .home, .settings, .profile: return true } } }
InitialView.swift
, map the new path to a destination view:@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)) // Add your new path case here case AppDeepLink.products.rawValue: ProductView(productId: vm.parameters["id"] ?? "") default: EmptyView() } }
Deep links can contain multiple paths, allowing for nested navigation. For instance, a single-path deeplink like myapp://profile
opens ProfileView
directly, but a multi-path deeplink such as myapp://home/profile
will first open the HomeView
and then navigate to ProfileView
.
When a deeplink has multiple paths, the app needs to navigate through them in sequence. Below is an example of how to manage this scenario.
At the top of your view, define a computed property to get the next path from the remaining paths:
var nextDestination: (path: String, remaining: [String])? { guard let paths = remainingPaths, !paths.isEmpty else { return nil } let next = (paths[0], Array(paths.dropFirst())) return next }
Add a state variable to indicate when navigation should occur:
@State private var shouldNavigate = false
When the view appears, if there is a next destination, set shouldNavigate
to true:
.onAppear { if nextDestination != nil { shouldNavigate = true } } .background( Group { if let next = nextDestination { CustomNavigationLink( shouldNavigate: $shouldNavigate, destination: { switch next.path { case "profile": return AnyView( UserProfileView( vm: UserProfileViewModel(), remainingPaths: next.remaining ) ) default: return nil } }, label: { EmptyView() } ) } } )
Testing ensures your deep links work as intended before releasing your app to users. Here are a few methods and tips for testing:
yourscheme://app/home
or yourscheme://app/home/profile
).xcrun simctl openurl
command on macOS. For example:xcrun simctl openurl booted yourscheme://app/products?id=123
This will open the currently running simulator and launch the deeplink.
yourdomain.com
), navigate to a URL in Safari on a device that matches your universal domain and path, such as https://yourdomain.com/products?id=123
. Ensure that the app opens and navigates correctly.