iOS in-depth
prerequisite
An extension to the URL class in the URL+DeeplinkExtension.swift file of the OpenContract repository adds custom properties to validate and process deep links and universal links. It checks various conditions that need to be met for a URL to be valid as a deep link and allows extraction of necessary information for routing purposes. The extension contains following properties:
- hasDeeplinkSchema: Check if URL Contains Deep Link Schema This property checks if the URL contains the required deep link schema by examining its query parameters for a specific schema (e.g., "navidapro"), which is essential for identifying the deep link, and returns a Boolean.
- valueForSchema: Extract Deep Link Schema and Parameters This property retrieves the value of the "schema" parameter from the URL, along with any additional query parameters. The schema identifies the type of deep link, and this method ensures the URL is correctly formatted by appending any extra parameters. It returns a String representing the full schema with appended query parameters, or nil if not found.
- isDeepLinkStartsWithSchemaNavidaPro: Check if URL Starts with "navidapro" Schema This property checks if the deep link URL starts with a specific scheme, typically used for universal links or custom URL schemes. In this case, it verifies whether the URL starts with the "navidapro" schema, and returns a Boolean.
- isDeeplink: Check if URL Has Matching Deep Link Scheme This property checks if the scheme of the URL matches a predefined scheme for deep links, ensuring that the URL belongs to the expected format for deep link processing and returns a Boolean.
- pluginId: Fetch Plugin ID from URL Host This property extracts the plugin ID from the URL host if the URL is a valid deep link. The pluginId is typically used to route users to specific content or functionality within the app. It returns a String with the pluginId or nil, if no plugin is found.
AppDelegate - Entry Point
The entry point when interacting with deep links / universal links is the AppDelegate class. It uses the DeepLinkValidator and DeepLinkManager instances to process and validate incoming links. These properties are essential for handling deep link navigation when a user interacts with a deep link or universal link. The AppDelegate class initializes the DeepLinkValidator and DeepLinkManager as follows:
- DeepLinkValidator: Responsible for validating and identifying the deep link from the incoming URL. It ensures that the link is properly formatted and contains the necessary parameters before navigating to the specified content.
- DeepLinkManager: Handles the actual navigation based on the validated deep link, ensuring the user is directed to the correct screen or content within the app.
Methods for Deep Link Handling
The AppDelegate class includes three methods to capture and handle incoming deep links:
- application(:open:options:): This method is called when the app is opened via a URL. It delegates the responsibility of handling the link to the handleUniversalLink(:) method.
- application(:continue:restorationHandler:): This method is invoked when the app is resumed from a background state via a universal link. It checks if the activity type is browsing a web page, extracts the URL, and calls handleUniversalLink(:).
- handleUniversalLink(_:): This is the main method for deep link processing. It asynchronously validates the received deep link using the DeepLinkValidator, and if the deep link is valid and the app has not exceeded the background time limit, it instructs the DeepLinkManager to navigate to the appropriate screen.
DeepLinkValidator Class
The DeepLinkValidator class is responsible for validating and identifying deep links from universal links, as well as managing routing logic for navigation within the app. It works in conjunction with authentication, content services, and master data management to ensure that deep links are correctly handled and routed to the appropriate screens.
Properties
- masterDataManager: Stores and manages the master data (e.g., plugin IDs, universal link data) needed for validation and routing.
- authManager: Handles user authentication, checking whether the user is logged in and whether specific actions (e.g., consent, login) are required.
- contentService: Used to fetch deep link data from a web service if it's not directly extractable from the universal link.
- deepLinkExemptionList: A lazily initialized list of deep links that are exempt from validation checks, populated from configuration.
Initialization
init(contentService: ContentServiceType,
masterDataManager: MasterDataPreferences,
authManager: OCAuthManagerProtocol)
- contentService: The service responsible for fetching deep links from the server.
- masterDataManager: A shared instance managing master data for the app.
- authManager: Protocol for managing user authentication, determining login status, etc.
Methods
- identifyDeepLinkFrom(universalLink:) This method is responsible for identifying a valid deep link from a universal link based on certain conditions. It checks for schema existence, the prefix of the link, or fetches the deep link from a web service if necessary.
- Parameters:
- universalLink: The universal link received by the app (of type URL).
- Functionality:
- If the universal link contains a valid schema, it assigns the schema value as the deep link.
- If the link starts with a specific schema (e.g., navidapro), it assigns the link directly as the deep link.
- If neither condition is met, the method asynchronously fetches the deep link using a web service.
- getDeepLinkFromUniversalLink(link:) This method makes an asynchronous web service call to fetch the valid deep link associated with a universal link.
- Parameters:
- link: The universal link string.
- Returns: A valid deep link string or a fallback error message if the web service call fails.
- navigationPathFrom(schema:) This method extracts the host and path from a URL schema and returns them as a string. It also appends any URL fragment to the path.
- Parameters:
- schema: A URL schema string.
- Returns: A string representing the host and path of the schema.
- isSuccessfullyNavigatedToDeepLink(_:) This method checks whether the app has successfully navigated to the target deep link by comparing its components (host, path, query parameters) with the current universal link.
- Parameters:
- deepLink: The deep link object that the app is attempting to navigate to.
- Returns: A Bool indicating whether the navigation was successful based on comparing the deep link's path and query parameters with the universal link.
- canOpenConsentPlugin(isConsentRequired:deepLink:) This method checks whether the user can open a consent screen based on their login status and whether consent is required.
- Parameters:
- isConsentRequired: A Bool indicating whether consent is required for the given deep link.
- Returns: A Bool value indicating whether the consent plugin can be opened.
- canOpenLoginPlugin(isPluginLoginRequired:deepLink:) This method determines whether the app should navigate to the login screen based on the user's login status and whether the plugin requires login.
- Parameters:
- isPluginLoginRequired: A Bool indicating whether login is required for the deep link.
- Returns: A Bool indicating whether the user should be navigated to the login screen.
- getConsentScreenIdentifier(from:) This method retrieves the appropriate consent screen identifier from a deep link, adjusting based on specific parameters (e.g., the goal type of a challenge).
- Parameters:
- deepLink: The deep link from which to extract the consent screen identifier.
- Returns: A String representing the consent screen identifier.
- isPluginIdValidForSelectedTenant(_:) This method checks whether a given plugin ID is valid for the selected tenant by comparing it against a list of available plugin IDs.
- Parameters:
- pluginId: The ID of the plugin being checked.
- Returns: A Bool indicating whether the plugin is valid for the current tenant.
- isDeepLinkInExemptionList(_:) This method checks whether a specific deep link is part of the exemption list, which bypasses certain validation checks.
- Parameters:
- deepLink: The deep link being checked.
- Returns: A Bool indicating whether the deep link is part of the exemption list.
DeepLinkManager class
The DeepLinkManager class is designed to handle deep link navigation within an application. It ensures that the app can parse deep links, validate them, and trigger the correct plugin or flow based on the deep link's contents.
Properties and Dependencies
- sceneCoordinator: Manages navigation within the app.
- plugins: Array of plugins registered to handle various deep link functionalities.
- features: Optional array for managing feature-based deep links.
- authManager: Handles authentication-related operations.
- userPreferences: Stores user-specific configurations.
- masterDataPreferences: Central repository for master data configurations.
- consentManager: Handles user consent for features.
- deepLinkValidator: Validates deep links based on app rules.
- urlOpener: Protocol for opening external URLs.
- analyticsManager: Configures analytics tracking.
Initializers
Convenience Initializer
Provides default dependencies like ContentService, OCMasterDataManager.shared, and OCAuthManager.shared. Calls the designated initializer with predefined or default dependencies.
Designated Initializer
Requires all properties to be explicitly provided: sceneCoordinator, plugins, features Core dependencies: authManager, userPreferences, masterDataPreferences Managers: consentManager, deepLinkValidator, urlOpener, analyticsManager.
Methods
1. Deep Link Navigation
func handleDeepLinkNavigation(for deepLinkString: String) To process and navigate based on a given deep link string.
Flow:
- Parses the deepLinkString into a URL.
- Attempts to create a DeepLink object.
- If successful, calls openPlugin(for:) to handle the deep link.
- If unsuccessful, directly opens the URL via urlOpener.openNavida(deepLinkString).
2. Deep Link Creation
func createDeepLink(url: URL) -> DeepLink? Constructs a DeepLink object from a URL.
Flow:
- Verifies the URL as a deep link, checks its validity, and ensures it's not in the exemption list.
- Extracts the plugin ID, path components (as sub-screens), and query parameters.
- Returns a DeepLinkModel containing these details.
3. Opening Plugins
func openPlugin(_ id: String, ...) and func openPlugin(for deepLink: DeepLink) Handles navigation to a specific plugin or feature within the app.
Flow:
- Validation:
- Ensures the plugin is valid for the current tenant.
- Determines if login or consent is required.
- Redirect:
- If login is needed, navigates to the login plugin.
- If consent is needed, navigates to the consent plugin.
- Final Navigation:
- Attempts to open the requested plugin.
- If unsuccessful, redirects to a home screen with an error message.
4. Handling Consent
&func showConsentPlugin(...) and func hasUserNotGivenConsent(for:) Manages user consent requirements for specific features or plugins.
Consent Flow:
- Constructs a deep link for the consent plugin, incorporating necessary query parameters.
- Opens the consent plugin via the start() method.
5. Post-Login Navigation
func handleNavigationAfterLogin(…) Manages navigation after a successful login.
Scenarios:
- First-Time Users: Directs to onboarding screens.
- Minor Users: Shows a "not old enough" message.
- Pending Consent: Navigates to consent flow if required.
- Biometric/Notification Setup: Guides the user through setting up biometric login or push notifications if needed.
- Default: Navigates to the home screen or deep link destination.
6. General Navigation Utilities
Functions:
- navigateToDeepLink
- prepareDeepLink
- shouldContinueDeepLinkNavigation
Handle deep link navigation and validate links.
Responsibilities:
- prepareDeepLink converts universal links into DeepLink objects.
- shouldContinueDeepLinkNavigation ensures app state is ready for navigation.
- navigateToDeepLink handles the actual navigation process.
7. Error Handling and Recovery
func launchHomeScreenToShowError(_ error: DeepLinkError) Redirects to the home screen and displays an appropriate error message when navigation fails.
8. Session and State Management
func clearUserSession() Clears user data and resets the app state, typically for scenarios requiring re-login or session invalidation.