Usando SQLite no iOS para processar dados - melhorando o desempenho do aplicativo

Introdução ao FMDB

FMDB é um wrapper de Objective-C baseado em SQLite, de código aberto e fácil de configurar, pode-se dizer que é a única biblioteca tão boa. (Se você conhece outras bibliotecas melhores, por favor deixe uma mensagem para compartilhar comigo, eu adoraria experimentar também!)

configuração

Vamos criar um novo projeto Xcode, eu o chamei de  SQLiteIntro.

Este aplicativo não será muito complicado, porque eu só quero apresentar brevemente o SQLite, para que todos possam entender facilmente como usar o SQL em projetos Swift.

embrulho

Devemos manter o bom hábito de separar a lógica em categorias ou estruturas dedicadas. Neste exemplo, estamos usando um banco de dados SQL, portanto, precisamos criar uma classe para abstrair parte da lógica da camada de dados para tornar o código mais 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 é o início do nosso código, bastante simples!

O código é bastante simples: depois que a  DataWrapper classe é criada, ela procura o arquivo de banco de dados e, se o arquivo não existir, o FMDB cria um banco de dados nesse caminho. Por fim, ele abre a conexão com o banco de dados e cria a  user tabela.

Modelo

Em seguida, construiremos uma  User estrutura para lidar com os registros do banco de dados. No aplicativo de exemplo, adicionaremos algum outro conteúdo relacionado a JSON. Usaremos algumas APIs da Web posteriormente para criar um usuário de nome aleatório.

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

Sempre que fazemos uma consulta no banco de dados, mesmo que haja apenas um resultado, ou nenhum resultado, obtemos um  FMResultSet. Portanto, neste caso, uma função init dedicada é útil para lidar com toda a lógica de configuração para nós.

Combine e 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 和不同的版本。

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

Acho que você gosta

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