From swiftui-dev
Integrates DuckDB as an in-process analytical SQL engine for SwiftUI apps to query CSV, Parquet, and SQLite files directly.
How this skill is triggered — by the user, by Claude, or both
Slash command
/swiftui-dev:duckdb-swiftThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Using DuckDB as an in-process analytical SQL engine for SwiftUI applications, enabling SQL queries over CSV, Parquet, and SQLite files.
Using DuckDB as an in-process analytical SQL engine for SwiftUI applications, enabling SQL queries over CSV, Parquet, and SQLite files.
// Package.swift
dependencies: [
.package(url: "https://github.com/duckdb/duckdb-swift", from: "1.0.0")
]
import DuckDB
// In-memory database
let database = try Database(store: .inMemory)
let connection = try database.connect()
// File-backed database
let database = try Database(store: .file(at: "analytics.duckdb"))
// Execute SQL
try connection.execute("CREATE TABLE items (id INTEGER, name VARCHAR, price DECIMAL)")
// Insert data
try connection.execute("""
INSERT INTO items VALUES
(1, 'Widget', 9.99),
(2, 'Gadget', 19.99)
""")
// Query with results
let result = try connection.query("SELECT * FROM items WHERE price > 10")
// Iterate rows
for row in result {
let id: Int32 = row[0]!
let name: String = row[1]!
let price: Double = row[2]!
print("\(id): \(name) - $\(price)")
}
// Column access
let names = result[1].cast(to: String.self)
for name in names {
print(name ?? "NULL")
}
// Query CSV directly
let result = try connection.query("""
SELECT * FROM read_csv('data.csv')
WHERE category = 'Electronics'
ORDER BY price DESC
LIMIT 10
""")
// With options
let result = try connection.query("""
SELECT * FROM read_csv('data.csv',
header = true,
delim = ',',
columns = {'id': 'INTEGER', 'name': 'VARCHAR', 'price': 'DECIMAL'}
)
""")
// Query Parquet directly
let result = try connection.query("""
SELECT
date_trunc('month', timestamp) as month,
SUM(amount) as total
FROM read_parquet('transactions.parquet')
GROUP BY 1
ORDER BY 1
""")
// Filter pushdown (efficient)
let result = try connection.query("""
SELECT * FROM read_parquet('large_file.parquet')
WHERE year = 2024 AND region = 'US'
""")
// Attach SQLite database
try connection.execute("INSTALL sqlite; LOAD sqlite;")
try connection.execute("ATTACH 'existing.sqlite' AS sqlite_db (TYPE sqlite)")
// Query SQLite tables
let result = try connection.query("""
SELECT * FROM sqlite_db.users
WHERE created_at > '2024-01-01'
""")
import SwiftUI
import DuckDB
@Observable
final class AnalyticsViewModel {
var results: [[String: Any]] = []
var isLoading = false
var error: Error?
private let database: Database
private let connection: Connection
init() throws {
database = try Database(store: .inMemory)
connection = try database.connect()
}
func executeQuery(_ sql: String) async {
isLoading = true
defer { isLoading = false }
do {
let result = try connection.query(sql)
results = result.map { row in
var dict: [String: Any] = [:]
for (index, column) in result.columns.enumerated() {
dict[column.name] = row[index]
}
return dict
}
error = nil
} catch {
self.error = error
results = []
}
}
func loadCSV(from url: URL) async throws {
try connection.execute("""
CREATE OR REPLACE TABLE data AS
SELECT * FROM read_csv('\(url.path)')
""")
}
}
struct QueryResultsView: View {
let results: [[String: Any]]
let columns: [String]
var body: some View {
Table(results, columns: columns) { result in
TableRow(result) { columnName in
Text(String(describing: result[columnName] ?? "NULL"))
}
}
}
}
struct SQLEditorView: View {
@State private var viewModel: AnalyticsViewModel
@State private var query = "SELECT * FROM data LIMIT 100"
var body: some View {
VSplitView {
// Query editor
TextEditor(text: $query)
.font(.system(.body, design: .monospaced))
.frame(minHeight: 100)
// Results
if viewModel.isLoading {
ProgressView()
} else if let error = viewModel.error {
Text(error.localizedDescription)
.foregroundStyle(.red)
} else {
ResultsTableView(results: viewModel.results)
}
}
.toolbar {
Button("Run") {
Task {
await viewModel.executeQuery(query)
}
}
.keyboardShortcut(.return, modifiers: .command)
}
}
}
let result = try connection.query("""
SELECT
date,
value,
AVG(value) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as moving_avg_7d
FROM time_series
""")
let result = try connection.query("""
WITH daily_totals AS (
SELECT
DATE_TRUNC('day', timestamp) as day,
SUM(amount) as total
FROM transactions
GROUP BY 1
),
running_totals AS (
SELECT
day,
total,
SUM(total) OVER (ORDER BY day) as cumulative
FROM daily_totals
)
SELECT * FROM running_totals
""")
let result = try connection.query("""
SELECT
category,
COUNT(*) as count,
AVG(price) as avg_price,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price) as median_price,
STDDEV(price) as price_stddev
FROM products
GROUP BY category
HAVING COUNT(*) > 10
ORDER BY avg_price DESC
""")
.file() store for large datasetsimport TabularData
extension DataFrame {
init(duckDBResult: ResultSet) {
var columns: [AnyColumn] = []
for (index, column) in duckDBResult.columns.enumerated() {
switch column.type {
case .integer:
let values = duckDBResult.map { $0[index] as Int64? }
columns.append(Column<Int64?>(name: column.name, contents: values).eraseToAnyColumn())
case .varchar:
let values = duckDBResult.map { $0[index] as String? }
columns.append(Column<String?>(name: column.name, contents: values).eraseToAnyColumn())
case .double:
let values = duckDBResult.map { $0[index] as Double? }
columns.append(Column<Double?>(name: column.name, contents: values).eraseToAnyColumn())
default:
break
}
}
self.init(columns: columns)
}
}
npx claudepluginhub arustydev/agents --plugin swiftui-devDuckDB CLI specialist for SQL analysis, data processing, and file conversion between CSV, Parquet, and JSON formats.
DuckDB CLI specialist for SQL analysis, data processing, and file conversion between CSV, Parquet, and JSON formats.
Guides Swift developers on SQLiteData library for type-safe SQLite persistence, @Table models, @FetchAll/@FetchOne queries, writes, migrations, and CloudKit sync.