Skip to content

Latest commit

 

History

History
642 lines (483 loc) · 17.2 KB

File metadata and controls

642 lines (483 loc) · 17.2 KB

@pwa-kit/sdk npm

TypeScript SDK for communicating with the PWAKit native iOS bridge. Provides Web API-aligned interfaces for native capabilities like push notifications, badges, haptics, biometrics, HealthKit, StoreKit, and more.

Installation

npm install @pwa-kit/sdk

Browser Bundle

For direct browser usage without a bundler:

<script src="https://cdn.jsdelivr.net/npm/@pwa-kit/sdk@latest/dist/index.global.js"></script>
<script>
  const { push, badging, haptics, ios, isNative } = PWAKit;

  if (isNative) {
    badging.setAppBadge(5);
  }
</script>

Quick Start

import { push, badging, haptics, ios, isNative } from "@pwa-kit/sdk";

if (isNative) {
  // Subscribe to push notifications (PushManager API)
  const subscription = await push.subscribe();
  console.log("Token:", subscription.token);

  // Set app badge (Badging API)
  await badging.setAppBadge(5);

  // Haptic feedback
  await haptics.impact("medium");

  // iOS-specific: Face ID / Touch ID
  const result = await ios.biometrics.authenticate("Confirm purchase");
}

API Structure

The SDK organizes APIs into three categories:

Web API-Aligned Modules

These modules follow Web Platform API conventions:

Module Web Standard Description
push PushManager Push notification registration
badging Badging API App icon badges
vibration Vibration API Vibration (uses haptics on iOS)
clipboard Clipboard API Read/write clipboard
share Web Share API Native share sheet
permissions Permissions API Permission management

Enhanced APIs

Extended functionality beyond web standards:

Module Description
haptics iOS haptic feedback (impact, notification, selection)
print AirPrint functionality
platform Platform detection and device info

iOS-Specific Modules (Namespaced)

Native iOS features without web equivalents, namespaced under ios.*:

Module Description
ios.biometrics Face ID / Touch ID authentication
ios.secureStorage Keychain storage
ios.healthKit HealthKit data access
ios.storeKit In-app purchases (StoreKit 2)
ios.app App lifecycle and reviews
ios.notifications Local notification scheduling

Web API Alignment

PWAKit SDK methods are designed to match Web Platform API signatures as closely as possible:

SDK Module Web Standard Alignment
push PushManager Exact match - subscribe(), getSubscription(), permissionState()
badging Badging API Exact match - setAppBadge(), clearAppBadge()
vibration Vibration API Exact match - vibrate(pattern) (polyfills with haptics on iOS)
clipboard Clipboard API Text methods - writeText(), readText()
share Web Share API Match - share(), async canShare()
permissions Permissions API Match with extension - query() + request()

Intentional Deviations

These deviations improve the developer experience on mobile:

  • clipboard.readText() returns string | null instead of throwing when clipboard is empty
  • share.canShare() is async to check native availability (Web API is sync)
  • share.share() returns ShareResult with completed and activityType (Web returns void)
  • permissions.request() is an extension for pre-prompting (not in Web standard)

Web API-Aligned Modules

Push (PushManager)

import { push } from "@pwa-kit/sdk";

// Subscribe to push notifications
const subscription = await push.subscribe();
console.log("Token:", subscription.token);
console.log("Endpoint:", subscription.endpoint); // apns://token

// Get existing subscription
const existing = await push.getSubscription();
if (existing) {
  console.log("Already subscribed");
}

// Check permission state
const state = await push.permissionState();
// 'granted' | 'denied' | 'prompt'

Badging (Badging API)

import { badging } from "@pwa-kit/sdk";

// Set badge count
await badging.setAppBadge(5);

// Clear badge
await badging.clearAppBadge();

Vibration (Vibration API)

import { vibration } from "@pwa-kit/sdk";

// Vibrate (triggers haptic on iOS)
await vibration.vibrate(100);
await vibration.vibrate([100, 50, 100]); // Pattern

Clipboard (Clipboard API)

import { clipboard } from "@pwa-kit/sdk";

// Write text
await clipboard.writeText("Hello, World!");

// Read text
const text = await clipboard.readText();

Share (Web Share API)

import { share } from "@pwa-kit/sdk";

// Share URL
await share.share({
  title: "Check this out",
  url: "https://example.com",
});

// Share with files
await share.share({
  title: "Document",
  files: [
    {
      name: "report.pdf",
      type: "application/pdf",
      data: "base64encodeddata...",
    },
  ],
});

// Check availability
const canShare = await share.canShare();

Permissions (Permissions API)

import { permissions } from "@pwa-kit/sdk";

// Query permission state
const status = await permissions.query({ name: "camera" });
console.log("Camera:", status.state); // 'granted' | 'denied' | 'prompt'

// Request permission
const result = await permissions.request({ name: "geolocation" });

// Supported permissions: 'camera', 'geolocation', 'microphone'

Enhanced APIs

Haptics

Native iOS haptic feedback with impact, notification, and selection patterns.

import { haptics } from "@pwa-kit/sdk";

// Impact feedback
await haptics.impact("light"); // light, medium, heavy, soft, rigid
await haptics.impact("medium");
await haptics.impact("heavy");

// Notification feedback
await haptics.notification("success"); // success, warning, error
await haptics.notification("error");

// Selection feedback
await haptics.selection();

Print

AirPrint support for the current page.

import { print } from "@pwa-kit/sdk";

const result = await print.print();
if (result.success) {
  console.log("Print job submitted");
}

Platform

Platform detection and device information.

import { platform } from "@pwa-kit/sdk";

const info = await platform.getInfo();
// {
//   platform: 'ios',
//   version: '17.0',
//   isNative: true,
//   appVersion: '1.0.0',
//   buildNumber: '42',
//   deviceModel: 'iPhone15,2'
// }

iOS-Specific Modules

These modules are namespaced under ios.* as they don't have web equivalents.

ios.biometrics

Face ID and Touch ID authentication.

import { ios } from "@pwa-kit/sdk";

// Check availability
const availability = await ios.biometrics.isAvailable();
// { available: true, biometryType: 'faceId' }

// Authenticate
const result = await ios.biometrics.authenticate("Confirm your identity");
if (result.success) {
  // Authentication successful
}

ios.secureStorage

Keychain-backed secure storage that persists across app reinstalls.

import { ios } from "@pwa-kit/sdk";

// Store value
await ios.secureStorage.set("auth_token", "secret123");

// Retrieve value
const token = await ios.secureStorage.get("auth_token");

// Check if key exists
const exists = await ios.secureStorage.has("auth_token");

// Delete value
await ios.secureStorage.delete("auth_token");

ios.healthKit

Access health and fitness data via Apple HealthKit.

import { ios } from "@pwa-kit/sdk";

// Check availability
const { available } = await ios.healthKit.isAvailable();

// Request authorization
const auth = await ios.healthKit.requestAuthorization({
  read: ["stepCount", "heartRate"],
  readWorkouts: true,
  readSleep: true,
});

// Query steps
const steps = await ios.healthKit.querySteps({
  startDate: "2024-01-01T00:00:00Z",
  endDate: "2024-01-31T23:59:59Z",
});

// Query heart rate
const heartRate = await ios.healthKit.queryHeartRate({
  startDate: "2024-01-01T00:00:00Z",
  endDate: "2024-01-31T23:59:59Z",
});

// Query workouts
const workouts = await ios.healthKit.queryWorkouts({
  startDate: "2024-01-01T00:00:00Z",
  endDate: "2024-01-31T23:59:59Z",
  limit: 10,
});

// Query sleep
const sleep = await ios.healthKit.querySleep({
  startDate: "2024-01-01T00:00:00Z",
  endDate: "2024-01-31T23:59:59Z",
});

// Save workout
await ios.healthKit.saveWorkout({
  activityType: "running",
  startDate: "2024-01-15T08:00:00Z",
  endDate: "2024-01-15T08:30:00Z",
  duration: 1800,
  totalEnergyBurned: 350,
  totalDistance: 5000,
});

ios.storeKit

In-app purchases via StoreKit 2.

import { ios } from "@pwa-kit/sdk";

// Get products
const products = await ios.storeKit.getProducts(["premium", "coins_100"]);
for (const product of products) {
  console.log(`${product.displayName}: ${product.displayPrice}`);
}

// Purchase
const result = await ios.storeKit.purchase("premium");
if (result.success) {
  console.log("Transaction ID:", result.transactionId);
}

// Restore purchases
await ios.storeKit.restore();

// Get entitlements
const entitlements = await ios.storeKit.getEntitlements();
console.log("Owned:", entitlements.ownedProductIds);

// Check ownership
const hasPremium = await ios.storeKit.isOwned("premium");

ios.app

App lifecycle and utilities.

import { ios } from "@pwa-kit/sdk";

// Get app version
const version = await ios.app.getVersion();
// { version: '1.0.0', build: '42' }

// Request App Store review
await ios.app.requestReview();

// Open app settings
await ios.app.openSettings();

ios.notifications

Local notification scheduling. Schedule one-off or recurring notifications that fire even when the app is in the background.

import { ios } from "@pwa-kit/sdk";

// Schedule a notification in 60 seconds
await ios.notifications.schedule({
  id: "reminder-123",
  title: "Time for a break",
  body: "You have been working for an hour",
  trigger: { type: "timeInterval", seconds: 60 },
});

// Schedule a repeating notification (minimum 60 seconds)
await ios.notifications.schedule({
  id: "hourly",
  title: "Hourly check-in",
  trigger: { type: "timeInterval", seconds: 3600, repeats: true },
});

// Schedule at a specific date
await ios.notifications.schedule({
  id: "meeting",
  title: "Meeting starts",
  body: "Project sync in conference room A",
  trigger: { type: "date", date: new Date("2024-12-25T10:00:00") },
});

// Schedule a daily recurring notification (9 AM)
await ios.notifications.schedule({
  id: "daily-reminder",
  title: "Good morning!",
  body: "Start your day with a review",
  trigger: { type: "calendar", hour: 9, minute: 0, repeats: true },
});

// Include badge, sound, and custom data
await ios.notifications.schedule({
  id: "full-example",
  title: "New message",
  body: "You have a new message",
  subtitle: "From John",
  badge: 1,
  sound: "default",
  data: { messageId: "123", senderId: "john" },
  trigger: { type: "timeInterval", seconds: 5 },
});

// Cancel a specific notification
await ios.notifications.cancel("reminder-123");

// Cancel all scheduled notifications
await ios.notifications.cancelAll();

// Get all pending notifications
const pending = await ios.notifications.getPending();
for (const notification of pending) {
  console.log(`${notification.id}: ${notification.title}`);
  if (notification.nextTriggerDate) {
    console.log(`  Next: ${notification.nextTriggerDate}`);
  }
}

Trigger Types:

Type Description Example
timeInterval Fire after N seconds { type: 'timeInterval', seconds: 60, repeats: false }
date Fire at specific date/time { type: 'date', date: new Date('2024-12-25T10:00:00') }
calendar Fire on calendar match { type: 'calendar', hour: 9, minute: 0, repeats: true }

Calendar Trigger Components:

  • hour (0-23), minute (0-59), second (0-59)
  • weekday (1=Sunday, 7=Saturday)
  • day (1-31), month (1-12), year

Note: iOS limits apps to 64 scheduled local notifications. Use getPending() to monitor your notification count.

Detection Utilities

import {
  isNative,
  getPlatformInfo,
  hasMessageHandlers,
} from "@pwa-kit/sdk";

// Quick check if running in native app
if (isNative) {
  // Use native features
}

// Full platform info
const info = getPlatformInfo();
// {
//   isNative: true,
//   hasMessageHandlers: true,
//   hasPWAKitUserAgent: true,
//   platform: 'ios',
//   userAgent: '...'
// }

// Check for webkit message handlers
if (hasMessageHandlers()) {
  // Bridge is available
}

Bridge API

For advanced usage, access the bridge directly.

import { bridge } from "@pwa-kit/sdk";

// Call any native module
const response = await bridge.call("myModule", "myAction", { key: "value" });

// Listen to native events
window.addEventListener("pwa:push", (event) => {
  console.log("Push notification:", event.detail);
});

window.addEventListener("pwa:lifecycle", (event) => {
  console.log("Lifecycle event:", event.detail);
});

Custom Modules

You can extend PWAKit by creating your own native Swift modules that can be called from JavaScript.

Quick Example

Swift Module:

public struct MyModule: PWAModule {
    public static let moduleName = "myModule"
    public static let supportedActions = ["doSomething"]

    public init() {}

    public func handle(
        action: String,
        payload: AnyCodable?,
        context: ModuleContext
    ) async throws -> AnyCodable? {
        try validateAction(action)

        switch action {
        case "doSomething":
            let value = payload?["value"]?.stringValue ?? "default"
            return AnyCodable([
                "result": AnyCodable("Processed: \(value)")
            ])
        default:
            throw BridgeError.unknownAction(action)
        }
    }
}

JavaScript Usage:

import { bridge } from "@pwa-kit/sdk";

const result = await bridge.call("myModule", "doSomething", { value: "test" });
console.log(result.result); // "Processed: test"

See the Custom Modules Guide for complete documentation including:

  • The PWAModule protocol
  • Registration and feature flags
  • Working with UIKit and @MainActor
  • Error handling patterns
  • Complete Hello World example

TypeScript

Full TypeScript support with type definitions for all modules.

import type {
  // Push types
  PushSubscription,
  PushPermissionState,
  // Permission types
  PermissionName,
  PermissionState,
  PermissionStatus,
  // iOS types
  BiometryType,
  BiometricAvailability,
  AuthenticationResult,
  ProductInfo,
  PurchaseResult,
  HealthSample,
  WorkoutData,
  // Local notification types
  NotificationOptions,
  NotificationTrigger,
  TimeIntervalTrigger,
  DateTrigger,
  CalendarTrigger,
  PendingNotification,
  // Platform types
  PlatformInfo,
  PlatformDetectionInfo,
} from "@pwa-kit/sdk";

Browser Compatibility

The SDK safely handles non-native environments:

import { isNative, push, haptics } from "@pwa-kit/sdk";

if (isNative) {
  // Running in PWAKit native app
  await push.subscribe();
  await haptics.impact("medium");
} else {
  // Running in browser - provide fallbacks
  console.log("Native features unavailable");
  // Use web APIs or graceful degradation
}

License

MIT