Skip to content
AppNorth
Log in

iOS SDK

Deep links

Use AppNorthDeepLinks to create custom-scheme links to an idea, private report, or the current chat. The SDK parses a destination; your app owns URL registration, navigation, and authentication.

View Markdown

Register and create links

Register your app's URL scheme in Xcode's URL Types. Pass that same scheme to the helper. The scheme below is an example; replace it with your app's registered scheme.

AppNorthURLs.swift
import Foundation
import AppNorth

func ideaURL(handle: String) throws -> URL {
    try AppNorthDeepLinks.idea(scheme: "myapp", handle: handle)
}

func reportURL(id: String) throws -> URL {
    try AppNorthDeepLinks.feedback(scheme: "myapp", id: id)
}

func chatURL() throws -> URL {
    try AppNorthDeepLinks.chat(scheme: "myapp")
}

These produce myapp://appnorth/ideas/<encoded-handle>, myapp://appnorth/feedback/<encoded-id>, and myapp://appnorth/chat/current. IDs and slugs are encoded as one path component. Empty values or invalid schemes throw AppNorthError.invalidConfiguration.

Validate before routing

parse(_:) checks the appnorth host and the two-part path. It does not verify that the URL uses your app's scheme. Check that in the host before accepting a link:

AppNorthRouter.swift
import Foundation
import AppNorth

func routeAppNorthURL(_ url: URL, client: AppNorthClient) async throws -> AppNorthLink? {
    guard url.scheme?.lowercased() == "myapp",
          let link = AppNorthDeepLinks.parse(url) else { return nil }
    switch link.kind {
    case .idea:
        _ = try await client.idea(link.value)
    case .feedback:
        _ = try await client.feedbackThread(link.value)
    case .chat:
        guard try await client.chatStatus().available else { return nil }
    }
    return link
}

Call this from your SwiftUI onOpenURL handler or UIKit URL callback, then navigate according to the returned link. For .chat, present AppNorthChatView(client: client) with that same client; link.value is "current", not a conversation ID. Show a sign-in flow before resolving a private report or entering a chat that requires identified users. An ID in a link grants no access; report ownership is checked by the server.

These custom-scheme links contain no project ID. Route them only through the intended project's client. The SDK does not register Universal Links or open screens automatically. Hosted roadmap URLs and identity handoff are separate server routes; there is no public Swift handoff helper.

Open chat from a push notification

AppNorth chat notifications carry appnorth.url: "appnorth://appnorth/chat/current", with appnorth.type: "chat" and the public App ID in appnorth.appId. Use the push facade to validate that envelope before routing. This payload URL uses the SDK's appnorth scheme; it is handled directly from the notification response, so you do not need to register that scheme in URL Types.

ChatPush.swift
import Foundation
import AppNorth

@MainActor
func configureChatPush(
    client: AppNorthClient,
    environment: AppNorthPushEnvironment,
    updateBadge: @escaping @MainActor (Int) -> Void,
    showChat: @escaping @MainActor () -> Void
) {
    AppNorth.configurePush(
        client: client,
        environment: environment,
        badgeHandler: updateBadge,
        routeHandler: { link in
            if link.kind == .chat { showChat() }
        }
    )
}

@MainActor
func handleChatNotificationTap(_ userInfo: [AnyHashable: Any]) -> Bool {
    AppNorth.handleNotificationTap(userInfo)
}

Configure with the client used by your chat and the environment matching your APNs signing configuration. showChat is your app's presentation callback. From your UNUserNotificationCenterDelegate tap callback, forward response.notification.request.content.userInfo to handleChatNotificationTap on the main actor, and complete the delegate callback after handling. A false result leaves the notification for your other handlers. The helper rejects a different App ID or an invalid chat destination; it does not switch accounts or grant access.

Your app still requests notification permission and registers with APNs. Forward the resulting raw device token with try await AppNorth.registerPushToken(deviceToken), and register again after an identity change. Call try await AppNorth.syncUnreadBadge() on foreground and after reads in a custom chat UI; its badge value is 0 or 1, not a message count. Before logout, await AppNorth.unregisterPushToken(deviceToken), then call AppNorth.resetPush() and clear identity. Handle failures so you can retry removal before clearing that identity. See the push API reference for signatures and lifecycle details.

Verify

  1. Open an idea URL using your registered scheme and confirm the correct idea loads.

  2. Test an encoded handle and reject a URL using another scheme or host.

  3. Open a report as its author, signed out, and as another account. Only the author should retrieve it.

  4. Check missing and deleted destinations with a useful error and a way back to your app.

  5. Tap a chat notification for the configured App ID and confirm it opens your current chat. A foreign App ID or malformed chat URL must not invoke your route handler.