Mobile client for SoapStone
Swift 94%
Python 5%
Shell <1%

README.md

Soapstone #

A location-based messaging app for iOS, inspired by the social message mechanics in the Dark Souls games. Players leave short, structured messages anchored to physical GPS coordinates; others nearby can discover and read them.

Soapstone is built on the AT Protocol (the decentralized network behind Bluesky) using custom social.soapstone.* lexicons. This repository is the mobile client, written in Swift / SwiftUI. The backend (Node.js + PostgreSQL/PostGIS App View) lives in a separate repo.

Requirements #

  • Swift 6 toolchain (Xcode 16+ on macOS, or the open-source toolchain on Linux)
  • iOS 18+ to run the app on a device or simulator
  • xtool to build and deploy the iOS app from a SwiftPM package (no .xcodeproj required)

The platform-agnostic core (SoapstoneKit) also builds and tests on Linux with just a Swift toolchain — no Apple SDKs needed.

Project layout #

Package.swift            SwiftPM manifest (two targets + test target)
xtool.yml                xtool project config (bundle ID, Info.plist)
Info.plist               iOS app Info.plist (permissions, URL scheme, orientation)

Sources/
  SoapstoneKit/          Platform-agnostic core — builds & tests on Linux
    Auth/                OAuth (PKCE + DPoP) session layer
    Core/                Domain models, config, geo URIs, phrase catalog
    Feed/                Nearby-feed + AR placement logic (pure, testable)
    Location/            LocationProviding protocol + mock
    Net/                 XRPC, App View (read) and PDS (write) clients
    Resources/           Bundled phrase catalog (en.json)
  soapstone_mobile/      The iOS app — SwiftUI, CoreLocation, ARKit scaffold
    App/                 Entry point, AppModel, Keychain token store
    Features/            Login, Feed, Compose, AR screens + view models
    Location/            CoreLocationProvider
    Theme/               Fonts + styling

Tests/SoapstoneKitTests/ Unit tests for the core (run with `swift test`)
docs/                    Lexicon reference, environment config, architecture

Two-target design #

All UI / Apple-framework code lives in the soapstone_mobile target and is guarded with #if canImport(SwiftUI), so the package still compiles to an empty module on Linux. Everything that can be tested without a device — auth crypto, geo math, networking, feed logic — lives in SoapstoneKit, which depends only on Foundation and swift-crypto. This keeps the test suite runnable in CI on Linux while the device-only layer stays thin.

Building & running #

Core (any platform) #

swift build          # compile both targets
swift test           # run the SoapstoneKit unit suite (runs on Linux & macOS)

iOS app (xtool) #

xtool dev            # build, install, and run on a connected device / simulator
xtool dev build      # build the .app without launching

See the xtool docs for first-time setup (signing, device pairing). The bundle identifier and Info.plist are configured in xtool.yml.

Configuration #

App-wide constants live in AppConfig — App View host, OAuth client metadata URL, redirect scheme, and the default 20 m search radius. See docs/ENVIRONMENT_CONFIG.md for per-environment overrides.

How it works #

  • Auth — ATProto OAuth with PKCE + DPoP, implemented from scratch in SoapstoneKit/Auth (there is no mature managed Swift OAuth client for ATProto). Tokens never leave the session layer; they are persisted only in the Keychain. App passwords are not supported.
  • Reads — nearby posts come from the App View backend (AppViewClient.getPosts), which runs the PostGIS geospatial query. Read-only.
  • Writes — new messages and ratings are written directly to the user's PDS via PDSClient (com.atproto.repo.createRecord) using the custom lexicon $types.
  • Location — foreground-only GPS via CoreLocationProvider; the nearby feed updates reactively as the user moves. A MockLocationProvider backs tests and previews.

For the data shapes exchanged with the backend, see docs/lexicon-reference.md and docs/XRPC_SERVICE.md.

Roadmap #

  • v1 (current): list-based UI showing posts within 20 m of the user's live location.
  • Future: an augmented-reality view rendering message indicators over the camera feed at their real-world coordinates (ARKit). The pure Feed/ARPlacement + Feed/GeoProjection logic is the start of that work.

Further reading #