Archived SwiftUI client for the OpenAI Assistants API (v2) with strategy-driven local file preprocessing and memory-safe run polling.
OpenAssistant is a native iOS client built using SwiftUI and the Combine framework that provides a mobile dashboard for interacting with the stateful OpenAI Assistants API (v2). The app enables users to manage custom AI assistants, thread histories, and vector store knowledge bases directly from an iPhone or iPad.
This repository now represents the legacy Assistants API line in Gunnar Hostetler's product catalog. The active direct-API successor is OpenResponses, which moved the product focus to the Responses API.
Unlike simple chat completions that rely on stateless inputs, the OpenAI Assistants API is stateful and asynchronous. OpenAssistant orchestrates the multi-phase lifecycle of thread runs (Queued → In Progress → Completed) using a memory-safe, active timer-based polling system.
Additionally, because the Assistants API rejects common mobile formats (like HEIC images or RTF documents) directly, OpenAssistant implements an on-device preprocessing pipeline using the Strategy Pattern to convert these file formats locally before transmission. This saves bandwidth and prevents server-side failures.
| Dimension | Detail |
|---|---|
| Platform | iOS 15.0+ / iPadOS 15.0+ |
| Language | Swift |
| UI | SwiftUI |
| Architecture | MVVM-S |
| Primary APIs | OpenAI Assistants API (v2) / Firebase Core |
| Storage | UserDefaults (via @AppStorage) |
| App Store | Download |
| Status | Archived |
| License | MIT |
- Asynchronous Run Orchestration: Active polling pipeline (2.0s interval) with memory-safe
[weak self]captures and explicit timer invalidation to prevent reference cycles. - Strategy-Driven File Preprocessing: Local, on-device conversion strategies (HEIC to JPEG, RTF to UTF-8 plain text, and voice memo transcription routing) executing off the main thread.
- Decoupled State Synchronization: Cross-module notifications using
NotificationCenterto synchronize lists (Assistants, Vector Stores) across tab views without direct ViewModel coupling. - Data Sovereignty: All API credentials reside in local user storage (
UserDefaults) and connect directly to OpenAI via TLS 1.3, bypassing external proxy servers. - Adaptive UI & Design System: Responsive SwiftUI layouts utilizing dark/light/system appearance modes and custom feedback states (creating thread, running assistant, processing, completing).
- Security Pre-Commit Hooks: Automated script verification preventing accidental commits of hardcoded developer API keys.
- Successor Path: The repo remains useful as a reference for Assistants API-era mobile orchestration, but current product work now lives in OpenResponses.
This flowchart maps the user experience from launching the app, through credential verification, and into main chat/vector store interaction pipelines:
flowchart TD
Launch[Launch App] --> CheckKey{Has API Key?}
CheckKey -->|No| Settings[Settings View]
Settings -->|Enter Key| SaveKey[Save & Initialize]
SaveKey --> Dashboard[Dashboard]
CheckKey -->|Yes| Dashboard
Dashboard --> VectorStore[Vector Stores]
VectorStore --> FileIngest[File Ingest]
FileIngest --> IngestStrategy[Apply Conversion Strategy]
IngestStrategy --> Upload[Upload & Index]
Upload --> Dashboard
Dashboard --> Chat[Select Assistant & Chat]
Chat --> SendMsg[Send Message]
SendMsg --> LocalSave[Save locally]
LocalSave --> ThreadRun[Run Thread]
ThreadRun --> Poll{Run Done?}
Poll -->|No| Poll
Poll -->|Yes| FetchMsg[Fetch Output]
FetchMsg --> SaveLocal[Deduplicate & Persist]
SaveLocal --> Render[Render Output]
OpenAssistant utilizes the MVVM-S design pattern. The View layer remains thin and declarative, observing reactive ViewModels that inherit from core base classes, which communicate with dedicated Services.
flowchart LR
subgraph UI ["Views"]
Chat[ChatView]
Vector[VectorStoreListView]
Settings[SettingsView]
end
subgraph Logic ["ViewModels"]
VM_Chat[ChatViewModel]
VM_Vector[VectorStoreManagerViewModel]
end
subgraph Storage ["Storage & Service"]
S_API[OpenAIService]
S_Upload[FileUploadService]
P_Msg[MessageStore]
end
subgraph External ["Cloud"]
E_OpenAI[OpenAI API]
end
UI --> Logic
Logic --> Storage
Storage --> External
VM_Chat <--> P_Msg
When a document is picked, the application routes the binary through an on-device conversion processor before packaging the payload:
flowchart TD
A[Select File] --> B{Supported?}
B -->|Yes| C[Read raw bytes]
B -->|No| D{Extension?}
D -->|heic| E[HEIC to JPEG]
D -->|rtf| F[RTF to TXT]
D -->|audio| G[Audio placeholder]
D -->|unsupported| H[Throw Error]
E --> C
F --> C
G --> C
C --> I[POST /v1/files]
I --> J[Link to Vector Store]
This diagram illustrates how data passes between the local device sandbox, secure transport layers, and external service boundaries:
flowchart TD
subgraph Device ["On-Device Sandbox"]
Key[API Key in AppStorage]
Msg[Saved Messages in AppStorage]
Tmp[Temporary File in tmp/]
end
subgraph Transport ["Network Transport"]
TLS[TLS 1.3 Encryption]
end
subgraph Cloud ["External Services"]
OpenAI[OpenAI Servers]
Firebase[Firebase Telemetry]
end
Key --> TLS
Tmp --> TLS
TLS --> OpenAI
Msg -.-> Msg
Device --> Firebase
| Concern | Files | Responsibility |
|---|---|---|
| App Entry | OpenAssistantApp.swift | Bootstrapping, Firebase configuration, and environment object injection. |
| Main UI Shell | MainTabView.swift / ContentView.swift | Primary tab routing and settings layout. |
| API Client | OpenAIService.swift | Base networking client, headers, and request execution with backoff retry logic. |
| API Extensions | OpenAIService-Assistant.swift, OpenAIService-Threads.swift, OpenAIService-Vector.swift | Domain-specific network mappings. |
| Ingestion | FileUploadService.swift | File conversion, multipart parsing, and vector store upload coordination. |
| Storage | MessageStore.swift | Chat history JSON serialization, deduplication, and persistence. |
The app's environment is parameterized by the following values:
| Setting | Storage | Default | Required | Purpose |
|---|---|---|---|---|
OpenAI_API_Key |
UserDefaults (via @AppStorage) |
"" |
Yes | Token for OpenAI API authorization. |
appearanceMode |
UserDefaults (via @AppStorage) |
"System" |
Yes | Dictates dark/light/system styling rules. |
savedMessages |
UserDefaults (via @AppStorage) |
nil |
No | Serialized chat history lists. |
enableNewFeature |
Compile-time flag (FeatureFlags.swift) |
false |
Yes | Controls the visibility of experimental features. |
- Clone the Repository:
git clone https://github.com/Gunnarguy/OpenAssistant.git cd OpenAssistant - Execute the Setup Helper Script:
The script checks prerequisites, runs CocoaPods installation, and installs local Git pre-commit security hooks to safeguard against API key leaks:
chmod +x setup.sh ./setup.sh
- Select Signing Identity:
- Open
OpenAssistant.xcworkspacein Xcode 15+. - Navigate to the OpenAssistant target.
- Under Signing & Capabilities, select your developer team and modify the Bundle Identifier.
- Open
- Build and Run:
- Select an iOS 15.0+ Simulator or physical device.
- Press
⌘+Rto build and execute the application.
The repository does not currently contain automated unit test targets. All validation must be performed manually:
| Validation | Procedure | Expected Result |
|---|---|---|
| Build verification | Run xcodebuild -workspace OpenAssistant.xcworkspace -scheme OpenAssistant -sdk iphonesimulator build CODE_SIGNING_ALLOWED=NO |
Build succeeds with zero errors. |
| Pre-Commit Scan | Attempt to commit a file containing sk-proj-abc123xyz... |
Commit is aborted with a warning. |
| Manual QA (Onboarding) | Clear API key in Settings, relaunch app. | Settings sheet automatically opens. |
| Manual QA (Assistant) | Create assistant "QA Bot", select model, tap Save. | Assistant appears in picker list. |
| Manual QA (Chat) | Type "Hello" inside "QA Bot" thread, send message. | Run lifecycle states progress to completed; text renders. |
- Local Storage Sandbox: API keys and message histories reside inside the app container's sandbox. Files copied to the app's
tmp/folder are purged immediately upon upload. - Network Protection: App Transport Security (ATS) rules restrict all API traffic to TLS 1.3 connections directly to OpenAI (
api.openai.com). Requests are sent directly from the app to the API without a custom proxy server. - Pre-Commit Hook: Scans changed files locally for keys before staging commits to avoid remote exposure.
- For detailed information, review SECURITY.md and PRIVACY.md.
| Document | Purpose |
|---|---|
| Architecture | System design, data flow, and service boundaries |
| Security | Secret handling, local storage, and release checks |
| Privacy | Data storage, API transmission, and user controls |
| Roadmap | Current status, planned work, and known gaps |
| App Store Notes | App Store metadata, review notes, and release checklist |
| Case Study | Engineering retrospective and implementation notes |
[x]Strategy-driven file format converters (JPEG, TXT conversions).[x]Decoupled state notification bus.[ ]Migrate credential storage from@AppStorageto secure Keychain Services.[ ]Introduce automated unit tests and Mock APIs.[ ]Implement true Speech-to-Text Whisper transcription inAudioTranscriptionStrategy.
OpenAssistant is licensed under the MIT License. See LICENSE for more details.