DRDifferent Requests
Start here

Start here

Add the package, create a hub, and show the board your users post to.

What you need

  • iOS 26 or macOS 26 or later.
  • Xcode 26 and Swift 6.2.
  • An API key from your dashboard.

1. Add the package

In Xcode, choose File > Add Package Dependencies and paste this URL.

https://github.com/Different-Productions/DifferentRequestsSDK

Or in Package.swift:

dependencies: [
  .package(
    url: "https://github.com/Different-Productions/DifferentRequestsSDK",
    from: "0.14.0"
  ),
],

2. Get your API key

Create an account to get an API key. It stays on the app's API keys page in the dashboard.Start free

Your API key isn't a secret. It ships inside your app, and anyone who installs your app can read it off their device. It identifies your app; it doesn't protect it.

3. Show the board

A DifferentRequestsHub holds the board while your app runs: loaded pages and unsent drafts. Make one in your App, not inside a view, and hand it to the screen. A view is rebuilt often, and a hub made inside one would lose the user's place each time.

import DifferentRequests
import SwiftUI

@main
struct MyApp: App {
  private let requests = DifferentRequestsHub(
    client: DifferentRequestsClient(appKey: "your-api-key", baseURL: .production),
    appearance: .standard,
    emptyBoard: .standard,
    notificationPermission: .askedBySDK
  )

  var body: some Scene {
    WindowGroup {
      NavigationStack {
        DifferentRequestsView(hub: requests)
      }
    }
  }
}

4. Sign your users in

Reading the board needs only your API key. Voting, asking, following and commenting need a signed-in user. Pass each user's ID from your app as externalID, so nobody signs up twice.

NavigationStack {
  DifferentRequestsView(hub: requests)
}
.task {
  do {
    let signedIn = try await requests.client.createSession(
      externalID: currentUser.id,
      email: currentUser.email,
      displayName: currentUser.name,
      proof: nil
    )
    logger.info("DifferentRequests: signed in as \(signedIn.user.id)")
  } catch {
    logger.error("DifferentRequests: \(error.localizedDescription)")
  }
}

Skip sign-in and the board still shows, but nobody can ask for anything. requests.client.describeIntegration() tells you when this is the case.

Use an externalID nobody can guess: a UUID stored for each user, not an email address or a sequential number. The same externalID is always the same user, so votes survive a reinstall.

5. Sign each sign-in on your server (optional)

Your API key says which app is asking, not which user. Anyone who installs your app can read the API key and could sign in as any user whose externalID they guess. If your app has its own server, create a signing secret on your app's API keys page in the dashboard, and your server signs each sign-in instead.

On your server, sign the user's externalID and an expiry time with the secret, and give the app the result. Any language with HMAC-SHA256 can do it. Encode the signature as URL-safe base64: standard base64 with - for +, _ for /, and no = padding.

let expiresAt = Int(Date().addingTimeInterval(300).timeIntervalSince1970)
let signature = Data(
  HMAC<SHA256>.authenticationCode(
    for: Data("\(externalID)\n\(expiresAt)".utf8),
    using: SymmetricKey(data: Data(signingSecret.utf8))
  )
)
.base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")

Your server makes the signature. Your app only passes it along:

let signedIn = try await requests.client.createSession(
  externalID: currentUser.id,
  email: currentUser.email,
  displayName: currentUser.name,
  proof: DRIdentityProof(
    signature: fromServer.signature,
    expiresAt: Date(timeIntervalSince1970: TimeInterval(fromServer.expiresAt))
  )
)
Once an app has a signing secret, every sign-in needs a DRIdentityProof, including users who already have accounts. An app with no secret carries on as before.

A DRIdentityProof lasts as long as you say, up to an hour. Sign a fresh one when a user opens your app rather than storing one on the device.

If a sign-in fails, log the reason for yourself and don't show it to the user. Every reason is one you can fix (no proof, an expired one, a replaced secret), and none is something the user can act on. The board still shows with your API key alone, and asking, voting and commenting stay hidden until a user is signed in.

6. Load the board early (optional)

Loading the board's first page takes a moment. If you know it's about to be opened, load it while the user is still on another screen, and it opens already filled in.

Button("Requests") {
  showingRequests = true
}
.task {
  await requests.readTheBoardBeforeItIsShown()
}