Usar SQLite en iOS para procesar datos: mejorar el rendimiento de la aplicación

Introducción a FMDB

FMDB es un contenedor de Objective-C basado en SQLite. Es de código abierto y fácil de configurar. Se puede decir que es la única biblioteca tan buena. (Si conoce otras bibliotecas mejores, deje un mensaje para compartir conmigo, ¡me encantaría probarlo también!)

configurar

Vamos a crear un nuevo proyecto Xcode, lo llamé  SQLiteIntro.

Esta aplicación no será muy complicada, porque solo quiero presentar brevemente SQLite, para que todos puedan entender fácilmente cómo usar SQL en proyectos Swift.

envoltura

Deberíamos mantener el buen hábito de separar la lógica en categorías o estructuras dedicadas. En este ejemplo, estamos usando una base de datos SQL, por lo que necesitamos crear una clase para abstraer parte de la lógica de la capa de datos para que el código sea más conciso.

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
    }
}
复制代码

Este es el comienzo de nuestro código, ¡bastante simple!

El código es bastante sencillo: después  DataWrapper de crear la clase por primera vez, busca el archivo de la base de datos y, si el archivo no existe, FMDB crea una base de datos en esa ruta. Finalmente, abre la conexión a la base de datos y crea la  user tabla.

Modelo

A continuación, construiremos una  User estructura para manejar los registros de la base de datos. En la aplicación de muestra, agregaremos otro contenido relacionado con JSON. Usaremos algunas API web más adelante para crear un usuario con nombre aleatorio.

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)
    }
}
复制代码

Cada vez que hacemos una consulta desde la base de datos, incluso si solo hay un resultado, o ningún resultado, obtenemos uno  FMResultSet. Entonces, en este caso, una función de inicio dedicada es útil para manejar toda la lógica de configuración por nosotros.

Combinar 和 MVVM

因為我使用的是 SwiftUI,我希望 DataWrapper 可以是响应式 (reactive) 的,并就数据库中可能发生的变化通知视图。 让我们回到 DataWrapper,添加 @Published User 阵列,这样就可以在一个 List 中显示 User。

final class DataWrapper: ObservableObject {
    private let db: FMDatabase
    @Published var users = [User]()
    ...
}
复制代码

我们想要从数据库中获取 User,并在数据库打开后立即进行发佈。因此,我们需要创建一个方法来查询所有 User,并在数据库初始化后,将它们设置為 DataWrapper 的 Users 变数。

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
    }
}
复制代码

然后,把这段程式码放在 DataWrapper 的 init 方法的最后:

users = getAllUsers()
复制代码

现在,当我们第一次啟动 DataWrapper 时,DataWrapper 就会自动获取所有 User,而且这些 User 是可用於 SwiftUI 的。

接著,让我们建立一个 insert 函式。我们稍后会用到它。

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)")
    }
}
复制代码

简单的 SwiftUI 视图

我想创建一个 List,来显示数据库中的所有使用者,并创建一个简单的函式,来向 Web API 获取随机的使用者名称,并将新使用者插入到数据库。

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()
    }
}
复制代码

如果我们现在执行 App,会看到一个空的列表。但只要点击右上的加号,就可以在数据库中加入内容,而列表的名称也会实时在你的列表中出现。

总结

这篇文章是一个非常简单的范例,用另一种方式来在熟悉的 SQLite 数据库中存储数据,你可以看到 App 的效能比 CoreData 版本大大提高。

如果你想更好地控制数据,SQLite 和 SQL 绝对不会让你失望!对於需要精密控制和查询优化器 (query optimization) 的 App 来说,SQLite 可以大大提高效能。使用 CloudKit 同步数据也会变得更容易,因為现在我们只需要同步 SQLite 档案,而无需处理其他 CoreData Table 和不同的版本。

这里也推荐一些面试相关的内容!

Supongo que te gusta

Origin juejin.im/post/7083742883706044429
Recomendado
Clasificación