iOS SDK
Integrate the WarpLink iOS SDK with Swift Package Manager to add deep linking, deferred deep links, and install attribution.
Requirements: iOS 15+, Swift 5.9+, Xcode 15+. Universal Links require a physical device. They do not work on the iOS Simulator.
Installation
- Go to File > Add Package Dependencies...
- Enter:
https://github.com/WarpLinkApp/warplink-ios-sdk - Select Up to Next Major Version and click Add Package
dependencies: [
.package(url: "https://github.com/WarpLinkApp/warplink-ios-sdk", from: "1.1.0")
]Configure Associated Domains
- In Xcode, select your app target > Signing & Capabilities
- Click + Capability > Associated Domains
- Add:
applinks:aplnk.to
Also enable Associated Domains in your App ID at developer.apple.com.
Serving links from a custom domain? Add an entry for it too (applinks:links.yourapp.com), then declare it to the SDK.
Initialize the SDK
Call configure() as early as possible in your app lifecycle. Pass an onLink callback and the SDK wires up cold start, warm start, and the deferred deep link check automatically. Your one callback receives every resolved link.
The credential below is an SDK key, created in the dashboard under API Keys > SDK key. An API key looks identical but cannot record installs: deep links still resolve, so the integration looks healthy while every attribution call is rejected.
import SwiftUI
import WarpLink
@main
struct MyApp: App {
init() {
WarpLink.configure(
apiKey: "wl_live_yoursdkkeyhere000000000000000000",
options: WarpLinkOptions(onLink: { result in
if case .success(let deepLink) = result, let deepLink {
// Fired for taps AND deferred installs. Check deepLink.isDeferred.
navigate(to: deepLink)
}
})
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}import UIKit
import WarpLink
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
WarpLink.configure(
apiKey: "wl_live_yoursdkkeyhere000000000000000000",
options: WarpLinkOptions(onLink: { result in
if case .success(let deepLink) = result, let deepLink {
navigate(to: deepLink)
}
})
)
return true
}
}Your onLink callback only fires for a real resolved link or an error. A deferred check that finds no match is not delivered to onLink, so the automatic sink stays quiet when there is nothing to route. Everything is dispatched on the main thread.
Deliver Incoming URLs
The SDK still needs the tapped URL from the OS. Pick one of these (no swizzling):
- Drop-in delegate: set your scene delegate class to
WarpLinkSceneDelegate(or app delegate toWarpLinkAppDelegate) and the SDK forwards Universal Links for you. - One-liner: if you keep your own delegate, forward the activity or URL:
// In your scene/app delegate
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
connectionOptions.userActivities.forEach { WarpLink.continue($0) }
connectionOptions.urlContexts.forEach { WarpLink.open($0.url) }
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
WarpLink.continue(userActivity)
}
func scene(_ scene: UIScene, openURLContexts contexts: Set<UIOpenURLContext>) {
contexts.forEach { WarpLink.open($0.url) }
}
That is the entire host integration. Resolution and the onLink dispatch are automatic.
Opt Out of Automatic Handling
Every piece is on by default. Disable what you want to drive yourself:
WarpLink.configure(
apiKey: "wl_live_yoursdkkeyhere000000000000000000",
options: WarpLinkOptions(
debugLogging: true, // [WarpLink] console logs
autoDeepLinkHandling: false, // you call WarpLink.handleDeepLink yourself
autoDeferredCheck: false // you call WarpLink.checkDeferredDeepLink yourself
)
)
Declare Your Link Domains
The SDK always recognizes aplnk.to. If your links are served from a custom domain, tell the SDK which domains are yours. Then a link on that domain resolves on the very first launch, with no network round trip.
This matters because the decision is synchronous. WarpLink.open(_:) returns a Bool the instant the OS hands over a URL, so it can only answer from what it already knows. A domain the SDK has not been told about is reported as not ours and handed back to your router, which is exactly what a fresh install looks like before the SDK has ever reached the server.
Declare them in code:
WarpLink.configure(
apiKey: "wl_live_yoursdkkeyhere000000000000000000",
options: WarpLinkOptions(
linkDomains: ["links.yourapp.com", "go.yourapp.com"],
onLink: { result in
if case .success(let deepLink) = result, let deepLink {
navigate(to: deepLink)
}
}
)
)
Or declare them with no code at all, in Info.plist:
<key>WarpLinkDomains</key>
<array>
<string>links.yourapp.com</string>
<string>go.yourapp.com</string>
</array>
Both forms are optional and both are additive. The SDK uses the union of aplnk.to, linkDomains, the WarpLinkDomains array, and the domains it learns from the server, so an app that declares nothing keeps working exactly as before.
Entries are normalized for you: whitespace is trimmed, the value is lowercased, and a full URL is reduced to its host, so https://Links.YourApp.com/ and links.yourapp.com mean the same thing. www. is left alone, because www.yourapp.com and yourapp.com are different hosts.
Handle Deep Links
Cold and warm start are handled for you once URLs reach the SDK. See Deep Links for the delegate options and the manual path.
Handle Deferred Deep Links
The deferred check fires automatically from configure() and delivers its result to onLink. See Deferred Deep Links for details and the manual path.
Test on a Physical Device
- Build and run on a physical device
- Open your test link in Safari (e.g.,
https://aplnk.to/abc123) - The app should open and trigger the deep link callback
- Check Xcode console for
[WarpLink]log messages
Universal Links do not work on the iOS Simulator. Always test on a physical device.
Next Steps
- Deep Links: Handle Universal Links for cold and warm start
- Deferred Deep Links: Preserve context through App Store installs
- Attribution: Understand match types and confidence scores
- API Reference: Full Swift API documentation