LimeLink iOS SDK

Target SDK Version: 0.3.5 | 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.

Version 0.3.5 is an immutable binary patch over 0.3.4. Its public runtime and API behavior are unchanged, and Objective-C and Objective-C++ module imports have been verified.

View previous iOS SDK releases


Requirements

ItemMinimum
iOS Deployment Target12.0
Swift5.0
Xcode14.0+
CocoaPods1.11.0+

Register the iOS Application in a LimeLink Project, create a dedicated SDK credential that is not reused by backend automation, and enable Associated Domains for the app target. The API key is included in app configuration and can be extracted from a shipped binary; keep it out of source control and logs, monitor its use, and rotate it after suspected exposure.

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.5, or add it to Package.swift:

dependencies: [
    .package(
        url: "https://github.com/hellovelop/limelink-ios-sdk-binary.git",
        from: "0.3.5"
    )
]

Then add the product to your app target:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "LimelinkIOSSDK", package: "limelink-ios-sdk-binary")
    ]
)

CocoaPods

Version 0.3.5 supports all three Podfile integration modes.

Without use_frameworks!

platform :ios, '12.0'

target 'YourApp' do
  pod 'LimelinkIOSSDK', '~> 0.3.5'
end

Dynamic Framework Linkage

platform :ios, '12.0'
use_frameworks!

target 'YourApp' do
  pod 'LimelinkIOSSDK', '~> 0.3.5'
end

Static CocoaPods Linkage Mode

platform :ios, '12.0'
use_frameworks! :linkage => :static

target 'YourApp' do
  pod 'LimelinkIOSSDK', '~> 0.3.5'
end

The CocoaPods static-linkage setting controls CocoaPods integration behavior; it does not convert the shipped dynamic XCFramework into a static binary.

Run pod install, then open the generated .xcworkspace.

Privacy Manifest

Version 0.3.5 includes an SDK-owned PrivacyInfo.xcprivacy in every XCFramework slice. The manifest declares the SDK's required-reason API use and deferred-link data practices, with tracking disabled.

The SDK manifest covers only LimeLink SDK behavior. Your app remains responsible for declaring its own required-reason APIs, collected data, tracking, and third-party SDK usage in App Store Connect and any app-owned privacy manifest.

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.

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)
        }
    }
}
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, manual completions, initialization errors, and pending replay are delivered on the main thread.

Result fields

FieldDescription
originalUrlOriginal URL, when available
resolvedUriDestination URI resolved by LimeLink
queryParamsQuery parameters from the original URL
pathParamsParsed mainPath and optional subPath
isDeferredtrue for an install-time deferred result

With deferredDeeplinkEnabled: true, SDK initialization automatically attempts deferred resolution for an eligible first launch.

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.5 consumer API.

Public API

APIPurpose
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.5

Troubleshooting

SDK not initialized

Call LimeLinkSDK.initialize(config:) before forwarding links or requesting deferred resolution.

  1. Verify Associated Domains and the AASA file.
  2. Reinstall the app after changing Associated Domains.
  3. Test by tapping a link from another app; typing it into Safari does not trigger a Universal Link.
  4. Confirm AppDelegate or SceneDelegate forwards the URL.

Deferred result is not delivered

  1. Confirm deferred handling is enabled.
  2. Confirm the app bundle identifier matches the registered LimeLink application.
  3. Keep the listener alive and register it early.
  4. Treat onDeferredDeepLinkNotFound() as a successful resolution with no match.
  5. 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.5 or later. Confirm the Podfile uses one of the supported linkage modes above, then rerun pod install.