iOS SDK

Swift Package. iOS 16+. Zero external dependencies. Currently at v0.7.0.

Install

In Xcode → File → Add Package Dependencies…, paste:

text
https://github.com/fil-technology/appmate-ios

Or in Package.swift:

swift
.package(url: "https://github.com/fil-technology/appmate-ios", from: "0.2.0")

Configure

Once at launch:

swift
import AppMate

RetentionFlow.configure(
    .init(
        appSlug: "my-ios-app",
        baseURL: URL(string: "https://cancel.appmate.cloud")!,
        urlScheme: "myapp"
    )
)

Start the cancel flow

swift
RetentionFlow.startCancelFlow(
    userId: currentUser.id,
    attributes: ["plan": "monthly"]
) { link in
    switch link.action {
    case .openPremium(let paywallId):
        navigateToPaywall(variant: paywallId)
    case .openOffer(let offerId):
        OfferRouter.present(offerId)
    case .openSupport(let topic, let message):
        openSupportInbox(topic: topic, prefilled: message)
    case .openFeature(let id): openFeature(id)
    case .returnToApp:        break
    case .manageSubscription:
        Task { await RetentionFlow.presentManageSubscriptions() }
    case .externalURL(let url):
        UIApplication.shared.open(url)
    case .none:               break
    }
}

Coexisting with your existing deep links

AppMate URLs are namespaced as {yourscheme}://retention-flow/action?.... The SDK's parser returns nil for URLs that don't match, so it never claims someone else's URL. Chain it in front of your existing handler:

swift
.onOpenURL { url in
    if let link = RetentionFlow.deepLink(from: url) {
        handleAppMate(link)
        return                     // AppMate URL — done
    }
    handleMyExistingDeepLinks(url) // everything else
}
manage_subscription doesn't even use your scheme — it routes to Apple's StoreKit sheet directly. Your URL handler never sees it.

Onboarding funnel (web → app)

Recover the answers + email captured by a web onboarding funnel on first launch. See the onboarding guide for the full flow.

swift
// Deferred handoff — call on first launch (paste banner shows):
Task {
    if let result = await RetentionFlow.fetchOnboardingResult(userId: user?.id) {
        if let goal = result.values(forStep: "goal").first { applyPreset(goal) }
        if let email = result.email { prefillSignup(email: email) }
    }
}

// Or run the funnel in-app for an installed user:
RetentionFlow.startOnboardingFlow(userId: user.id) { result in
    guard let result else { return }
    apply(result)
}

Referral (share with a friend)

Install-attributed referrals — see the referral guide. Don't grant the referrer's reward on share; it's earned only when a friend installs.

swift
// Share:
if let url = await RetentionFlow.referralShareLink(userId: user.id) {
    presentShareSheet(items: [shareMessage, url])
}

// New user, first launch (paste banner shows):
if let attr = await RetentionFlow.attributeReferral(userId: user.id),
   let reward = attr.refereeReward {
    grantFreeWeeks(reward.weeks)
}

// Referrer, every launch:
let earned = await RetentionFlow.claimReferralRewards(userId: user.id)
if earned.weeks > 0 { grantFreeWeeks(earned.weeks) }

Manage subscriptions helper

Wraps StoreKit 2's AppStore.showManageSubscriptions(in:) with an App Store URL fallback for Simulator and signed-out devices.

swift
Task { await RetentionFlow.presentManageSubscriptions() }

Soft-fail behaviour

If the AppMate server is unreachable, onAction is invoked with .manageSubscription so you can route the user straight to the App Store — they're never blocked from cancelling.

Action reference

  • .returnToApp — open your home screen.
  • .openPremium(paywallId:) — show your paywall; paywallId may be set to target a variant.
  • .openOffer(id:) — your app maps the id to a StoreKit promo, RevenueCat offering, or custom paywall.
  • .openSupport(topic:message:) — pre-fill your support sheet.
  • .openFeature(id:) — deep-link to a feature, tutorial, or onboarding screen.
  • .manageSubscription — present StoreKit 2 manage-subs.
  • .externalURL(URL) — open in user's browser.
  • .onboardingComplete(claimToken:) — a web-to-app onboarding funnel finished; startOnboardingFlow handles it for you.
  • .none — future / unknown — handle defensively.

Full source + tests: github.com/fil-technology/appmate-ios.