Quickstart
Pick your path. Both take under 15 minutes.- iOS App
- Server API
Build a notes app with PrismArchitecture (state management), PrismUI (components), and PrismStorage (persistence).Or open in Xcode:
Requires Swift 6.3+, Xcode 16.4+, iOS 26+. See Installation for details.
Create Your Project
Add Prism to Package.swift
Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "notes-app",
platforms: [.iOS(.v17), .macOS(.v14)],
dependencies: [
.package(url: "https://github.com/byescaleira/prism.git", from: "4.4.0")
],
targets: [
.executableTarget(
name: "notes-app",
dependencies: [
.product(name: "Prism", package: "prism")
]
)
]
)
import Prism gives you PrismUI, PrismArchitecture, PrismNetwork, PrismStorage, and more.Define Your Feature
Prism uses the reducer pattern — state is a struct, changes happen through actions, side effects return viaPrismEffect.NotesFeature.swift
import Prism
struct Note: Identifiable, Codable, Sendable, Equatable {
let id: UUID
var title: String
var body: String
var createdAt: Date
init(title: String, body: String) {
self.id = UUID()
self.title = title
self.body = body
self.createdAt = .now
}
}
struct NotesFeature: PrismReducer {
struct State: Sendable, Equatable {
var notes: [Note] = []
var draft = ""
}
enum Action: Sendable {
case addNote
case updateDraft(String)
case deleteNote(UUID)
case loaded([Note])
}
let storage = PrismDefaultsStore(suite: "com.app.notes")
func reduce(into state: inout State, action: Action) -> PrismEffect<Action> {
switch action {
case .addNote:
guard !state.draft.isEmpty else { return .none }
let note = Note(title: state.draft, body: "")
state.notes.insert(note, at: 0)
state.draft = ""
return .run { [notes = state.notes] _ in
try storage.save(notes, forKey: "notes")
}
case .updateDraft(let text):
state.draft = text
return .none
case .deleteNote(let id):
state.notes.removeAll { $0.id == id }
return .run { [notes = state.notes] _ in
try storage.save(notes, forKey: "notes")
}
case .loaded(let notes):
state.notes = notes
return .none
}
}
}
Build the UI
PrismUI provides ready-made components with design tokens and theming.NotesView.swift
import Prism
struct NotesView: View {
let store: PrismStore<NotesFeature.State, NotesFeature.Action>
var body: some View {
NavigationStack {
VStack(spacing: 0) {
// Input area
HStack {
PrismTextField("New note...", text: Binding(
get: { store.state.draft },
set: { store.send(.updateDraft($0)) }
))
PrismButton("Add", variant: .filled) {
store.send(.addNote)
}
.disabled(store.state.draft.isEmpty)
}
.padding()
// Notes list
List {
ForEach(store.state.notes) { note in
PrismListItem(note.title, subtitle: note.createdAt.formatted())
}
.onDelete { indexSet in
for index in indexSet {
store.send(.deleteNote(store.state.notes[index].id))
}
}
}
}
.navigationTitle("Notes")
}
}
}
Wire It Up
NotesApp.swift
import Prism
@main
struct NotesApp: App {
let store = PrismStore(
initialState: NotesFeature.State(),
reducer: NotesFeature()
)
var body: some Scene {
WindowGroup {
NotesView(store: store)
.prismTheme(.default)
.task {
let storage = PrismDefaultsStore(suite: "com.app.notes")
if let notes: [Note] = try? storage.load(forKey: "notes") {
store.send(.loaded(notes))
}
}
}
}
}
Run It
swift run
open Package.swiftWhat You Used
| Module | What for |
|---|---|
| PrismArchitecture | PrismStore, PrismReducer, PrismEffect |
| PrismUI | PrismButton, PrismTextField, PrismListItem |
| PrismStorage | PrismDefaultsStore for persistence |
Next Steps — Client
Add Networking
Sync notes to a remote API with type-safe endpoints and retry policies.
Middleware
Add logging, analytics, or undo/redo middleware to your store.
More Components
Explore cards, chips, charts, forms, and 100+ more SwiftUI components.
Encrypted Storage
Wrap your store with AES-GCM encryption for sensitive data.
Build a bookmark manager API with PrismServer — routing, database, validation, and middleware.
Requires Swift 6.3+, macOS 26+. See Installation for details.
Create Your Project
Initialize a Swift package
mkdir bookmarks-api && cd bookmarks-api
swift package init --type executable
Add Prism to Package.swift
Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "bookmarks-api",
platforms: [.macOS(.v14)],
dependencies: [
.package(url: "https://github.com/byescaleira/prism.git", from: "4.4.0")
],
targets: [
.executableTarget(
name: "bookmarks-api",
dependencies: [
.product(name: "PrismServer", package: "prism")
]
)
]
)
Write Your Server
ReplaceSources/main.swift:Sources/main.swift
import PrismServer
import Foundation
let server = PrismHTTPServer(port: 8080)
let db = try PrismDatabase(path: "bookmarks.db")
// Migrations
let migrator = PrismMigrator(database: db)
try await migrator.migrate([
PrismMigration(
name: "create_bookmarks",
up: """
CREATE TABLE bookmarks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
url TEXT NOT NULL,
tags TEXT DEFAULT '',
created_at TEXT DEFAULT (datetime('now'))
)
""",
down: "DROP TABLE bookmarks"
)
])
// Middleware
await server.use(PrismCORSMiddleware())
await server.use(PrismLoggingMiddleware())
await server.use(PrismErrorMiddleware(includeStackTrace: true))
// List
await server.get("/bookmarks") { request in
let rows = try await db.table("bookmarks")
.orderBy("created_at", ascending: false)
.get()
return .json(rows)
}
// Create with validation
await server.post("/bookmarks") { request in
let validation = request.validateJSON { v in
v.field("title", .required, .minLength(1), .maxLength(200))
v.field("url", .required, .url)
}
if let errorResponse = validation.errorResponse() { return errorResponse }
guard let body = request.body,
let json = try? JSONSerialization.jsonObject(with: body) as? [String: Any] else {
return PrismHTTPResponse(status: .badRequest, body: .text("Invalid JSON"))
}
let title = json["title"] as? String ?? ""
let url = json["url"] as? String ?? ""
let tags = json["tags"] as? String ?? ""
let id = try await db.table("bookmarks").insert([
"title": .text(title),
"url": .text(url),
"tags": .text(tags)
])
return .json(["id": id, "created": true], status: .created)
}
// Get one
await server.get("/bookmarks/:id") { request in
let id = request.parameters["id"]!
guard let row = try await db.table("bookmarks")
.where("id", .text(id))
.first() else {
throw PrismAppError.notFound("Bookmark not found")
}
return .json(row)
}
// Delete
await server.delete("/bookmarks/:id") { request in
let id = request.parameters["id"]!
let changes = try await db.table("bookmarks")
.where("id", .text(id))
.delete()
guard changes > 0 else {
throw PrismAppError.notFound("Bookmark not found")
}
return .json(["deleted": true])
}
print("Bookmarks API running on http://localhost:8080")
try await server.start()
try await Task.sleep(for: .seconds(.max))
Use
":memory:" instead of "bookmarks.db" for an in-memory database during development. See Database overview for options.Run It
swift run
Test with curl
curl -X POST http://localhost:8080/bookmarks \
-H "Content-Type: application/json" \
-d '{"title": "Swift.org", "url": "https://swift.org", "tags": "swift"}'
What You Used
| Module | What for |
|---|---|
| PrismServer | HTTP server, routing, request/response |
| Middleware | CORS, logging, error handling |
| Database | SQLite, query builder, migrations |
| Validation | Request body validation rules |
Next Steps — Server
Add Authentication
Protect endpoints with bearer tokens and session cookies.
Add WebSockets
Build real-time features with WebSocket rooms.
Add GraphQL
Expose data through a GraphQL endpoint alongside REST.
Query Builder
Go deeper with relationships, connection pools, and models.