iOS SDK Integration (Swift)
This tutorial walks you through integrating the OpenLynk SDK into a native iOS app using Swift. By the end, your app will handle Universal Links, create shareable deep links, and restore deferred deep links after install.
Prerequisites
- Completed the Quick Start tutorial (you have an app with an API key)
- An iOS project in Xcode (Swift, iOS 13+)
- Your App ID and API Key from the OpenLynk dashboard
- A physical iOS device for testing (Universal Links do not work in the Simulator)
Step 1: Add the SDK
Add the OpenlynkSDK.swift file directly to your Xcode project. No package manager is required -- the SDK is a single self-contained file with no external dependencies.
- Download
OpenlynkSDK.swiftfrom the OpenLynk dashboard or repository - Drag it into your Xcode project navigator
- Make sure "Copy items if needed" is checked and the file is added to your app target
Step 2: Get Your Credentials
In the OpenLynk dashboard, navigate to your app's settings page. You need two values:
| Credential | Where to find it |
|---|---|
| App ID | App settings page, displayed at the top |
| API Key | App settings page, under "SDK API Key". Click Generate if you have not created one yet. |
Step 3: Initialize the SDK in SceneDelegate
The recommended approach is to initialize the SDK in your SceneDelegate. This gives you access to the connection options for handling cold-start Universal Links.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var openlynkSDK: OpenlynkSDK!
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
openlynkSDK = OpenlynkSDK(
appId: "YOUR_APP_ID",
apiKey: "YOUR_API_KEY",
config: OpenlynkSDKConfig(
autoRestoreOnInit: true,
userEmailProvider: { callback in
let email = AuthManager.shared.currentUserEmail
callback(email)
},
onRestoredLinks: { [weak self] links in
for link in links {
let path = link.destinationPath ?? link.destinationUrl
let params = link.parameters ?? link.metadata
self?.navigateTo(path: path, params: params)
}
},
onDeepLink: { [weak self] parsed in
self?.navigateTo(
path: parsed.destinationPath,
params: parsed.parameters
)
}
)
)
openlynkSDK.initSDK()
// Handle link if app was launched from a Universal Link
if let urlContext = connectionOptions.urlContexts.first {
openlynkSDK.handleIncomingURL(urlContext.url)
}
if let userActivity = connectionOptions.userActivities.first,
userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL {
openlynkSDK.handleIncomingURL(url)
}
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UINavigationController(
rootViewController: HomeViewController()
)
self.window = window
window.makeKeyAndVisible()
}
private func navigateTo(path: String, params: [String: Any]) {
guard let nav = window?.rootViewController
as? UINavigationController else { return }
if path.hasPrefix("/product/") {
let productId = String(path.split(separator: "/").last ?? "")
let vc = ProductViewController(
productId: productId, sdk: openlynkSDK)
nav.pushViewController(vc, animated: true)
} else if path.hasPrefix("/profile/") {
let userId = String(path.split(separator: "/").last ?? "")
let vc = ProfileViewController(userId: userId)
nav.pushViewController(vc, animated: true)
}
}
}
Replace YOUR_APP_ID and YOUR_API_KEY with the real values from your dashboard. The userEmailProvider is optional but improves deferred deep link matching accuracy. If you do not have user authentication, you can omit it.
What Happens on initSDK()
When you call openlynkSDK.initSDK(), the SDK:
- Reports an install heartbeat (throttled to once per 24 hours)
- If
autoRestoreOnInitistrue, calls the restore API to check for pending deferred deep links - Calls
onRestoredLinksif any pending links are found
Unlike the Flutter SDK, the iOS SDK does not start an automatic link listener. You must call handleIncomingURL(_:) from your SceneDelegate (or AppDelegate) when a Universal Link arrives.
Step 4: Handle Universal Links
Add the following methods to your SceneDelegate to handle Universal Links when the app is already running:
// Universal Link -- app already running, opened via URL
func scene(_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
openlynkSDK.handleIncomingURL(url)
}
// Universal Link -- continue user activity
func scene(_ scene: UIScene,
continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return }
openlynkSDK.handleIncomingURL(url)
}
There are three scenarios for Universal Link arrival:
| Scenario | Method called |
|---|---|
| App not running (cold start) | scene(_:willConnectTo:options:) via connectionOptions |
| App in background | scene(_:continue:) |
| App opened via custom URL scheme | scene(_:openURLContexts:) |
AppDelegate Alternative
If your app does not use SceneDelegate, initialize the SDK in AppDelegate instead:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var openlynkSDK: OpenlynkSDK!
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
openlynkSDK = OpenlynkSDK(
appId: "YOUR_APP_ID",
apiKey: "YOUR_API_KEY",
config: OpenlynkSDKConfig(
autoRestoreOnInit: true,
onRestoredLinks: { links in /* navigate */ },
onDeepLink: { parsed in /* navigate */ }
)
)
openlynkSDK.initSDK()
return true
}
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return false }
openlynkSDK.handleIncomingURL(url)
return true
}
}
Step 5: Add Associated Domains
For Universal Links to work, you must configure Associated Domains in Xcode:
- Select your app target in Xcode
- Go to Signing & Capabilities
- Click + Capability and add Associated Domains
- Add the entry:
applinks:YOUR_APP_SLUG.openlynk.to
Replace YOUR_APP_SLUG with your app's slug from the dashboard (e.g. applinks:myshop.openlynk.to).
Verify Your Configuration
Ensure the following match between Xcode and the OpenLynk dashboard:
- Bundle ID in Xcode matches the iOS Bundle ID in the dashboard
- Team ID in Xcode matches the iOS Team ID in the dashboard
OpenLynk automatically serves the AASA (Apple App Site Association) file at:
https://YOUR_APP_SLUG.openlynk.to/.well-known/apple-app-site-association
Visit this URL in a browser to verify it contains your appID in the format {TeamID}.{BundleID}.
If you change your bundle ID or team ID in the dashboard, it may take up to 24 hours for Apple's CDN to pick up the updated AASA file. During development, you can delete and reinstall the app to force a refresh.
Step 6: Create a Share Button
Add a share button to a view controller that creates a deep link and presents the share sheet:
import UIKit
class ProductViewController: UIViewController {
private let productId: String
private let sdk: OpenlynkSDK
init(productId: String, sdk: OpenlynkSDK) {
self.productId = productId
self.sdk = sdk
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError() }
override func viewDidLoad() {
super.viewDidLoad()
title = "Product \(productId)"
view.backgroundColor = .systemBackground
navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .action,
target: self,
action: #selector(shareTapped)
)
}
@objc private func shareTapped() {
sdk.createLink(
destination: "/product/\(productId)",
metadata: [
"product_id": productId,
"utm_source": "share",
"utm_medium": "ios_app"
]
) { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let link):
let activityVC = UIActivityViewController(
activityItems: [
"Check out this product!",
link.url
],
applicationActivities: nil
)
self?.present(activityVC, animated: true)
case .failure(let error):
let alert = UIAlertController(
title: "Error",
message: "Could not create share link: "
+ "\(error.localizedDescription)",
preferredStyle: .alert
)
alert.addAction(
UIAlertAction(title: "OK", style: .default))
self?.present(alert, animated: true)
}
}
}
}
}
The createLink call is asynchronous. The completion handler returns a Result type with either a CreatedLink (containing url, id, slug) or an error.
Step 7: Test on a Physical Device
Universal Links do not work in the iOS Simulator. You must test on a real device.
Verify Universal Links
- Install your app on the device via Xcode
- Open the Notes app on the device
- Type or paste your OpenLynk link URL (e.g.
https://myshop.openlynk.to/1709876543-x7k9m2) - Long-press the link -- you should see an option to "Open in [Your App]"
- Tap the link normally -- your app should open and
onDeepLinkshould fire
If the link opens in Safari instead of your app, try these steps:
- Delete the app and reinstall it (iOS caches AASA files on install)
- Verify the AASA URL is accessible in a browser
- Make sure you are not long-pressing and choosing "Open in Safari" -- just tap normally
Test Deferred Deep Linking
- Uninstall the app from the device
- Open your link in Safari on the device
- You will be redirected to the App Store (or your web fallback)
- Reinstall the app via Xcode
- Launch the app --
onRestoredLinksshould fire with the original link data
Exit Checklist
-
OpenlynkSDK.swiftadded to the Xcode project - SDK initialized in
SceneDelegate(orAppDelegate) - Universal Link handling implemented in all three scene methods
- Associated Domains capability added with
applinks:entry - Bundle ID and Team ID match between Xcode and the dashboard
- Share button creates links via
createLink - Deep linking tested on a physical device
- Deferred deep linking tested after uninstall and reinstall
What's Next?
- iOS SDK Reference -- Full API reference with all methods, configuration options, and data types.
- Handle Deep Links -- Advanced routing patterns and edge case handling.
- Push Notifications -- Register devices and send targeted push messages.
- Custom Domains -- Serve links from your own domain.