Apple Deployment (macOS & iOS)
Purple8 Graph ships a native Swift client and full SwiftUI app for macOS 14+ and iOS 17+. Everything lives in sdk/swift/ — zero external dependencies.
What's included
| Target | Type | Description |
|---|---|---|
Purple8Graph | Library | Pure Swift REST client — Purple8Client with 7 sub-clients, Codable models, AnyJSON, typed errors |
Purple8GraphUI | Library | SwiftUI screens — License gate, Login, Dashboard, Graph Explorer, Query Builder, Settings |
Purple8GraphApp | Executable | Runnable macOS / iOS app wrapping the two libraries above |
App screens
License gate → server URL + Stripe license key validation
↓
Login → username/password or API key
↓
Dashboard → health, stats, quick Cypher, active journeys
Graph Explorer→ hybrid search → node detail → traversal
Query Builder → full Cypher editor + dynamic results table
Settings → server URL, subscription info, disconnect, deactivateRun in Xcode
# From the repo root:
open sdk/swift/Package.swift- Xcode opens the package automatically
- Select the
Purple8GraphAppscheme (top toolbar) - Select My Mac (or a connected iPhone / simulator) as the destination
- Press ▶ Run (
⌘R) - The license gate appears — enter your server URL and Stripe license key
Requirements
| Version | |
|---|---|
| Swift | 5.9+ |
| Xcode | 15+ |
| macOS (run) | 14 Sonoma+ |
| iOS (run) | 17+ |
| Purple8 Graph server | 0.23.0+ |
License gate flow
Every app launch checks for a validated license before showing the login screen:
isLicensed == false → LicenseGateView (paste Stripe key → validate)
isLicensed == true &&
isAuthenticated == false → LoginView (username/password or API key)
isAuthenticated == true → ContentView (main app)The validated license key is stored in the system Keychain (not UserDefaults) — encrypted at rest, survives app reinstalls. Deactivating a device removes it from the Keychain so the seat can be transferred.
Distribute for macOS
Step 1 — Archive
Product ▸ ArchiveWait for the archive, then in the Organizer:
Distribute App
→ Developer ID ← direct download / .dmg (recommended for B2B SaaS)
→ App Store Connect ← Mac App Store
→ Custom ← internal / enterpriseFor Developer ID you need:
- Apple Developer Program membership ($99/yr)
- A Developer ID Application certificate in Keychain
- Notarization — Xcode handles this automatically during export
The export produces a signed, notarized Purple8 Graph.app.
Step 2 — Wrap in a .dmg
brew install create-dmg
create-dmg \
--volname "Purple8 Graph" \
--volicon "path/to/AppIcon.icns" \
--background "path/to/dmg-background.png" \
--window-pos 200 120 \
--window-size 660 400 \
--icon-size 128 \
--icon "Purple8 Graph.app" 180 170 \
--hide-extension "Purple8 Graph.app" \
--app-drop-link 480 170 \
"Purple8-Graph-0.23.0.dmg" \
"path/to/export-folder/"This produces Purple8-Graph-0.23.0.dmg — a standard drag-and-drop installer ready to email to subscribers.
Distribute for iOS / TestFlight
- In Xcode select an iPhone destination (real device or simulator)
Product ▸ ArchiveDistribute App ▸ TestFlight— uploads directly to App Store Connect for beta testing
Use as a library in your own app
Add the Swift package as a dependency in Xcode (File → Add Package Dependencies) or in your own Package.swift:
// Local dependency:
.package(path: "../../sdk/swift")
// Remote (after the repo is public):
.package(url: "https://github.com/Redlock666/purple8-graph", from: "0.23.0")Minimum integration — embed the full SwiftUI app in your own app:
import SwiftUI
import Purple8GraphUI
@main
struct MyApp: App {
@StateObject private var appState = Purple8AppState()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appState)
}
}
}Or use just the REST client without any UI:
import Purple8Graph
let client = Purple8Client(baseURL: URL(string: "https://graph.acme.com")!)
try await client.auth.login(username: "admin", password: "secret")
let node = try await client.nodes.create(
nodeID: "person_001",
labels: ["Person"],
properties: ["name": "Alice", "age": 30]
)
let results = try await client.search.vector(
queryEmbedding: embedding,
topK: 5,
labelFilter: "Person"
)Architecture
Purple8GraphApp (executable — @main entry point)
└── Purple8GraphUI (SwiftUI library)
├── Purple8AppState @MainActor ObservableObject — owns client, auth, license
├── LicenseGateView Server URL + Stripe key activation
├── LicenseValidator Calls /auth/validate-license → Keychain storage
├── LoginView Username/password or API key
├── ContentView TabView (iOS) / NavigationSplitView (macOS)
├── DashboardView Health indicator, stats, quick Cypher, journeys
├── GraphExplorerView Hybrid search → node detail card → BFS traversal
├── QueryBuilderView Cypher editor → dynamic results table
└── SettingsView Server URL, subscription plan, disconnect, deactivate
└── Purple8Graph (networking library — no SwiftUI dependency)
├── Purple8Client Facade — 7 sub-clients
├── AuthClient Login, logout, refresh, /auth/me, validate-license
├── NodesClient CRUD + bulk create + count
├── EdgesClient CRUD + existence check
├── SearchClient Vector + hybrid + BM25 search
├── QueryClient Cypher POST /v1/query
├── JourneysClient Start, advance, cancel, audit, list
├── KMSClient Status, list, rotate, disable/enable
├── TokenStore actor — thread-safe token + auto-refresh on 401
├── Models All Codable types + AnyJSON
└── Errors Purple8Error enum + HTTP factoryApp entitlements
The app is fully sandboxed. The shipped entitlements enable:
| Entitlement | Reason |
|---|---|
com.apple.security.app-sandbox | Required for Mac App Store; recommended for Developer ID |
com.apple.security.network.client | Outbound URLSession calls to the Purple8 Graph server |
For development against a local http:// server, NSAllowsLocalNetworking: true is set in Info.plist — HTTPS exceptions are not needed in production.