iOS SDK
The Ferry iOS SDK supports iOS 15 and later with Swift 5.9 or later. It has no third-party runtime dependencies and accepts only a project pk_ public key.
Add the package
In Xcode, choose File → Add Package Dependencies, enter https://github.com/FerryLink/ferry-ios.git, and add the Ferry library to the app target.
For a Package.swift consumer:
dependencies: [ .package(url: "https://github.com/FerryLink/ferry-ios.git", from: "0.1.0")]Or install the Ferry CocoaPod:
pod 'Ferry', '~> 0.1.0'1. Configure at launch
configure records the key. It does not read device signals or start matching.
SwiftUI
import SwiftUIimport Ferry
@mainstruct MyApp: App { init() { Ferry.configure(publicKey: "pk_test_xxx") }
var body: some Scene { WindowGroup { ContentView() } }}UIKit
func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Ferry.configure(publicKey: "pk_test_xxx") return true}2. Register one link handler
Register onLink during launch. This callback receives an installed-app universal link and a deferred first-install match.
Ferry.onLink { link in switch link.data["screen"] { case "product": router.showProduct(id: link.data["product_id"]) case "cart": router.showCart() default: router.showHome() }}Ferry calls the handler on the main queue. Registering it starts the deferred first-open flow. A link that resolves before registration is buffered and delivered afterward.
3. Forward universal links
SwiftUI
ContentView() .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { Ferry.handle($0) } .onOpenURL { Ferry.handle($0) }UIKit
func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { Ferry.handle(userActivity)}
func application( _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { Ferry.handle(url)}4. Add Associated Domains
Enable Associated Domains for the app ID and app target. Add one applinks: entry for every Ferry hostname used by the project.
<key>com.apple.developer.associated-domains</key><array> <string>applinks:acme.feryl.io</string> <string>applinks:go.acme.com</string></array>The hostname must serve an Apple App Site Association file containing the app’s TEAMID.bundle.id. Ferry publishes the correct file from the project settings for hostnames assigned to that project.
5. Track attributed events
Ferry.track(.login)Ferry.track(.signup, customerID: "customer_123")Ferry.track( .purchase( transactionId: "order_123", value: 29.99, currency: "USD", productId: "pro_monthly" ), customerID: "customer_123")Observe the first installation
Use onInstallation when the app needs to distinguish attributed and organic installations, including a repeated first-open result:
Ferry.onInstallation { result in analytics.note( result.attribution, isNew: result.isNewInstallation )}An organic first open has no link to route, so it appears here rather than in onLink.
App Clips
Pass a shared app group to the full app and configure Ferry in the App Clip target with the same key and group:
// Full appFerry.configure( publicKey: "pk_test_xxx", options: FerryOptions( appGroupIdentifier: "group.com.acme.ferry" ))
// App Clip targetFerry.configureAppClip( publicKey: "pk_test_xxx", appGroup: "group.com.acme.ferry")Both targets need the identical App Groups capability. The Clip also needs an appclips: Associated Domains entry. Ferry records the invocation link for the full app and consumes the handoff on its first launch.