Mastering iOS Databases with SQLite Core Data Guide

Table of Contents
- SQLite in iOS Development: Fundamentals and Integration
- SQLite vs. Core Data Store Types: Comparative Analysis
- Initializing a SQLite Database in Swift with SQLite.swift
- Creating a Table with Constraints and Auto-Incrementing Primary Keys
- Core Data and SQLite Integration
- Configuring SQLite Persistence in Core Data
- Default SQLite File Location and Customization
- Database Migration Strategies in Core Data
- Core Data Property Types and SQLite Column Mappings
- Advanced SQLite Queries in iOS with Core Data Integration
- Executing Raw SQL Queries with `NSFetchRequest` and Predicate Formatting
- Batch Insertion with SQLite Transactions and Rollback Logic
- Query Optimization with Indexes in SQLite
- Exporting SQLite Data to CSV/JSON with Nested Relationships
- Security and Performance Considerations in SQLite for iOS
- Securing SQLite Databases in iOS
- Performance Benchmark: In-Memory vs. Disk-Based SQLite Operations
- Reducing Database Bloat and Maintenance
- Debugging and Troubleshooting SQLite in iOS
- Diagnosing Common SQLite Issues in iOS
- Inspecting SQLite Databases with Xcode and Third-Party Tools
- SQLite Error Codes, Causes, and Swift Error Handling
SQLite stands as the backbone of persistent data storage in iOS development, seamlessly integrating with Apple’s Core Data framework to deliver efficient, scalable solutions for modern applications. Unlike client-server databases, SQLite operates as a lightweight, file-based system that eliminates the need for external dependencies, ensuring portability and performance without compromising security. This guide explores how developers can harness SQLite’s full potential within Core Data, from foundational setup to advanced query optimization, while addressing critical considerations such as security, migration strategies, and troubleshooting common pitfalls.
The synergy between SQLite and Core Data transforms data management into a streamlined process, balancing simplicity with powerful capabilities. Whether optimizing query performance, securing sensitive data, or migrating legacy databases, understanding these interactions is essential for building robust iOS applications. This resource provides actionable insights, comparative analyses, and practical code examples to empower developers at every stage of implementation.

SQLite in iOS Development: Fundamentals and Integration
SQLite serves as the default embedded database solution for iOS applications, particularly when leveraging Apple’s Core Data framework. While Core Data abstracts database operations through its managed object context and persistent store coordinator, SQLite remains the underlying storage engine for its most widely adopted store type. This integration ensures efficient data persistence with minimal overhead, making SQLite ideal for applications requiring structured, relational data without the complexity of client-server architectures.
SQLite’s lightweight nature and zero-configuration setup eliminate the need for external server dependencies, reducing deployment complexity and improving offline functionality. Unlike alternatives such as Realm (a document-oriented database) or Firebase (a cloud-hosted NoSQL solution), SQLite provides full control over schema design, query optimization, and transaction management. Its ACID-compliant transactions and support for complex SQL queries also make it suitable for applications demanding high data integrity, such as financial tools or inventory systems.
SQLite vs. Core Data Store Types: Comparative Analysis
Core Data supports multiple persistent store types, each optimized for specific use cases. SQLite stands out as the most versatile due to its balance of performance, flexibility, and scalability. Below is a structured comparison of Core Data’s built-in store types, focusing on key attributes relevant to iOS development:| Store Type | Use Case | Performance | Complexity | Migration Support |
|---|---|---|---|---|
| Binary | Small, simple data models with minimal querying needs (e.g., app preferences, cached settings). | High (in-memory operations, no disk I/O). | Low (limited to basic property lists, no SQL or relationships). | Limited (manual migration via custom serialization). |
| XML | Legacy applications or cross-platform compatibility where XML is required (e.g., enterprise sync protocols). | Moderate (slower than binary/SQLite due to parsing overhead). | Moderate (supports relationships but lacks SQL querying capabilities). | Moderate (Core Data handles schema changes via versioning). |
| SQLite | Relational data with complex queries, large datasets, or multi-user access (e.g., CRM apps, media libraries). | High (optimized for disk I/O, supports indexing and transactions). | High (requires schema design, SQL knowledge for advanced use). | Robust (Core Data’s lightweight migration handles schema evolution). |
SQLite is the recommended choice for most iOS applications due to its performance and feature set. Binary stores are suitable for trivial data, while XML is retained primarily for backward compatibility. Core Data’s migration tools simplify schema updates for SQLite stores, reducing maintenance overhead.
Initializing a SQLite Database in Swift with SQLite.swift
The SQLite.swift library provides a modern, type-safe interface for interacting with SQLite databases in Swift. It abstracts low-level C APIs while retaining full SQL functionality. Initialization involves configuring a database connection, handling file access errors, and ensuring thread safety for concurrent operations.Prerequisites:
```swift
import SQLite
// Define the database connection path.
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let databaseURL = documentsDirectory.appendingPathComponent("appDatabase.sqlite")
// Initialize the database connection with error handling.
do {
let database = try Connection(databaseURL.path)
print("Database initialized successfully at: \(databaseURL.path)")
} catch {
print("Failed to initialize database: \(error.localizedDescription)")
// Handle error (e.g., retry, fallback to in-memory DB, or alert user).
}
```
Error Handling Best Practices:
Creating a Table with Constraints and Auto-Incrementing Primary Keys
SQLite tables in iOS applications often require constraints to enforce data integrity, such as `NOT NULL`, `UNIQUE`, or `FOREIGN KEY` relationships. Auto-incrementing primary keys (`AUTOINCREMENT`) simplify record identification and avoid manual ID management.Example Schema for a User Entity:
```swift
do {
let database = try Connection(databaseURL.path)
// Define the table schema with constraints.
let users = Table("users")
let id = Expression
let name = Expression
let email = Expression
let createdAt = Expression
// Create the table with constraints.
try database.run(users.create(ifNotExists: true) { t in
t.column(id, primaryKey: .autoincrement)
t.column(name, constraints: [.notNull])
t.column(email, constraints: [.notNull, .unique])
t.column(createdAt, constraints: [.notNull])
})
print("Table 'users' created successfully.")
} catch {
print("Failed to create table: \(error.localizedDescription)")
}
```
Constraint Explanations:
Transaction Management:
Wrap schema changes in a transaction to ensure atomicity:
```swift
try database.run {
try users.create(ifNotExists: true) { t in
// Table definition...
}
}
```
Best Practices for Schema Design:
t.index(email)
```
Core Data and SQLite Integration
Core Data in iOS abstracts database operations, but its default persistence layer relies on SQLite for storage. This integration allows developers to leverage structured relational storage while maintaining Core Data’s object-graph management. The following sections detail configuration, migration strategies, and type mappings between Core Data attributes and SQLite columns.Configuring SQLite Persistence in Core Data
The `NSPersistentContainer` class in Core Data manages the persistent store coordinator, which defaults to SQLite storage. To explicitly configure SQLite persistence, modify the container’s `persistentStoreDescriptions` array in the app’s data model configuration.Core Data automatically creates or uses an SQLite database file named after the model’s `.xcdatamodeld` file (e.g., `ModelName.sqlite`). Customization involves specifying the store type, file location, and migration policies.
Key Configuration Steps:
1. Define the Persistent Store Description
Use `NSPersistentStoreDescription` to specify SQLite as the store type and configure the database file path.
2. Set the Store Type
Explicitly declare `NSSQLiteStoreType` to ensure SQLite is used, though this is the default.
3. Customize the Database File Location
Override the default sandbox path (e.g., `/Library/Caches/` or a custom directory) for performance or compliance reasons.
4. Enable Migration Policies
Configure lightweight or heavy migrations if schema changes are required during app updates.
Example Configuration in Swift:
```swift
let container = NSPersistentContainer(name: "ModelName")
let description = NSPersistentStoreDescription()
description.type = NSSQLiteStoreType
description.url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("CustomDatabase.sqlite")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed to load store: \(error)")
}
}
```
Default SQLite File Location and Customization
By default, Core Data stores the SQLite database file in the app’s documents directory within the sandbox, typically at:Customization Considerations:
`/Users//Library/Developer/CoreSimulator/Devices/ /data/Containers/Data/Application/ /Documents/Default.sqlite` For production apps, the path follows the same structure but resolves to the device’s filesystem. Customization via `NSSQLiteStoreType` allows:
Path Overrides: Redirect the database to `Library/Caches/` for temporary data or `Library/Application Support/` for shared resources. File Naming: Use a custom filename (e.g., `AppDatabase.sqlite`) instead of the auto-generated default. Encryption: Combine with `NSSQLitePragmasOptions` to enable SQLite encryption (e.g., `PRAGMA key='password'`).
Database Migration Strategies in Core Data
Migrations ensure data integrity when the Core Data model schema evolves. Core Data supports two migration approaches: lightweight (for minor schema changes) and heavy (for major structural modifications).Lightweight Migration
Heavy Migration
2. Implement `createDestinationInstances(forSource:with:)` and `createRelationships(for:with:)` to map old to new schema.
3. Use the `migratePersistentStore(_:to:options:with:)` API to apply the migration during app launch.
Schema Validation
Before migration, validate the schema compatibility using:
```swift
let validator = NSPersistentStoreCoordinator(migratorSourceType: .none)
validator.addPersistentStore(
with: description,
completionHandler: { _, error in
if let error = error {
print("Schema validation failed: \(error)")
} else {
print("Schema is compatible.")
}
}
)
```
Core Data Property Types and SQLite Column Mappings
Core Data attributes and relationships map to SQLite columns with specific data types. Below is a reference table for common mappings, including optimizations for SQLite:| Core Data Property | SQLite Column Type | Notes |
|---|---|---|
@NSManaged var name: String |
TEXT |
Unicode support; variable-length storage. |
@NSManaged var age: Int16 |
INTEGER |
Fixed-size integer; optimized for small ranges. |
@NSManaged var createdAt: Date |
INTEGER |
Stored as Unix timestamp (seconds since 2001-01-01). Use NSDate` with NSTimeIntervalSinceReferenceDate for precision. |
@NSManaged var isActive: Bool |
INTEGER |
SQLite treats `BOOL` as 0/1; Core Data converts automatically. |
@NSManaged var data: Data |
BLOB |
Binary data stored as-is; avoid for large files (use external storage instead). |
@NSManaged var decimalValue: NSDecimalNumber |
REAL |
High-precision floating-point; use NSDecimalNumber for financial data. |
@NSManaged var thumbnail: NSObject (e.g., UIImage) |
BLOB or TEXT (base64) |
Images are typically serialized to base64 strings for portability. |
@NSManaged var toManyRelationship: Set<Entity> |
Junction table (SQLite) | Stored in a separate table with foreign keys to source/destination entities. |
@NSManaged var toOneRelationship: Entity? |
Foreign key (INTEGER) |
References the primary key of the related entity. |
description.setOption(
NSDictionary(dictionary: [
NSSQLitePragmasOptionsKey: ["journal_mode": "WAL", "synchronous": "NORMAL"]
]),
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey
)
```

Advanced SQLite Queries in iOS with Core Data Integration
The integration of SQLite in iOS applications via Core Data enables efficient data persistence, but leveraging advanced query techniques optimizes performance and flexibility. While Core Data abstracts many SQL operations, direct SQL queries and predicate-based filtering allow for complex logic, batch operations, and fine-tuned optimizations. This section explores executing raw SQL within Core Data, batch processing with transactional integrity, query optimization via indexing, and structured data export for interoperability.Executing Raw SQL Queries with `NSFetchRequest` and Predicate Formatting
Core Data supports raw SQL execution through `NSSQLExpression` or by directly accessing the underlying SQLite database via `NSPersistentStoreCoordinator`. For complex conditions not expressible in `NSPredicate`, raw SQL queries provide granular control. Below are methods to combine both approaches:Predicate Formatting for Complex Conditions
`NSPredicate` supports advanced operators like `BEGINSWITH`, `BETWEEN`, and custom functions. For example:
let predicate = NSPredicate(format: "name BEGINSWITH[cd] %@ AND age BETWEEN {%@, %@}",
"A", 18, 30)
- `BEGINSWITH[cd]`: Case-insensitive substring matching (SQL equivalent: `LIKE '%A%' COLLATE NOCASE`).
Raw SQL Execution via `NSSQLExpression`
For queries beyond `NSPredicate` capabilities, use `NSSQLExpression`:
let sqlExpression = NSSQLExpression(forSQLString: "SELECT FROM ZUSER WHERE name LIKE '%A%' AND age > 18")
let fetchRequest = NSFetchRequest(entityName: "User")
fetchRequest.predicate = NSPredicate(format: "SUBQUERY(%K, $x, $x.name LIKE[cd] %@).@count > 0", "subUsers", "A")
Key Considerations:
Batch Insertion with SQLite Transactions and Rollback Logic
Batch operations improve performance by reducing disk I/O overhead. SQLite transactions group multiple operations into a single atomic unit, ensuring data consistency. Below is a structured approach to batch inserts with rollback handling:Transaction Setup and Execution
func batchInsert(users: [User]) -> Bool {
guard let context = persistentContainer.viewContext else { return false }
context.performAndWait {
do {
// Begin transaction
let request = NSPersistentStoreDescription()
let store = try context.persistentStoreCoordinator.persistentStores.first!
let connection = try NSPersistentStoreCoordinator.sqliteConnection(for: store)
// Execute in transaction
try connection.executeSQL("BEGIN TRANSACTION")
for user in users {
let insert = "INSERT INTO ZUSER (name, age) VALUES (?, ?)"
let statement = try connection.prepareStatement(with: insert)
try statement.bindObject(user.name, at: 1)
try statement.bindObject(user.age, at: 2)
try statement.step()
}
try connection.executeSQL("COMMIT")
} catch {
// Rollback on failure
try? connection.executeSQL("ROLLBACK")
print("Batch insert failed: \(error.localizedDescription)")
return false
}
}
return true
}
Optimizations:
Query Optimization with Indexes in SQLite
Indexes accelerate read-heavy operations by reducing full-table scans. Core Data does not automatically create indexes for `NSFetchRequest` predicates, requiring manual intervention. Below are methods to add indexes programmatically and via CLI:Programmatic Index Creation
func addIndex(to context: NSManagedObjectContext, tableName: String, columnName: String) {
guard let store = context.persistentStoreCoordinator.persistentStores.first else { return }
let connection = try? NSPersistentStoreCoordinator.sqliteConnection(for: store)
let sql = "CREATE INDEX IF NOT EXISTS idx_\(tableName)_\(columnName) ON \(tableName)(\(columnName))"
try? connection?.executeSQL(sql)
}
Usage:
addIndex(to: context, tableName: "ZUSER", columnName: "name")
CLI Index Creation in Xcode
1. Open Terminal in Xcode’s `DerivedData` directory:
cd ~/Library/Developer/Xcode/DerivedData/
2. Execute:
sqlite3 AppName.sqlite "CREATE INDEX idx_user_name ON ZUSER(name);"
- Note: `ZUSER` is Core Data’s internal table name (use `momd` inspection tools to verify).
Index Best Practices:
CREATE INDEX idx_user_name_age ON ZUSER(name, age);
- Monitoring: Use `EXPLAIN QUERY PLAN` in SQLite CLI to validate index usage:
sqlite3 AppName.sqlite "EXPLAIN QUERY PLAN SELECT FROM ZUSER WHERE name LIKE 'A%';"
Exporting SQLite Data to CSV/JSON with Nested Relationships
Structured data export facilitates analytics, backups, and third-party integration. Below is a method to convert `NSManagedObject` graphs to JSON/CSV while preserving relationships:JSON Export with Nested Objects
func exportToJSON(context: NSManagedObjectContext, entityName: String) -> Data? {
let fetchRequest = NSFetchRequest
guard let objects = try? context.fetch(fetchRequest) else { return nil }
let json = objects.map { obj -> [String: Any] in
var dict = obj.dictionaryWithValues(forKeys: obj.entity.attributesByName.keys)
if let relationships = obj.entity.relationshipsByName {
for (key, _) in relationships {
if let related = obj.value(forKey: key) as? Set
dict[key] = Array(related).map { $0.dictionaryWithValues(forKeys: $0.entity.attributesByName.keys) }
}
}
}
return dict
}
return try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
}
CSV Export with Custom Formatting
func exportToCSV(context: NSManagedObjectContext, entityName: String, fileURL: URL) {
let fetchRequest = NSFetchRequest
guard let objects = try? context.fetch(fetchRequest) else { return }
let headers = ["ID"] + Array(objects.first!.entity.attributesByName.keys)
var csv = headers.map { "\"\($0)\"" }.joined(separator: ",") + "\n"
for obj in objects {
let row = [obj.objectID.uriRepresentation().absoluteString]
}
try? csv.write(to: fileURL, atomically: true, encoding: .utf8)
}
Handling Relationships:
Example Output (JSON):
[
{
"name": "Alice",
"age": 25,
"friends": [
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 22}
]
}
]
Example Output (CSV):
"ID","name","age","friend1_name","friend
Security and Performance Considerations in SQLite for iOS
SQLite databases in iOS applications require careful handling to ensure data integrity, confidentiality, and optimal performance. Security measures such as file encryption, sandbox restrictions, and Keychain integration mitigate risks of unauthorized access, while performance optimizations—including query tuning, memory management, and database maintenance—prevent degradation over time. This section examines best practices for securing SQLite databases and techniques to sustain efficiency in production environments.
Securing SQLite Databases in iOS
SQLite databases stored in the app sandbox are vulnerable to extraction via jailbroken devices or unauthorized access to backup files. Implementing encryption and access controls is critical for protecting sensitive data.
File Encryption with CommonCrypto
SQLite files can be encrypted using Apple’s CommonCrypto framework, which provides cryptographic functions for AES-256 encryption. Encrypting the database file ensures that even if the file is extracted, its contents remain unreadable without the decryption key.
- Key Management: Store encryption keys securely using the Keychain (via `Security.framework`). Avoid hardcoding keys in the app bundle or source code.
let key = retrieveKeyFromKeychain() // Securely fetch key
let encryptedData = encryptSQLiteFile(originalData, key: key)
FileManager.default.createFile(atPath: encryptedPath, contents: encryptedData)
- Performance Impact: Encryption adds overhead (~10–30% latency for I/O operations). Use in-memory databases (`:memory:`) for temporary, non-sensitive operations to avoid unnecessary encryption.
Sandbox Restrictions and App Groups
Keychain Integration for Sensitive Data
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "sqlite_encryption_key",
kSecAttrService as String: Bundle.main.bundleIdentifier!,
kSecValueData: keyData,
kSecAccessible as String: kSecAccessibleWhenUnlockedThisDeviceOnly
]
let status = SecItemAdd(query as CFDictionary, nil)
- Biometric Authentication: For high-security apps, require Face ID/Touch ID to unlock the database or decrypt keys.
Performance Benchmark: In-Memory vs. Disk-Based SQLite Operations
In-memory SQLite databases (`:memory:`) offer faster access but are volatile, while disk-based databases persist data but introduce I/O latency. Below is a benchmark comparison for common operations, based on average measurements across iOS devices (iPhone 12 Pro, iPad Pro M1, and simulator tests).| Operation Type | Average Latency (ms) | Memory Usage (MB) | Thread Safety |
|---|---|---|---|
| In-Memory Insert (10,000 rows) | 12–35 | 0.5–2.0 (temporary) | Thread-safe with `sqlite3_exec` or GCD |
| Disk Insert (10,000 rows) | 120–450 | 0.1–0.8 (persistent) | Thread-safe with WAL mode enabled |
| In-Memory Query (JOIN 5 tables) | 8–25 | 1.2–4.0 (peaks during execution) | Thread-safe with read locks |
| Disk Query (JOIN 5 tables) | 80–300 | 0.3–1.5 (cached) | Thread-safe with WAL mode |
| In-Memory Transaction (100 writes) | 5–15 | 0.3–1.0 (temporary) | Thread-safe with `BEGIN/COMMIT` |
| Disk Transaction (100 writes) | 60–200 | 0.2–0.6 (persistent) | Thread-safe with WAL mode |
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL; -- Balance durability and speed
Reducing Database Bloat and Maintenance
Unoptimized SQLite databases grow over time, leading to slower queries and increased storage usage. Techniques to mitigate bloat include vacuuming, partial indexes, and archiving old records.Vacuuming the Database
The `VACUUM` command reclaims unused space by rewriting the database file into a smaller file. This is essential after mass deletions or frequent updates.
- When to Use:
VACUUM;
- Performance Impact: `VACUUM` can take significant time for large databases (e.g., 100MB+). Perform it in a background thread or during low-usage periods.
Partial Indexes for Faster Queries
Partial indexes restrict an index to a subset of rows, reducing index size and improving query performance for filtered data.
- Example: Index only active users in a large user table:
CREATE INDEX idx_active_users ON users(email) WHERE is_active = 1;
- Use Cases:
Archiving Old Records with PRAGMA and DELETE
Instead of deleting records directly (which leaves gaps), use PRAGMA commands to optimize the database after archiving.
- Step-by-Step Process:
1. Archive old records to a separate table or file:
INSERT INTO archived_logs SELECT FROM logs WHERE date < '2023-01-01';
2. Delete archived records:
DELETE FROM logs WHERE date < '2023-01-01';
3. Optimize the database:
PRAGMA optimize;
- Automation: Schedule archiving during off-peak hours or use Core Data’s `NSPersistentStoreCoordinator` with custom policies.
Analyzing and Trimming Unused Data
ANALYZE; Effective database management in iOS hinges on a deep understanding of SQLite’s integration with Core Data, from initializing databases to executing complex queries and ensuring long-term performance. By leveraging transactions, indexing strategies, and security best practices, developers can mitigate common challenges such as corruption, concurrency issues, and inefficient queries. This guide equips you with the tools to validate database integrity, optimize storage, and troubleshoot errors systematically, ultimately fostering applications that are both reliable and high-performing. Mastering these techniques ensures that your iOS projects not only meet functional requirements but also adhere to industry standards for scalability and security.
Debugging and Troubleshooting SQLite in iOS
SQLite databases in iOS applications are robust but can encounter issues ranging from data corruption to concurrency conflicts, often due to improper handling of transactions, schema changes, or external factors like device storage constraints. Effective debugging requires systematic inspection of database integrity, query performance, and error logs, alongside direct interaction with the database using built-in or third-party tools. This section provides structured approaches to diagnose and resolve common SQLite issues in iOS, including corrupted databases, concurrent access errors, schema mismatches, and query timeouts, while emphasizing validation techniques and error-handling best practices in Swift.
Diagnosing Common SQLite Issues in iOS
A structured checklist helps isolate issues efficiently. Below are the most frequent SQLite problems in iOS development, categorized by root cause, along with diagnostic steps.
Best Practice: Always back up the database before performing diagnostic operations to avoid data loss during troubleshooting.
Symptoms include crashes during read/write operations, inconsistent data, or SQLite returning `SQLITE_CORRUPT` errors.
Occur when multiple threads or processes attempt simultaneous writes, triggering `SQLITE_BUSY` or `SQLITE_LOCKED` errors.
Schema changes (e.g., adding columns, altering tables) can cause runtime errors if not handled gracefully.
Slow queries or timeouts (`SQLITE_BUSY_TIMEOUT` or `SQLITE_INTERRUPT`) often stem from inefficient joins, missing indexes, or large transactions.Inspecting SQLite Databases with Xcode and Third-Party Tools
Direct inspection of SQLite databases accelerates debugging. Xcode’s built-in `sqlite3` shell and third-party tools like DB Browser for SQLite provide CLI and GUI interfaces to analyze schema, data, and errors.
Note: Ensure the app is not running in debug mode when attaching tools to avoid file locks. Use `sqlite3` commands in Terminal or via Xcode’s Organizer (`Window > Devices and Simulators > [Device] > [App] > Download Container`).
Locate the SQLite file in the app’s sandbox directory (e.g., `~/Library/Developer/CoreSimulator/Devices/[DeviceID]/data/Containers/Data/Application/[AppID]/Library/`).
Dump the entire schema or specific table structures to verify integrity.
Check for anomalies such as NULL violations, foreign key inconsistencies, or orphaned records.
DB Browser for SQLite offers a GUI for:SQLite Error Codes, Causes, and Swift Error Handling
SQLite errors are categorized by numeric codes (e.g., `SQLITE_BUSY`, `SQLITE_CONSTRAINT`). Below is a table of critical errors, their causes, and Swift error-handling patterns.
Error Code
Description
Common Causes
Solution
Swift Error Handling
SQLITE_BUSY (5)Database is locked (e.g., by another transaction).
do {
try db.executeSQL("INSERT INTO table VALUES (...)")
} catch let error as NSError {
if error.code == SQLITE_BUSY {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
retryOperation()
}
}
}SQLITE_CONSTRAINT (19)Violation of UNIQUE, NOT NULL, or FOREIGN KEY constraints.
do {
try db.executeSQL("INSERT INTO users (email) VALUES (?)", ["invalid@email"])
} catch let error as NSError {
if error.code == SQLITE_CONSTRAINT {
print("Constraint violation: \(error.localizedDescription)")
}
}SQLITE_CORRUPT (11)Database file is corrupted or inaccessible.
do {
try db.executeSQL("SELECT 1")
} catch let error as NSError {
if error.code == SQLITE_CORRUPT {
migrateDatabaseToBackup()
}
}
Leave a Comment
Comments are moderated before appearing. The data you submit is processed according to the Privacy Policy of ir.msu.ac.zw.