Accessing an SQLite Database in Swift
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
SQLite is a lightweight, file-based database engine embedded directly in iOS and macOS. Swift can access SQLite through the C API (libsqlite3), which ships with every Apple platform — no additional dependencies needed. For most apps, the workflow is: open a database file, execute SQL statements using sqlite3_exec or prepared statements, read results, and close the connection. While the C API works, wrapper libraries like GRDB.swift and SQLite.swift provide safer, more idiomatic Swift interfaces.
Using the C API Directly
Add libsqlite3 to your project by importing the module:
Inserting Data with Prepared Statements
Prepared statements prevent SQL injection and improve performance for repeated inserts:
Querying Data
Using GRDB.swift (Recommended Wrapper)
GRDB.swift provides a type-safe, Swift-native API. Add it via Swift Package Manager:
Bundling a Pre-populated Database
To ship a database with your app, add the .sqlite file to your Xcode project and copy it to the Documents directory on first launch:
Common Pitfalls
- Forgetting
sqlite3_finalize(): Everysqlite3_prepare_v2call must be paired withsqlite3_finalize. Leaking statements causes memory leaks and eventuallySQLITE_BUSYerrors. - Thread safety: SQLite's default serialized mode is safe for multi-threaded reads, but concurrent writes from different threads can cause
SQLITE_BUSY. Use a serialDispatchQueueor GRDB'sDatabaseQueuefor write serialization. - SQL injection with string interpolation: Never use
"INSERT INTO users VALUES ('\(name)')". Always use?parameter binding withsqlite3_bind_text. - Database file location: On iOS, store the database in the Documents or Application Support directory. The app bundle is read-only — you cannot write to a database stored there.
- Not handling
SQLITE_BUSY: When another connection holds a write lock, queries returnSQLITE_BUSY. Implement retry logic or use WAL mode (PRAGMA journal_mode=WAL) for better concurrency.
Summary
- Import
SQLite3to use the C API directly — no extra dependencies needed on Apple platforms - Use prepared statements (
sqlite3_prepare_v2+sqlite3_bind_*) for safe, efficient queries - Always finalize statements and close the database connection to prevent resource leaks
- GRDB.swift provides a type-safe, Codable-compatible Swift wrapper that eliminates most C API boilerplate
- Store database files in the Documents or Application Support directory, not the app bundle
Related reading
- Accessing data on distributed database on OrientDB
- Accessing dict_keys element by index in Python3
- Accessing H2 Console While Running SpringBootTest
- accessing indexes of tf.data.Dataset for deleting and appending data elements
- Accessing Kotlin extension functions from Java
- Accessing localhostport from Android emulator
- Accidentally import the MySQL user table into TiDB, or forget the password
- Acessing Values only at certain indexes using iterators

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.