Lưu trữ dữ liệu là yếu tố quan trọng khi thiết kế ứng dụng. SQLite là một hệ quản trị cơ sở dữ liệu quan hệ nhẹ, nhúng trong thư viện C, phù hợp cho lưu trữ cục bộ trên iOS.
Bài viết hướng dẫn cách truy xuất SQLite đơn giản nhất bằng Swift, tạo app demo: nhập tên → lưu vào DB → hiển thị danh sách.
Các bước thực hiện
Bước 1: Tạo dự án
Xcode → Single View App → Tên: "DBSqlite".
Bước 2: Thiết kế UI
Mở Main.storyboard → Thêm:
- 1
UITextField(nhập tên) - 2
UIButton: "Insert" (lưu) và "Show Data" (hiển thị)
Tạo @IBOutlet cho text field (nameTextField), @IBAction cho 2 button (btnInsert, btnShowData).

Bước 3: Import SQLite3 và tạo kết nối
import UIKit
import SQLite3
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
var db: OpaquePointer?
override func viewDidLoad() {
super.viewDidLoad()
// 1. Tạo đường dẫn file .sqlite trong Document Directory
let fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("user_name.sqlite")
print("SQLite path: \(fileURL.path)") // Debug: in ra đường dẫn file
// 2. Mở database
if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
print("Lỗi mở database")
return
}
// 3. Tạo bảng nếu chưa tồn tại
let createTableQuery = """
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
)
"""
if sqlite3_exec(db, createTableQuery, nil, nil, nil) != SQLITE_OK {
let errorMsg = String(cString: sqlite3_errmsg(db)!)
print("Lỗi tạo bảng: \(errorMsg)")
}
}
Bước 4: Chèn dữ liệu (Insert)
@IBAction func btnInsert(_ sender: Any) {
guard let userName = nameTextField.text, !userName.isEmpty else { return }
var statement: OpaquePointer?
let query = "INSERT INTO users (name) VALUES (?)"
// 1. Prepare statement
if sqlite3_prepare_v2(db, query, -1, &statement, nil) != SQLITE_OK {
let errMsg = String(cString: sqlite3_errmsg(db)!)
print("Lỗi prepare insert: \(errMsg)")
return
}
// 2. Bind tham số
if sqlite3_bind_text(statement, 1, (userName as NSString).utf8String, -1, nil) != SQLITE_OK {
let errMsg = String(cString: sqlite3_errmsg(db)!)
print("Lỗi bind text: \(errMsg)")
return
}
// 3. Thực thi
if sqlite3_step(statement) != SQLITE_DONE {
let errMsg = String(cString: sqlite3_errmsg(db)!)
print("Lỗi insert: \(errMsg)")
} else {
print("Đã lưu: \(userName)")
nameTextField.text = ""
}
// 4. Finalize
sqlite3_finalize(statement)
}
Bước 5: Truy vấn dữ liệu (Select)
@IBAction func btnShowData(_ sender: Any) {
let query = "SELECT * FROM users"
var statement: OpaquePointer?
if sqlite3_prepare_v2(db, query, -1, &statement, nil) != SQLITE_OK {
let errMsg = String(cString: sqlite3_errmsg(db)!)
print("Lỗi prepare select: \(errMsg)")
return
}
print("=== Danh sách users ===")
while sqlite3_step(statement) == SQLITE_ROW {
// Cột 0: id, Cột 1: name
let id = sqlite3_column_int(statement, 0)
let name = String(cString: sqlite3_column_text(statement, 1))
print("ID: \(id), Name: \(name)")
}
sqlite3_finalize(statement)
}
Bước 6: Đóng database khi không dùng (AppDelegate)
// AppDelegate.swift
func applicationWillTerminate(_ application: UIApplication) {
if let db = (window?.rootViewController as? ViewController)?.db {
sqlite3_close(db)
}
}
Mã hoàn chỉnh ViewController.swift
import UIKit
import SQLite3
class ViewController: UIViewController {
@IBOutlet weak var nameTextField: UITextField!
var db: OpaquePointer?
override func viewDidLoad() {
super.viewDidLoad()
let fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("user_name.sqlite")
print("DB Path: \(fileURL.path)")
if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
print("Lỗi mở DB")
return
}
let createTable = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)"
if sqlite3_exec(db, createTable, nil, nil, nil) != SQLITE_OK {
let err = String(cString: sqlite3_errmsg(db)!)
print("Lỗi tạo bảng: \(err)")
}
}
@IBAction func btnInsert(_ sender: Any) {
guard let name = nameTextField.text, !name.isEmpty else { return }
var stmt: OpaquePointer?
let sql = "INSERT INTO users (name) VALUES (?)"
if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK {
sqlite3_bind_text(stmt, 1, (name as NSString).utf8String, -1, nil)
if sqlite3_step(stmt) == SQLITE_DONE {
print("Đã lưu: \(name)")
nameTextField.text = ""
}
}
sqlite3_finalize(stmt)
}
@IBAction func btnShowData(_ sender: Any) {
var stmt: OpaquePointer?
let sql = "SELECT * FROM users"
if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK {
while sqlite3_step(stmt) == SQLITE_ROW {
let id = sqlite3_column_int(stmt, 0)
let name = String(cString: sqlite3_column_text(stmt, 1))
print("ID: \(id), Name: \(name)")
}
}
sqlite3_finalize(stmt)
}
deinit {
sqlite3_close(db)
}
}
Kiểm tra file SQLite
Chạy app → Nhập tên → Bấm Insert → Bấm Show Data → Xem console in ra kết quả.
Để xem file DB trực tiếp: Mở Terminal → sqlite3 /path/to/user_name.sqlite → .tables → SELECT * FROM users;


Thư viện khuyến nghị cho Production
- GRDB.swift: Type-safe, Swift-friendly, hỗ trợ migration, observation
- SQLite.swift: Wrapper nhẹ, expressive API
- Realm: Không phải SQLite nhưng phổ biến, dễ dùng hơn
Lưu ý: SQLite3 API là C API, cần quản lý bộ nhớ thủ công (prepare/bind/step/finalize). Thư viện wrapper giúp an toàn và ngắn gọn hơn.