LimeLink iOS SDK
Target SDK Version: 0.3.3 | iOS 12.0+ | Swift 5.0 | Xcode 14.0+
LimeLink iOS SDK provides Universal Link resolution, deferred deep links, and link event tracking through a binary XCFramework.
View previous iOS SDK releases
Requirements
| Item | Minimum |
|---|---|
| iOS Deployment Target | 12.0 |
| Swift | 5.0 |
| Xcode | 14.0+ |
| CocoaPods | 1.11.0+ |
Register the app in the LimeLink console, obtain an API key, and enable Associated Domains for the app target.
Binary Installation
Consumer releases are distributed as verified XCFramework binaries. The SDK source repository and manual source copying are not supported installation routes.
Swift Package Manager
In Xcode, select File > Add Package Dependencies and enter:
https://github.com/hellovelop/limelink-ios-sdk-binary.git
Select version 0.3.3, or add it to Package.swift:
dependencies: [
.package(
url: "https://github.com/hellovelop/limelink-ios-sdk-binary.git",
from: "0.3.3"
)
]
Then add the product to your app target:
.target(
name: "YourApp",
dependencies: [
.product(name: "LimelinkIOSSDK", package: "limelink-ios-sdk-binary")
]
)
CocoaPods
platform :ios, '12.0'
use_frameworks!
target 'YourApp' do
pod 'LimelinkIOSSDK', '~> 0.3.3'
end
Run pod install, then open the generated .xcworkspace.
Initialize the SDK
Initialize once during application launch:
import UIKit
import LimelinkIOSSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
_ = DeepLinkManager.shared
let config = LimeLinkConfig(
apiKey: "YOUR_API_KEY",
loggingEnabled: false,
deferredDeeplinkEnabled: true
)
LimeLinkSDK.initialize(config: config)
return true
}
}
Register a long-lived listener before initialization when possible so it can receive the automatic launch outcome immediately.
Configure Universal Links
In Signing & Capabilities > Associated Domains, add:
applinks:limelink.org
applinks:*.limelink.org
For a custom domain, add its applinks: entry and provide a valid Apple App Site Association file.
AppDelegate
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
LimeLinkSDK.shared.handleUniversalLink(url)
return true
}
SceneDelegate
import LimelinkIOSSDK
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
if let activity = connectionOptions.userActivities.first,
activity.activityType == NSUserActivityTypeBrowsingWeb,
let url = activity.webpageURL {
LimeLinkSDK.shared.handleUniversalLink(url)
}
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL {
LimeLinkSDK.shared.handleUniversalLink(url)
}
}
func scene(_ scene: UIScene, openURLContexts contexts: Set<UIOpenURLContext>) {
if let url = contexts.first?.url {
LimeLinkSDK.shared.handleUniversalLink(url)
}
}
}
Receive Link Results
import LimelinkIOSSDK
final class DeepLinkManager: NSObject, LimeLinkListener {
static let shared = DeepLinkManager()
private override init() {
super.init()
LimeLinkSDK.shared.addLinkListener(self)
}
func onDeeplinkReceived(result: LimeLinkResult) {
guard let destination = result.resolvedUri else { return }
// Route destination in your app.
}
func onDeferredDeepLinkNotFound() {
// Normal outcome: continue the default launch flow.
}
func onDeeplinkError(error: LimeLinkError) {
print("LimeLink error [\(error.code)]: \(error.message)")
}
}
Listeners are weakly retained. Keep an app-wide listener alive, for example with static let shared. Public listener callbacks are delivered on the main thread.
Result fields
| Field | Description |
|---|---|
originalUrl | Original URL, when available |
resolvedUri | Destination URI resolved by LimeLink |
queryParams | Query parameters from the original URL |
pathParams | Parsed mainPath and optional subPath |
isDeferred | true for an install-time deferred result |
Deferred Deep Links
With deferredDeeplinkEnabled: true, SDK initialization automatically attempts deferred resolution for an eligible first launch.
- A match calls
onDeeplinkReceivedwithisDeferred == true. - A normal no-match calls
onDeferredDeepLinkNotFound(). - If resolution completes before listener registration, the undelivered launch outcome is replayed once to the first listener.
- Concurrent automatic and manual checks share one request and notify listeners once.
- Transport, HTTP, or response-validation failures remain eligible for a later automatic retry.
Manual usage:
LimeLinkSDK.shared.handleDeferredDeepLink { result, error in
if let result {
navigateTo(result.resolvedUri)
} else if let error {
print(error.message)
} else {
// Normal no-match outcome.
}
}
Deferred matching uses the app bundle identifier and bounded device context. Advertising identifiers are not required, and raw IP is not included in the SDK request body.
Stats Tracking
Resolved Universal Links are tracked automatically. To track a link explicitly:
if let url = URL(string: resolvedUri) {
LimeLinkSDK.shared.trackLinkStatus(url: url)
}
Objective-C
Import the generated binary module interface:
#import <LimelinkIOSSDK/LimelinkIOSSDK-Swift.h>
Initialize and handle a Universal Link through the public SDK facade:
LimeLinkConfig *config = [[LimeLinkConfig alloc] initWithApiKey:@"YOUR_API_KEY"
baseUrl:@"https://limelink.org/"
loggingEnabled:NO
deferredDeeplinkEnabled:YES];
[LimeLinkSDK initializeWithConfig:config];
[[LimeLinkSDK shared] handleUniversalLink:url];
Use LimeLinkListener for results. The removed UniversalLinkHandlerBridge is not part of the 0.3.3 consumer API.
Public API
| API | Purpose |
|---|---|
LimeLinkSDK.initialize(config:) | Initialize once |
addLinkListener(_:) | Register result callbacks |
removeLinkListener(_:) | Remove callbacks |
handleUniversalLink(_:) | Resolve a Universal Link |
handleDeferredDeepLink(completion:) | Explicitly check deferred resolution |
trackLinkStatus(url:) | Explicitly send link stats |
Use the LimeLinkSDK facade and public configuration/result models. Resolver services, URL parsers, request models, and state management are internal implementation details.
Migrating to 0.3.3
- Replace source or manual installation with the binary SPM repository or binary CocoaPod.
- Remove
UniversalLinkHandlerBridgeand direct service/handler calls. - Route Universal Links and stats through
LimeLinkSDK.shared. - Implement
onDeferredDeepLinkNotFound()if the app needs an explicit normal no-match signal. - Register and retain a listener early to handle automatic launch outcomes.
- Recompile Objective-C consumers against the generated interface in the 0.3.3 binary.
Troubleshooting
SDK not initialized
Call LimeLinkSDK.initialize(config:) before forwarding links or requesting deferred resolution.
Universal Link is not delivered
- Verify Associated Domains and the AASA file.
- Reinstall the app after changing Associated Domains.
- Test by tapping a link from another app; typing it into Safari does not trigger a Universal Link.
- Confirm AppDelegate or SceneDelegate forwards the URL.
Deferred result is not delivered
- Confirm deferred handling is enabled.
- Confirm the app bundle identifier matches the registered LimeLink application.
- Keep the listener alive and register it early.
- Treat
onDeferredDeepLinkNotFound()as a successful resolution with no match. - Retry transport failures on a later launch rather than treating them as no-match.
CocoaPods cannot find the module
Open the .xcworkspace, clean Derived Data, and confirm the selected pod version is 0.3.3 or later.