Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

20 Commits

Folders and files

Repository files navigation

Loomup native SDKs

Native client packages for Loomup, preserved in a repository separate from the server and JavaScript SDK repositories.

  • Package.swift / swift/ — Swift Package Manager products Loomup and LoomupAppIntegrity (iOS 16+).
  • kotlin/ — Kotlin/JVM core plus the Android integrity integration.
  • flutter/ — Dart/Flutter client retained for a later integrity rollout.
  • conformance/ — language-neutral client fixtures.

These packages are source-available and continuously tested. Swift is released from the root package through semantic-version Git tags; the current release is 0.1.9. The release workflow verifies tagged Swift source but does not publish to Swift Package Index or a Swift package-registry server. Kotlin and Dart do not yet have Maven Central or pub.dev releases.

Mobile applications must never embed a Loomup service key. User access tokens remain the authorization principal; app integrity is an additional anti-abuse signal for projects that opt into it.

Swift Package Manager

Add the released package from its Git URL and link both products:

dependencies: [
    .package(
        url: "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/bluppco/loomup-native.git",
        from: "0.1.9"
    )
],
targets: [
    .target(
        name: "MyApp",
        dependencies: [
            .product(name: "Loomup", package: "loomup-native"),
            .product(name: "LoomupAppIntegrity", package: "loomup-native"),
        ]
    )
]

Use an exact 0.1.9 requirement when automatic patch updates are not desired. For local development, replace the URL dependency with .package(path: "../loomup-native").

For an iOS 16+ App Store or TestFlight app, construct the client with the manifest app identifier, not a backend credential:

import Loomup
import LoomupAppIntegrity

let loomup = createMobileClient(
    url: URL(string: "https://api.example.com")!,
    appID: "ios_main"
)

createMobileClient uses App Attest for each mutation, stores only the refresh token in a ThisDeviceOnly Keychain item, and keeps the access token in memory. The appID must match an [app_integrity.apps.<id>] entry on the server. Keep the core createClient factory for macOS/server-side tools and tests; it also has no service-key input.

For Google, Apple, or GitHub sign-in, use the system authentication session helper. It keeps the one-use verifier in memory and exchanges the deep-link code through the same integrity-protected client:

let tokens = try await signInWithOAuth(
    client: loomup,
    provider: .google,
    redirectTo: URL(string: "com.example.app:/auth/callback")!,
    presentationContextProvider: windowProvider
)

Android

The kotlin/ build contains the JVM core and :android library. Android apps use createAndroidClient(context, url, appId, cloudProjectNumber). It requests standard Play Integrity tokens bound to the exact mutation and stores only the refresh token using a non-exportable Android Keystore key. The project number and appId are identifiers, not secrets.

The repository CI builds and tests all native packages. Swift releases use Git tags consumable by Swift Package Manager; they are not separately listed or uploaded to Swift Package Index, a Swift package-registry server, or CocoaPods. Kotlin and Dart are not published to Maven Central or pub.dev.

Android exposes signInWithOAuth(client, provider, redirectTo, launcher) in the Android library; the app supplies its Custom Tab/deep-link launcher. Flutter exposes the same authorize/exchange flow with an injected URL launcher so no browser plugin is forced on applications.

Swift and Android mobile constructors reconnect realtime automatically when the app returns to the foreground. Flutter apps call resumeRealtime() from their AppLifecycleState.resumed handler so active subscriptions are preserved, re-subscribed, and resynchronized after a background suspension.

The released Swift client also verifies application-level realtime liveness while at least one subscription is active. It sends correlated JSON ping text frames every 25 seconds and expects the matching JSON pong within 12 seconds. A missing or mismatched response retires the socket even if it still reports OPEN; the normal jittered reconnect path then authenticates, re-subscribes, and refetches current authorized state. These messages complement the WebSocket protocol Ping/Pong frames and do not replace or disable them.

Swift large file uploads

Release 0.1.9 automatically sends uploads above 8 MiB as bounded resumable chunks. Deploy the backend resumable-upload routes first. The project must allow the desired completed object size (up to 1 GiB) and have sufficient storage quota. Transient chunk and completion failures are retried; terminal failures abort staged uploads. Cancellation is checked between chunks.

Prefer client.storage.from("files").upload(path: "movie.mp4", fileURL: url, contentType: "video/mp4") for large files: it reads only one chunk at a time. Existing data: callers also use chunked transfer for large Data values.

Use downloadFile(path:) to receive a temporary-file URL for large downloads. The caller must remove the file after previewing or sharing it. Built-in URLSession transports download directly to disk; custom HTTP transports must implement download(for:) to support this operation. Authentication refresh applies as for ordinary downloads.

Swift inbox actions

client.inbox performs online, recipient-owned bulk actions atomically:

let scope: [String: JSONValue] = ["workspace_id": .string(workspaceID)]
let selection = try await client.inbox.select(scope: scope, expectedRecipientId: userID)
let request = InboxActionRequest(
    action: .markRead, scope: scope,
    target: .selection(id: selection.selectionId, excludedIds: [], includedIds: []),
    expectedRecipientId: userID
)
let result = try await client.inbox.act(request)

Retain and reuse the same request after an ambiguous transport failure: its idempotency key prevents duplicate work for 24 hours. Actions are .markRead, .delete, and .deleteRead; targets are .matching, .ids, and .selection. Select-all snapshots include unloaded pages, exclude later arrivals, and expire after 30 minutes. Use client.inbox.members(selectionId:ids:scope:) to resolve loaded checkboxes. Limits: 50,000 snapshot members, 1,000 explicit IDs, additions, or exclusions. The server checks recipient ownership and current permissions. Results report changed/skipped counts and the cutoff/change timestamps. See the inbox contract for full semantics.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages