TypeScript, Swift and Kotlin
Every namespace, method and type in the SDK API exists in all three languages:
| Language | Platforms |
|---|---|
| TypeScript | Web apps, desktop apps and the browser extension |
| Swift | iOS and macOS |
| Kotlin | Android |
Only the syntax differs, by the rules in the rendering sheet. If you know one language’s SDK, you know the shape of the other two.
The rendering sheet
| Concept | TypeScript | Swift | Kotlin |
|---|---|---|---|
| Package | npm install seald-sdk | SPM .package(url: "https://github.com/seald-healthcare/seald-sdk", from: "1.0.0"), product SealdSDK | Gradle implementation("health.seald:sdk:1.0.0") |
| Import | import { create } from 'seald-sdk' | import SealdSDK | import com.sealdhealthcare.sdk.* |
| Entry | await create({ kind: 'desktop', release: '1.4.0', adapters: { unlock, signIn } }) | try await SealdHealthcare.create(Config(kind: .mobile, release: "1.4.0", adapters: Adapters(unlock: unlock, signIn: signIn))) | SealdHealthcare.create(Config(kind = ClientKind.MOBILE, release = "1.4.0", adapters = Adapters(unlock = unlock, signIn = signIn))) |
| Async | Promise<T> | async throws -> T | suspend fun ...: T |
| Names | as written | as written, enum cases lowerCamelCase (.notEntitled, .releaseBelowFloor, .accessRecord) | as written, enum entries UPPER_SNAKE (DenyReason.NOT_ENTITLED, ErrorCode.RELEASE_BELOW_FLOOR) |
| Arguments | positional, option objects | first parameter unlabeled, later ones labeled with the TS parameter name. Option-object fields become trailing labeled parameters with defaults: open(_ ref: ObjectRef, action: OpenAction = .view) | positional. Option fields become named parameters with defaults: open(ref: ObjectRef, action: OpenAction = OpenAction.VIEW) |
Decided<T> | union on outcome | enum Decided<T> { case allow(T, eventId: EventId); case deny(Denied); case challenge(Challenged<T>) } | sealed interface Decided<out T> { data class Allow<T>(val value: T, val eventId: EventId); data class Deny(val eventId: EventId, val reason: DenyReason, val text: String); class Challenge<T>(val eventId: EventId) { suspend fun stepUp(): Decided<T> } } |
Other unions (Opened, SaveOutcome, WorklistItem, ShareTarget, ShareInput, ExportScope, HoldTarget, Participation, Submitted) | tag field | enum with associated values, case per tag: .record(OpenedRecord), .file(OpenedFile), .saved(Saved), .baseMoved(BaseMoved), ShareTarget.person(p), .group("x") | sealed interface, one class per tag: Opened.Record, Opened.File, SaveOutcome.Saved, SaveOutcome.BaseMoved, ShareTarget.ToPerson(p), ShareTarget.ToGroup("x") |
save/create/saveDrafts result Decided<...> | Drafted | check 'outcome' in result | enum SaveResult<T> { case decided(Decided<T>); case draft(Drafted) } | sealed interface SaveResult<out T> { data class Decided<T>(val decided: com.sealdhealthcare.sdk.Decided<T>); data class Draft(val drafted: Drafted) } |
| Errors | SealdHealthcareError with code, retryable | enum SealdHealthcareError: Error one case per code, var retryable: Bool | class SealdHealthcareException(val code: ErrorCode, val retryable: Boolean) : Exception() |
Bytes / streams | Uint8Array / ReadableStream<Uint8Array> | Data / AsyncThrowingStream<Data, Error> | ByteArray / Flow<ByteArray> |
Events client.on | client.on('session', ({ state }) => ...) returns Unsubscribe | for await event in client.on(.session) { event.state } (an AsyncStream) | client.on<ClientEvent.Session>().collect { event -> event.state } (a Flow) |
| Dates | Date, Seconds number | Date, TimeInterval | Instant, Duration |
Person | { iss, sub } | Person(iss:sub:) | Person(iss, sub) |
Where the TypeScript API writes a shape inline, an unnamed union or an anonymous object, Swift and Kotlin give it a name: ObjectContent, DraftChoice, EnrollmentEvent, DomainScope, SaveResult for a result that may be a draft, SaveDraftsResult, DatasetRegistration, StorageBinding, S3Binding, DatabaseBinding, Contract, Change, MaskedField, RoleGrant, Times and Lifetimes. Each is declared on its namespace’s reference page.
A result that is an anonymous object with a single field unwraps to that field’s value in Swift and Kotlin:
Decided<{ escrowVersion: number }>isDecided<Int>.Decided<{ root: Locator }>isDecided<Locator>.Decided<{ lifted: boolean }>isDecided<Bool>in Swift andDecided<Boolean>in Kotlin.Decided<{ ownerlessDomains: Locator[] }>isDecided<[Locator]>in Swift andDecided<List<Locator>>in Kotlin.proveShare’s{ held: boolean }is a plainBoolin Swift andBooleanin Kotlin.
TypeScript keeps the object. A result with more than one field keeps a named type, such as SaveDraftsResult.
A worked example
The same call, objects.open followed by the allowed helper, in all three languages:
const opened = await allowed(await client.objects.open({ locator }));let opened = try await allowed(client.objects.open(ObjectRef(locator: locator)))val opened = allowed(client.objects.open(ObjectRef(locator)))Each language calls open on objects with a locator. Only the syntax follows each language’s
idiom. TypeScript’s option object becomes an unlabeled first parameter in Swift and Kotlin. Both
wrap it in ObjectRef, which matches TypeScript’s { locator }.
A platform adds nothing to the surface. Platform-specific needs, such as a native key store, a share extension or a file provider, belong to that platform’s SDK. They are outside the SDK API.
Next
- Adapters for the one place a platform’s surface differs:
Adapters. - Access decisions for what
Decided<T>means. - Reference overview for every namespace rendered this way.