{"id":875,"date":"2026-04-22T09:00:00","date_gmt":"2026-04-22T14:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=875"},"modified":"2026-03-07T03:48:30","modified_gmt":"2026-03-07T08:48:30","slug":"sdk-swap-guide","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/sdk-swap-guide\/","title":{"rendered":"SDK Swap Guide: Replacing Your Deep Linking SDK"},"content":{"rendered":"\n<p>Swapping a deep linking SDK is the most engineering-intensive part of a platform migration. The SDK handles deep link reception, deferred deep linking, and often event tracking. Replacing it means changing how your app receives and processes links.<\/p>\n\n\n\n<p>This guide covers the technical steps for swapping deep linking SDKs in iOS and Android apps, with patterns that minimize risk.<\/p>\n\n\n\n<p>For the full migration guide, see <a href=\"https:\/\/tolinku.com\/blog\/migrating-to-tolinku\/\">Migrating to Tolinku from Branch, Firebase, and AppsFlyer<\/a>. For the migration checklist, see <a href=\"https:\/\/tolinku.com\/blog\/deep-linking-migration-checklist\/\">Deep Linking Migration Checklist: 30 Steps<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Abstraction Layer Approach<\/h2>\n\n\n\n<p>Before ripping out the old SDK, build an abstraction layer. This gives you a clean interface that your app code calls, with the implementation swappable behind it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Define the Interface<\/h3>\n\n\n\n<p><strong>iOS (Swift)<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-swift\">protocol DeepLinkHandler {\n    func handleURL(_ url: URL) -&gt; DeepLinkResult?\n    func handleDeferredLink(completion: @escaping (DeepLinkResult?) -&gt; Void)\n}\n\nstruct DeepLinkResult {\n    let path: String\n    let parameters: [String: String]\n    let isDeferred: Bool\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Android (Kotlin)<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">interface DeepLinkHandler {\n    fun handleUri(uri: Uri): DeepLinkResult?\n    fun handleDeferredLink(callback: (DeepLinkResult?) -&gt; Unit)\n}\n\ndata class DeepLinkResult(\n    val path: String,\n    val parameters: Map&lt;String, String&gt;,\n    val isDeferred: Boolean\n)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Wrap the Old SDK<\/h3>\n\n\n\n<p>Create an implementation using your current SDK:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ iOS: Branch implementation (temporary)\nclass BranchDeepLinkHandler: DeepLinkHandler {\n    func handleURL(_ url: URL) -&gt; DeepLinkResult? {\n        \/\/ Bridge Branch&#39;s data format to your DeepLinkResult\n        \/\/ This wraps existing Branch logic\n    }\n\n    func handleDeferredLink(completion: @escaping (DeepLinkResult?) -&gt; Void) {\n        \/\/ Bridge Branch&#39;s deferred link callback\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Update App Code<\/h3>\n\n\n\n<p>Change all deep link handling in your app to use the protocol:<\/p>\n\n\n\n<pre><code class=\"language-swift\">class AppRouter {\n    let deepLinkHandler: DeepLinkHandler\n\n    init(handler: DeepLinkHandler) {\n        self.deepLinkHandler = handler\n    }\n\n    func handleIncomingURL(_ url: URL) {\n        guard let result = deepLinkHandler.handleURL(url) else { return }\n        navigate(to: result)\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>Now your app code is decoupled from any specific SDK. Swapping SDKs means creating a new <code>DeepLinkHandler<\/code> implementation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Remove the Old SDK<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">iOS Removal<\/h3>\n\n\n\n<p><strong>CocoaPods<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-ruby\"># Remove from Podfile\n# pod &#39;Branch&#39;  # or pod &#39;AppsFlyerFramework&#39;, pod &#39;FirebaseDynamicLinks&#39;\n<\/code><\/pre>\n\n\n\n<p>Run <code>pod install<\/code>.<\/p>\n\n\n\n<p><strong>Swift Package Manager<\/strong>:\nRemove the package dependency in Xcode (File \u2192 Swift Packages \u2192 Remove).<\/p>\n\n\n\n<p><strong>Manual removal<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Delete the framework files<\/li>\n<li>Remove from Build Phases \u2192 Link Binary With Libraries<\/li>\n<li>Remove any related build flags<\/li>\n<\/ul>\n\n\n\n<p>After removing the dependency, clean the build folder (Cmd+Shift+K in Xcode) and search for any remaining imports:<\/p>\n\n\n\n<pre><code class=\"language-bash\">grep -r &quot;import Branch&quot; .\ngrep -r &quot;import AppsFlyerLib&quot; .\ngrep -r &quot;import FirebaseDynamicLinks&quot; .\n<\/code><\/pre>\n\n\n\n<p>Remove all found references.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Android Removal<\/h3>\n\n\n\n<p>Remove from <code>build.gradle<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-groovy\">\/\/ Remove these lines\n\/\/ implementation &#39;io.branch.sdk.android:library:5.+&#39;\n\/\/ implementation &#39;com.appsflyer:af-android-sdk:6.+&#39;\n\/\/ implementation &#39;com.google.firebase:firebase-dynamic-links:21.+&#39;\n<\/code><\/pre>\n\n\n\n<p>Sync the project and search for remaining imports:<\/p>\n\n\n\n<pre><code class=\"language-bash\">grep -r &quot;import io.branch&quot; .\ngrep -r &quot;import com.appsflyer&quot; .\ngrep -r &quot;import com.google.firebase.dynamiclinks&quot; .\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Cleanup Checklist<\/h3>\n\n\n\n<p>After removing the SDK:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Remove initialization code from AppDelegate\/Application class<\/li>\n<li>Remove delegate\/listener implementations<\/li>\n<li>Remove event tracking calls<\/li>\n<li>Remove link creation calls<\/li>\n<li>Remove SDK-specific configuration from Info.plist \/ AndroidManifest.xml<\/li>\n<li>Remove SDK-specific build scripts or plugins<\/li>\n<li>Remove proguard rules for the old SDK (Android)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Add the New SDK<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Tolinku SDK Integration<\/h3>\n\n\n\n<p><strong>iOS (Swift Package Manager)<\/strong>:\nAdd the TolinkuSDK package.<\/p>\n\n\n\n<p><strong>Associated Domains<\/strong>:\nAdd your Tolinku domain to the Associated Domains entitlement:<\/p>\n\n\n\n<pre><code>applinks:go.yourapp.com\n<\/code><\/pre>\n\n\n\n<p><strong>SceneDelegate<\/strong> (or AppDelegate for older apps):<\/p>\n\n\n\n<pre><code class=\"language-swift\">func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {\n    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,\n          let url = userActivity.webpageURL else { return }\n\n    let handler = TolinkuDeepLinkHandler()\n    if let result = handler.handleURL(url) {\n        AppRouter.shared.navigate(to: result)\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Android<\/strong>:\nAdd the dependency to <code>build.gradle<\/code>.<\/p>\n\n\n\n<p><strong>AndroidManifest.xml<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;activity android:name=&quot;.MainActivity&quot;&gt;\n    &lt;intent-filter android:autoVerify=&quot;true&quot;&gt;\n        &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n        &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n        &lt;data\n            android:scheme=&quot;https&quot;\n            android:host=&quot;go.yourapp.com&quot; \/&gt;\n    &lt;\/intent-filter&gt;\n&lt;\/activity&gt;\n<\/code><\/pre>\n\n\n\n<p><strong>MainActivity<\/strong>:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    handleIntent(intent)\n}\n\noverride fun onNewIntent(intent: Intent) {\n    super.onNewIntent(intent)\n    handleIntent(intent)\n}\n\nprivate fun handleIntent(intent: Intent) {\n    val uri = intent.data ?: return\n    val handler = TolinkuDeepLinkHandler()\n    handler.handleUri(uri)?.let { result -&gt;\n        AppRouter.navigate(result)\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Implement the New Handler<\/h2>\n\n\n\n<p>Create the Tolinku implementation of your abstraction:<\/p>\n\n\n\n<pre><code class=\"language-swift\">class TolinkuDeepLinkHandler: DeepLinkHandler {\n    func handleURL(_ url: URL) -&gt; DeepLinkResult? {\n        let components = URLComponents(url: url, resolvingAgainstBaseURL: false)\n        let path = url.path\n        let params = components?.queryItems?.reduce(into: [String: String]()) { dict, item in\n            dict[item.name] = item.value\n        } ?? [:]\n\n        return DeepLinkResult(\n            path: path,\n            parameters: params,\n            isDeferred: false\n        )\n    }\n\n    func handleDeferredLink(completion: @escaping (DeepLinkResult?) -&gt; Void) {\n        \/\/ Use Tolinku SDK to check for deferred deep link data\n        Tolinku.shared.checkForDeferredLink { url in\n            guard let url = url else {\n                completion(nil)\n                return\n            }\n            completion(self.handleURL(url))\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>The key difference from most old SDKs: Tolinku passes deep link data through standard URLs, so parsing is just URL component extraction. No proprietary data dictionaries to decode.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Handle Edge Cases<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Multiple URL Schemes<\/h3>\n\n\n\n<p>If your app registered custom URL schemes (e.g., <code>yourapp:\/\/product\/123<\/code>) in addition to Universal Links\/App Links, decide whether to keep them. Custom URL schemes are less secure and less reliable than Universal Links\/App Links, but some older integrations may depend on them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Deep Link Routing<\/h3>\n\n\n\n<p>Map your old routing patterns to the new URL structure:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Old SDK Route<\/th>\n<th>New URL Route<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td><code>Branch.data[&quot;product_id&quot;] = &quot;123&quot;<\/code><\/td>\n<td><code>\/product\/123<\/code> or <code>?product_id=123<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>AppsFlyer.deepLinkValue = &quot;promo&quot;<\/code><\/td>\n<td><code>\/promo<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>Firebase.link = &quot;https:\/\/yourapp.com\/sale&quot;<\/code><\/td>\n<td><code>\/sale<\/code><\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Event Tracking<\/h3>\n\n\n\n<p>If you tracked events through the old deep linking SDK, move event tracking to your analytics SDK or Tolinku&#39;s analytics. Don&#39;t mix deep link routing with event tracking responsibilities.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing the Swap<\/h2>\n\n\n\n<p>Test these scenarios on both iOS and Android:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Cold start with link<\/strong>: App is not running; user taps a link \u2192 app launches and navigates to the correct screen<\/li>\n<li><strong>Warm start with link<\/strong>: App is backgrounded; user taps a link \u2192 app comes to foreground and navigates<\/li>\n<li><strong>Deferred deep link<\/strong>: App not installed; user taps link \u2192 installs \u2192 opens \u2192 sees correct content<\/li>\n<li><strong>No-op link<\/strong>: User taps a link that doesn&#39;t match any route \u2192 app handles gracefully (home screen, not crash)<\/li>\n<li><strong>Malformed link<\/strong>: Link with missing or invalid parameters \u2192 app handles gracefully<\/li>\n<\/ol>\n\n\n\n<p>For a comprehensive comparison of deep linking SDKs, see <a href=\"https:\/\/tolinku.com\/blog\/deferred-deep-linking-sdks-comparison\/\">Deferred Deep Linking SDK Comparison<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Swap your deep linking SDK with minimal disruption. Step-by-step guide for replacing Branch, AppsFlyer, or Firebase SDKs in iOS and Android apps.<\/p>\n","protected":false},"author":2,"featured_media":874,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"SDK Swap Guide: Replacing Your Deep Linking SDK","rank_math_description":"Swap your deep linking SDK with minimal disruption. Step-by-step guide for replacing Branch, AppsFlyer, or Firebase SDKs in iOS and Android apps.","rank_math_focus_keyword":"deep linking SDK swap","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-sdk-swap-guide.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-sdk-swap-guide.png","footnotes":""},"categories":[17],"tags":[25,20,24,34,52,30,31],"class_list":["post-875","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-comparisons","tag-android","tag-deep-linking","tag-ios","tag-kotlin","tag-migration","tag-sdks","tag-swift"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/875","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=875"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/875\/revisions"}],"predecessor-version":[{"id":2518,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/875\/revisions\/2518"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/874"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=875"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=875"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=875"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}