SwiftUI SQLite ForEach 列表数据 [英] SwiftUI SQLite ForEach list data

查看:39
本文介绍了SwiftUI SQLite ForEach 列表数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将 SQLite 数据库 (使用 SQLite.swift) 集成到我的项目中(因为我未恢复的 CoreData 明显限制在此处引用.

我有一个帮助文件,其中包含一个我实例化的类来完成与 SQLite 查询有关的所有繁重工作.

我不知道如何在 SwiftUI 的 ForEach 语句中获取这些数据,让我们说一个简单的数据库内容列表.

我试图从运行查询的函数中设置一个返回值,但它的返回类型是 AnySequence,我不确定如何处理它.当我在获取数据后尝试返回它时,我收到关于不返回失败的错误(可以理解).在这种情况下,不知道如何返回空白的 AnySequence.请参阅下面的代码.

有什么想法吗?

来自帮助程序类的代码(按照 Kilo Loco 的 YouTube 视频中的一些说明构建:

<预><代码>进口基金会导入 SQLite类数据模型{var 数据库:连接!让 usersTable = Table("users")let id = Expression("id")let name = Expression("name")let email = Expression("email")var dataDict = [Dictionary()]初始化(){做{let documentDirectory = try FileManager.default.url(for: .applicationSupportDirectory, in: .allDomainsMask,合适的: nil, create: true)让 fileUrl = documentDirectory.appendingPathComponent("users").appendingPathExtension("sqlite3")让数据库 = 尝试连接(fileUrl.path)self.database = 数据库print("数据库初始化在路径\(fileUrl)")}抓住{打印(错误)}}功能创建表(){打印(创建点击")让 createTable = self.usersTable.create { (table) intable.column(self.id,primaryKey:true)table.column(self.name)table.column(self.email,唯一的:true)}做{尝试 self.database.run(createTable)打印(表创建")}抓住{打印(错误)}}func insertRecord(名称:字符串,电子邮件:字符串){让 insertRecord = self.usersTable.insert(self.name <- name, self.email <- email)做{尝试 self.database.run(insertRecord)打印(添加记录")}抓住{打印(错误)}}功能列表用户(){打印(列出的用户")做{让用户 = 尝试 self.database.prepare(self.usersTable)对于用户中的用户{打印(用户ID:\(用户[self.id]),用户名:\(用户[self.name]),电子邮件:\(用户[self.email])")}}抓住{打印(错误)}}}

和简单的 SwiftUI 内容查看

<预><代码>导入 SwiftUI让 myData = DataModel()结构内容视图:查看{@State var 名称:String = ""@State var 电子邮件:String = ""var主体:一些视图{虚拟堆栈{TextField("输入姓名", text: self.$name)TextField("输入邮箱", text: self.$email)按钮(动作:{myData.insertRecord(name: self.$name.wrappedValue, email: self.$email.wrappedValue)}) {文本(插入记录")}按钮(动作:{myData.createTable()}) {文本(创建表")}按钮(动作:{myData.listUsers()}) {Text("列出用户")}}}}

理论上,我希望能够在我的 VStack 中做一些事情:

ForEach (myData.listUsers, id: \.self) { 记录在文本(用户名\(记录.用户名)")}

我试图让 listUsers 函数返回它的结果,这样就可以像这样使用它,但效果不佳..

更新 #1

尝试让函数返回数据,然后我可以在 SwiftUI 视图中对其进行迭代.我正在尝试这个:

 func listUsers() ->(整数,字符串,字符串){打印(列出的用户")做{让用户 = 尝试 self.database.prepare(self.usersTable)对于用户中的用户{打印(用户ID:\(用户[self.id]),用户名:\(用户[self.name]),电子邮件:\(用户[self.email])")返回(用户[self.id],用户[self.name],用户[self.email])}}抓住{打印(错误)}return (0, "No Name", "No Email")}

然后在我试图调用的视图中:

 让 response = myData.listUsers()响应响应{文本(resp.name)}

但我随后收到错误:

<块引用>

类型 '(Int, String, String)' 不符合协议 'Sequence'

更新 #2

Holy C*&@ - 好痛苦..

我能够让函数返回一些我可以使用的东西,但这绝对不是最有效的方法.知道如何做得更好:

我现在的功能是:

 func listUsers() ->[字典<字符串,任何>] {打印(列出的用户")做{让用户 = 尝试 self.database.prepare(self.usersTable)对于用户中的用户{打印(用户ID:\(用户[self.id]),用户名:\(用户[self.name]),电子邮件:\(用户[self.email])")dataDictArray.append(["id":user[self.id], "name":user[self.name], "email":user[self.email]])}}抓住{打印(错误)}返回数据字典数组}}

它返回一个字符串字典数组:Any(因为我在我的数据库中混合了 String 和 Int.然后我的函数看起来像:

 让 response = myData.listUsers()打印(响应)对于响应中的 dicts {用于记录在 dicts {if (record.key.datatypeValue == "name") {print("记录键:\(record.key)")print("记录值:\(record.value)")}}}}) {Text("列出用户")}

那么它在做什么,这是荒谬的,是遍历数组,然后遍历字典以获取键值对..

只是感觉很麻烦而且过于复杂.

  • 仍然无法弄清楚如何将其放入实际视图中.我可以将输出打印到日志中,但不能打印在 Text() 标签中.

解决方案

我发现了如何做到这一点:

在我的 SQLite 工作文件中,我需要创建一个结构来表示数据模型:

struct TestDataModel: Hashable {让 ID:Int让名称:字符串让电子邮件:字符串}

然后在我的助手类中,我的函数将数据加载到 TestDataModel 对象数组中.该函数然后像这样返回数组:

 func listUsers2() ->[测试数据模型] {打印(列出的用户")var theseVars = [TestDataModel]()做{让用户 = 尝试 self.database.prepare(self.usersTable)对于用户中的用户{打印(用户ID:\(用户[self.id]),用户名:\(用户[self.name]),电子邮件:\(用户[self.email])")这些Vars.append(TestDataModel(id: user[self.id], name: user[self.name], email: user[self.email]))}}抓住{打印(错误)}返回这些变量}

然后,在我的 SwiftUI 视图中,我可以像平常一样使用 TestDataModel 对象数组:

 ForEach (myData.listUsers2(), id: \.self) { 记录在文本(记录.电子邮件)}

这有效,不确定它是否是最优雅的方法,但结果很好..

I am trying to integrate an SQLite database (with SQLite.swift) into my project (since my unrecovered apparent limitations of CoreData referenced here.

Ive got a helper file with a class that I instantiate to do all of the heavy lifting with regards to SQLite queries.

I can not figure out how to get that data, let's say a simple list of the contents of the database, into a ForEach statement in SwiftUI.

I have tried to set a return from the function that runs the query, but its return type is AnySequence<Row>, and I'm not sure what to do with that. When I try to return it after the data is fetched, I get errors about not returning is on failure (understandably so). Don't know how to return a blank AnySequence<Row> in that case. See code below.

Any thoughts?

Code from helper class (built following some of the instructions at Kilo Loco's YouTube video:


import Foundation
import SQLite



class DataModel {

    var database: Connection!
    let usersTable = Table("users")
    let id = Expression<Int>("id")
    let name = Expression<String>("name")
    let email = Expression<String>("email")
    var dataDict = [Dictionary<String, String>()]

    init() {

        do {

            let documentDirectory = try FileManager.default.url(for: .applicationSupportDirectory, in: .allDomainsMask, appropriateFor: nil, create: true)
            let fileUrl = documentDirectory.appendingPathComponent("users").appendingPathExtension("sqlite3")
            let database = try Connection(fileUrl.path)
            self.database = database
            print("Database initialized at path \(fileUrl)")
        } catch {
            print(error)
        }

    }


    func createTable() {

        print("Create Tapped")

        let createTable = self.usersTable.create { (table) in
            table.column(self.id, primaryKey: true)
            table.column(self.name)
            table.column(self.email, unique: true)
        }

        do {

            try self.database.run(createTable)
            print("Table Created")

        } catch {
            print(error)
        }

    }


    func insertRecord(name: String, email: String) {

        let insertRecord = self.usersTable.insert(self.name <- name, self.email <- email)

        do {

            try self.database.run(insertRecord)
            print("record added")
        } catch {
            print(error)
        }

    }

    func listUsers() {

        print("Listed Users")

        do {

            let users = try self.database.prepare(self.usersTable)

            for user in users {
                print("User ID: \(user[self.id]), UserName: \(user[self.name]), Email: \(user[self.email])")

            }

        } catch {
            print(error)
        }
    }
}

and simple SwiftUI content View


import SwiftUI

let myData = DataModel()

struct ContentView: View {

    @State var name: String = ""
    @State var email: String = ""

    var body: some View {


        VStack {

            TextField("Enter Name", text: self.$name)
            TextField("Enter email", text: self.$email)

            Button(action: {
                myData.insertRecord(name: self.$name.wrappedValue, email: self.$email.wrappedValue)
            }) {
                Text("Insert Record")
            }

            Button(action: {
                myData.createTable()
            }) {
                Text("Create Table")
            }


            Button(action: {
                myData.listUsers()
            }) {
                Text("List users")
            }
        }
    }
}

In theory, what I would like to be able to do is something like in my VStack:

ForEach (myData.listUsers, id: \.self) { record in 
      Text("UserName \(record.userName)")
}

I was trying to have the listUsers function return its result so it could be used like this, but that did not work out so well..

Update #1

Trying to have the function return the data, then I can iterate over it in the SwiftUI View. I was trying this:

    func listUsers() -> (Int, String, String) {
        print("Listed Users")
        do {
            let users = try self.database.prepare(self.usersTable)
            for user in users {
                print("User ID: \(user[self.id]), UserName: \(user[self.name]), Email: \(user[self.email])")
                return (user[self.id], user[self.name], user[self.email])
            }
        } catch {
            print(error)
        }
        return (0, "No Name", "No Email")
    }

and then in the View I was trying to call:

                let response = myData.listUsers()
                for resp in response {
                    Text(resp.name)
                }

but I then get the error:

Type '(Int, String, String)' does not conform to protocol 'Sequence'

Update #2

Holy C*&@ - What a pain..

I was able to get the function to return something that I can work with, but it is most definitely not the most efficient way to do this. Any idea how I can do it better:

My function now is:

    func listUsers() ->  [Dictionary<String,Any>]  {
        print("Listed Users")
        do {
            let users = try self.database.prepare(self.usersTable)
            for user in users {
                print("User ID: \(user[self.id]), UserName: \(user[self.name]), Email: \(user[self.email])")
                dataDictArray.append(["id":user[self.id], "name":user[self.name], "email":user[self.email]])
            }
        } catch {
            print(error)
        }
        return dataDictArray
    }


}

Which returns an Array of Dictionary of String:Any (because I'm mixing String and Int in my database. Then my function looks like:

                let response = myData.listUsers()
                print(response)
                for dicts in response {
                    for record in dicts {
                        if (record.key.datatypeValue == "name") {
                            print("Record Key: \(record.key)")
                            print("Record Value: \(record.value)")
                        }
                    }
                }
                }) {
                     Text("List users")
                 }

So what its doing, which is ridiculous, is looping through the array, then looping through the dictionary to get the key value pairs..

It just feels cumbersome and overly complicated.

  • Still can't figure out how to get it into the actual view. I can print the output to the log, but not in a Text() tag..

解决方案

Ive discovered how to do this:

in my SQLite worker file, I needed to create a struct to represent the data model:

struct TestDataModel: Hashable {
    let id: Int
    let name: String
    let email: String
}

Then in my helper class, my function loads the data into an array of TestDataModel objects. The function then returns the array like this:

    func listUsers2() ->  [TestDataModel]  {
         print("Listed Users")
        var theseVars = [TestDataModel]()
         do {
             let users = try self.database.prepare(self.usersTable)
             for user in users {
                 print("User ID: \(user[self.id]), UserName: \(user[self.name]), Email: \(user[self.email])")
                theseVars.append(TestDataModel(id: user[self.id], name: user[self.name], email: user[self.email]))
            }
         } catch {
             print(error)
         }
         return theseVars
     }

Then, in my SwiftUI view, I can use the array of TestDataModel objects like normal:

            ForEach (myData.listUsers2(), id: \.self) { record in
                Text(record.email)

            }

This works, not sure if its the most elegant approach, but results are good..

这篇关于SwiftUI SQLite ForEach 列表数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆