FMDB profile
FMDB is an Objective-C wrapper built on SQLite. It is open source and very easy to set up. It is the only Library that is this good. (If you know of other libraries that are better, please share them with me in the comments. I’d love to try them out!)
Set up the
Let’s create a new Xcode project that I’ll call SQLiteIntro.
This App won’t be too complicated, because I just want to give you a quick introduction to SQLite and give you a quick idea of how to use SQL in your Swift project.
The wrapper
It is a good practice to separate logic in specialized categories or structures. In this example, we are using a SQL database, so we will create a category to abstract some data-layer logic and make the code more concise.
final class DataWrapper: ObservableObject {
private let db: FMDatabase
init(fileName: String = "test") {
// 1 - Get filePath of the SQLite file
let fileURL = try! FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("\(fileName).sqlite")
// 2 - Create FMDatabase from filePath
let db = FMDatabase(url: fileURL)
// 3 - Open connection to database
guard db.open() else {
fatalError("Unable to open database")
}
// 4 - Initial table creation
do {
try db.executeUpdate("create table if not exists users(username varchar(255) primary key, age integer)", values: nil)
} catch {
fatalError("cannot execute query")
}
self.db = db
}
}
Copy the code
This is the beginning of our code, very simple!
The code is straightforward: after the DataWrapper class is first created, it looks for the database file, and if the file doesn’t exist, FMDB creates a database at that path. Finally, it opens the connection to the database and creates the User table.
Model (Model)
Next we will set up a User structure to handle database records. In the sample App, we’ll add some other JSON-related stuff. We’ll use some Web API later to create randomly named users.
struct User: Hashable, Decodable { let username: String let age: Int init(username: String, age: Int) { self.username = username self.age = age } init? (from result: FMResultSet) { if let username = result.string(forColumn: "username") { self.username = username self.age = Int(result.int(forColumn: "age")) } else { return nil } } private enum CodingKeys : String, CodingKey { case username = "first_name" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) username = try container.decode(String.self, forKey: .username) age = Int.random(in: 1.. < 100)}}Copy the code
Every time we make a query from the database, even if there is only one result or no result, we get an FMResultSet. Therefore, a special init function is useful in this case to handle all the setup logic.
Combine and MVVM
Since I’m using SwiftUI, I want the DataWrapper to be reactive and notify the view of possible changes in the database. Let’s go back to the DataWrapper and add the @Published User array so that users can be displayed in a List.
final class DataWrapper: ObservableObject {
private let db: FMDatabase
@Published var users = [User]()
...
}
Copy the code
We want to get the User from the database and publish it as soon as the database is opened. Therefore, we need to create a method to query all Users and set them to the Users variable of DataWrapper after the database is initialized.
func getAllUsers() -> [User] {
var users = [User]()
do {
let result = try db.executeQuery("select username, age from users", values: nil)
while result.next() {
if let user = User(from: result) {
users.append(user)
}
}
return users
} catch {
return users
}
}
Copy the code
Then, place this code at the end of the DataWrapper init method:
users = getAllUsers()
Copy the code
Now, when we start DataWrapper for the first time, DataWrapper will automatically get all the users that are available for SwiftUI.
Next, let’s create an INSERT function. We’ll use it later.
func insert(_ user: User) {
do {
try db.executeUpdate(
"""
insert into users (username, age)
values (?, ?)
""",
values: [user.username, user.age]
)
users.append(user)
} catch {
fatalError("cannot insert user: \(error)")
}
}
Copy the code
Simple SwiftUI view
I want to create a List of all users in the database, and create a simple function to get a random user name from the Web API and insert the new user into the database.
struct ContentView: View {
@EnvironmentObject var db: DataWrapper
var body: some View {
NavigationView {
List(db.users, id: \.self) { user in
HStack {
Text(user.username)
Spacer()
Text("\(user.age)")
}
}
.navigationTitle("Users")
.toolbar {
ToolbarItem(id: "plus", placement: .navigationBarTrailing, showsByDefault: true) {
Button(action: {
createRandomUser()
}, label: {
Image(systemName: "plus")
})
}
}
}
}
private func createRandomUser() {
let url = URL(string: "[https://random-data-](https://random-data-api.com/api/name/random_name)
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
fatalError("No data")
}
DispatchQueue.main.async {
let user = try! JSONDecoder().decode(User.self, from: data)
db.insert(user)
}
}
task.resume()
}
}
Copy the code
If we execute our App now, we’ll see an empty list. But just click the plus sign on the top right to add content to the database, and the name of the list will appear in your list in real time.
conclusion
This article is a very simple example of another way to store data in a familiar SQLite database, and you can see that the App performs much better than the CoreData version.
If you want more control over your data, SQLite and SQL won’t disappoint! SQLite can greatly improve performance for apps that require sophisticated control and query optimization. Synchronizing data with CloudKit will also be easier because now we only need to synchronize SQLite files without dealing with other CoreData Tables and different versions.
Here are some interview related tips as well!
-
1.BAT and other major manufacturers iOS interview questions + answers
-
2.Must-read books for Advanced Development in iOS (Classics must Read)
-
3.IOS Development Advanced Interview “resume creation” guide
-
(4)IOS Interview Process to the basics