i'm here to ask help.
I just create a tiny dictionary for self use.
And now i want to add a SearchBar to search the keyword from the CSV file,
and it's not work and i cant find any reference from internet
Here is Preview
My CVS Model Code:
import Foundation
struct Model: Identifiable {
let id: Int
let Vocab: String
let Type1 : String
let Type2 : String
let Meaning: String
let Meaning2: String
init(raw:[String]){
self.id = Int(raw[0])!
self.Vocab = raw [1]
self.Type1 = raw[2]
self.Type2 = raw[3]
self.Meaning = raw[4]
self.Meaning2 = raw[5]
}
}
Handle CSV Code:
import Foundation
func cleanRows(file:String) -> String{
var cleanFile = file
cleanFile = cleanFile.replacingOccurrences(of: "\r", with: "\n")
cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")
return cleanFile
}
func loadCSVData() ->[Model]{
var csvToStruct = [Model]()
guard let filePath = Bundle.main.path(forResource: "Book2", ofType:"csv") else {
print("Error: file not found")
return []
}
var data = ""
do{
data = try String(contentsOfFile: filePath)
} catch{
print(error)
return []
}
data = cleanRows(file: data)
var rows = data.components(separatedBy: "\n")
rows.removeFirst()
for row in rows {
let csvColumns = row.components(separatedBy: ",")
if csvColumns.count == rows.first?.components(separatedBy: ",").count{
let linesStruct = Model.init(raw:csvColumns)
csvToStruct.append(linesStruct)
}
}
return csvToStruct
}
Content:
import SwiftUI
struct ContentView: View {
#State var Modelx:[Model]
#State private var searchText = ""
var body: some View{
NavigationView{
List{
ForEach (Modelx){ model in
NavigationLink(destination:{ModelDetailView(thisModel:model)}){
HStack{
Text("#"+String(format: "%03d", model.id))
.font(.subheadline)
Text(model.Vocab)
.font(.headline)
Spacer()
reusableTypeView(thisVocabType: model.Type1)
if model.Type2 != ""{
reusableTypeView(thisVocabType: model.Type2)
}
}
}
}
}.navigationTitle("Music Picionary")
.searchable(text: $searchText)
}
}
}
struct reusableTypeView: View {
var thisVocabType:String
var body:some View{
Text(thisVocabType)
.font(.system(size:15))
.padding(5)
.background(Color(thisVocabType))
.cornerRadius(9)
.foregroundColor(.white)
}
}
I think the main point is in the Handle CSV file right?
how can i add a SearchBar to search the keyword from the CSV file
thanks everyone help~
The problem is you never use the search test. You must say the list what data you want and how to apply the filter set by searchTest.
A example on how to use searchText :
struct ContentView: View {
#State var models:[Model]
#State private var searchText = ""
// use a model list filtered by the searchText
var filteredModels: [Model] {
if searchText == "" {
return models
}
return models.filter { model in
model.meaning.contains(searchText)
}
}
init() {
models = Model.loadCSVData()
}
var body: some View{
NavigationView{
List{
ForEach (filteredModels){ model in
NavigationLink(destination:{ModelDetailView(thisModel:model)}){
HStack{
Text("#"+String(format: "%03d", model.id))
.font(.subheadline)
Text(model.vocab)
.font(.headline)
Spacer()
reusableTypeView(thisVocabType: model.type1)
if model.type2 != ""{
reusableTypeView(thisVocabType: model.type2)
}
}
}
}
}.navigationTitle("Music Picionary")
.searchable(text: $searchText)
.textInputAutocapitalization(.none)
.autocorrectionDisabled()
// here just to suppress autocapitalisation
// and autocorrection (not needed)
}
}
}
Model with its associated functions :
struct Model: Identifiable {
let id: Int
let vocab: String
let type1 : String
let type2 : String
let meaning: String
let meaning2: String
init(raw:[String]){
self.id = Int(raw[0])!
self.vocab = raw [1]
self.type1 = raw[2]
self.type2 = raw[3]
self.meaning = raw[4]
self.meaning2 = raw[5]
}
static func cleanRows(file:String) -> String{
var cleanFile = file
cleanFile = cleanFile.replacingOccurrences(of: "\r", with: "\n")
cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")
return cleanFile
}
static func loadCSVData() ->[Model]{
var csvToStruct = [Model]()
guard let filePath = Bundle.main.path(forResource: "Book2", ofType:"csv") else {
print("Error: file not found")
return []
}
var data = ""
do{
data = try String(contentsOfFile: filePath)
} catch{
print(error)
return []
}
data = cleanRows(file: data)
var rows = data.components(separatedBy: "\n")
// compute column number from first row
let titlesRow = rows.removeFirst()
let columnCount = titlesRow.components(separatedBy: ",").count
for row in rows {
let csvColumns = row.components(separatedBy: ",")
if csvColumns.count == columnCount {
let linesStruct = Model.init(raw:csvColumns)
csvToStruct.append(linesStruct)
}
}
return csvToStruct
}
}
Note : I made some little changes in property naming using lowercase for first character. I also initialise the models array in ContentView.init.
I also put the functions inside the model definition as these are specific to your model.
Related
I am using Relam to store the data locally and working fine but when I try to add the new record with navigation link it returns the duplicate record as well . Another problem is when I click the record , I am expecting change the navigation but since it got duplicate record , the first record does not work but the second one it work .
Here is the Model .
import SwiftUI
import RealmSwift
struct Task: Identifiable {
var id: String
var title: String
var completed: Bool = false
var completedAt: Date = Date()
init(taskObject: TaskObject) {
self.id = taskObject.id.stringValue
self.title = taskObject.title
self.completed = taskObject.completed
self.completedAt = taskObject.completedAt
}
}
Here is the Persisted Model...
import Foundation
import RealmSwift
class TaskObject: Object {
#Persisted(primaryKey: true) var id: ObjectId
#Persisted var title: String
#Persisted var completed: Bool = false
#Persisted var completedAt: Date = Date()
}
Here is the View Model ..
/
/ 2
final class TaskViewModel: ObservableObject {
// 3
#Published var tasks: [Task] = []
// 4
private var token: NotificationToken?
init() {
setupObserver()
}
deinit {
token?.invalidate()
}
// 5
private func setupObserver() {
do {
let realm = try Realm()
let results = realm.objects(TaskObject.self)
token = results.observe({ [weak self] changes in
// 6
self?.tasks = results.map(Task.init)
.sorted(by: { $0.completedAt > $1.completedAt })
.sorted(by: { !$0.completed && $1.completed })
})
} catch let error {
print(error.localizedDescription)
}
}
// 7
func addTask(title: String) {
let taskObject = TaskObject(value: [
"title": title,
"completed": false
])
do {
let realm = try Realm()
try realm.write {
realm.add(taskObject)
}
} catch let error {
print(error.localizedDescription)
}
}
// 8
func markComplete(id: String, completed: Bool) {
do {
let realm = try Realm()
let objectId = try ObjectId(string: id)
let task = realm.object(ofType: TaskObject.self, forPrimaryKey: objectId)
try realm.write {
task?.completed = completed
task?.completedAt = Date()
}
} catch let error {
print(error.localizedDescription)
}
}
func remove(id: String) {
do {
let realm = try Realm()
let objectId = try ObjectId(string: id)
if let task = realm.object(ofType: TaskObject.self, forPrimaryKey: objectId) {
try realm.write {
realm.delete(task)
}
}
} catch let error {
print(error.localizedDescription)
}
}
func updateTitle(id: String, newTitle: String) {
do {
let realm = try Realm()
let objectId = try ObjectId(string: id)
let task = realm.object(ofType: TaskObject.self, forPrimaryKey: objectId)
try realm.write {
task?.title = newTitle
}
} catch let error {
print(error.localizedDescription)
}
}
}
Here is the code for Content view ...
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
AddTaskView()
TaskListView()
}
.navigationTitle("Todo")
.navigationBarTitleDisplayMode(.automatic)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here is the code for Add task view ..
import SwiftUI
struct AddTaskView: View {
#State private var taskTitle: String = ""
#EnvironmentObject private var viewModel: TaskViewModel
var body: some View {
HStack(spacing: 12) {
TextField("Enter New Task..", text: $taskTitle)
Button(action: handleSubmit) {
Image(systemName: "plus")
}
}
.padding(20)
}
private func handleSubmit() {
viewModel.addTask(title: taskTitle)
taskTitle = ""
}
}
Here is the Task list View ..
struct TaskListView: View {
#EnvironmentObject private var viewModel: TaskViewModel
var body: some View {
ScrollView {
LazyVStack (alignment: .leading) {
ForEach(viewModel.tasks, id: \.id) { task in
TaskRowView(task: task)
Divider().padding(.leading, 20)
NavigationLink (destination: TaskView(task: task)) {
TaskRowView(task: task)
}.animation(.default)
}
}
}
}
}
Here is the code for Row View ..
struct TaskRowView: View {
let task: Task
// 1
#EnvironmentObject private var viewModel: TaskViewModel
var body: some View {
HStack(spacing: 12) {
Button(action: {
// 2
viewModel.markComplete(id: task.id, completed: !task.completed)
}) {
Image(systemName: task.completed ? "checkmark.circle.fill" : "circle")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(task.completed ? Color.green : Color.gray)
}
VStack(alignment: .leading, spacing: 8) {
Text(task.title)
.foregroundColor(.black)
if !task.completedAt.formatted().isEmpty {
Text(task.completedAt.formatted())
.foregroundColor(.gray)
.font(.caption)
}
}
Spacer()
}
.padding(EdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20))
}
}
Here is the screenshot ..
Let's troubleshoot the discrepancies one by one.
According to your code, each row in the list represents a Task. But, there are two models Task and TaskObject (persistable model) for that.
struct Task: Identifiable {
var id: String
var title: String
var completed: Bool = false
var completedAt: Date = Date()
init(taskObject: TaskObject) {
self.id = taskObject.id.stringValue
self.title = taskObject.title
self.completed = taskObject.completed
self.completedAt = taskObject.completedAt
}
}
class TaskObject: Object {
#Persisted(primaryKey: true) var id: ObjectId
#Persisted var title: String
#Persisted var completed: Bool = false
#Persisted var completedAt: Date = Date()
}
Instead of using two models, convert them into one.
class TaskObject: Object, Identifiable {
#Persisted(primaryKey: true) var id: ObjectId
#Persisted var title: String
#Persisted var completed: Bool = false
#Persisted var completedAt: Date = Date()
var idStr: String {
id.stringValue
}
}
Therefore, there's no need for mapping to another object after retrieving it from the database. The updated setupObserver function should be...
private func setupObserver() {
do {
let realm = try Realm()
let results = realm.objects(TaskObject.self)
token = results.observe({ [weak self] changes in
// 6
self?.tasks = results
.sorted(by: { $0.completedAt > $1.completedAt })
.sorted(by: { !$0.completed && $1.completed })
})
} catch let error {
print(error.localizedDescription)
}
}
Let's address your questions now.
When I try to add the new record with navigation link it returns the duplicate record as well
It does not produce duplicate data. Instead, the same data is displayed twice in the view. To correct this, remove one of the two instances of TaskRowView(task: task).
struct TaskListView: View {
#EnvironmentObject private var viewModel: TaskViewModel
var body: some View {
ScrollView {
LazyVStack (alignment: .leading) {
ForEach(viewModel.tasks, id: \.id) { task in
TaskRowView(task: task) // first row đź“Ś
Divider().padding(.leading, 20)
NavigationLink (destination: TaskView(task: task)) {
TaskRowView(task: task) // second row đź“Ś
}.animation(.default)
}
}
}
}
}
Next question,
I am expecting change the navigation but since it got duplicate record , the first record does not work but the second one it work.
Again, the second one changes navigation, and the first one does not, because this is exactly what is written in the code.
TaskRowView(task: task) // Why would it change navigation?
Divider().padding(.leading, 20)
NavigationLink (destination: TaskView(task: task)) {
TaskRowView(task: task) // changing navigation
}
This is Model and View Model. I am using UserDefaults for saving data.
import Foundation
struct Item: Identifiable, Codable {
var id = UUID()
var name: String
var int: Int
var date = Date()
}
class ItemViewModel: ObservableObject {
#Published var ItemList = [Item] ()
init() {
load()
}
func load() {
guard let data = UserDefaults.standard.data(forKey: "ItemList"),
let savedItems = try? JSONDecoder().decode([Item].self, from: data) else { ItemList = []; return }
ItemList = savedItems
}
func save() {
do {
let data = try JSONEncoder().encode(ItemList)
UserDefaults.standard.set(data, forKey: "ItemList")
} catch {
print(error)
}
}
}
and this is the view. I am tryng too add new item and sort them by date. After that adding numbers on totalNumber. I tried .sorted() in ForEach but its not work for sort by date. and I try to create a func for adding numbers and that func is not work thoo.
import SwiftUI
struct ContentView: View {
#State private var name = ""
#State private var int = 0
#AppStorage("TOTAL_NUMBER") var totalNumber = 0
#StateObject var VM = ItemViewModel()
var body: some View {
VStack(spacing: 30) {
VStack(alignment: .leading) {
HStack {
Text("Name:")
TextField("Type Here...", text: $name)
}
HStack {
Text("Number:")
TextField("Type Here...", value: $int, formatter: NumberFormatter())
}
Button {
addItem()
VM.save()
name = ""
int = 0
} label: {
Text ("ADD PERSON")
}
}
.padding()
VStack(alignment: .leading) {
List(VM.ItemList) { Item in
Text(Item.name)
Text("\(Item.int)")
Text("\(Item.date, format: .dateTime.day().month().year())")
}
Text("\(totalNumber)")
.padding()
}
}
}
func addItem() {
VM.ItemList.append(Item(name: name, int: int))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
First of all please name variables always with starting lowercase letter for example
#Published var itemList = [Item] ()
#StateObject var vm = ItemViewModel()
To sort the items by date in the view model replace
itemList = savedItems
with
itemList = savedItems.sorted{ $0.date < $1.date }
To show the sum of all int properties of the today items add a #Published var totalNumber in the view model and a method to calculate the value. Call this method in load and save
class ItemViewModel: ObservableObject {
#Published var itemList = [Item] ()
#Published var totalNumber = 0
init() {
load()
}
func load() {
guard let data = UserDefaults.standard.data(forKey: "ItemList"),
let savedItems = try? JSONDecoder().decode([Item].self, from: data) else { itemList = []; return }
itemList = savedItems.sorted{ $0.date < $1.date }
calculateTotalNumber()
}
func save() {
do {
let data = try JSONEncoder().encode(itemList)
UserDefaults.standard.set(data, forKey: "ItemList")
calculateTotalNumber()
} catch {
print(error)
}
}
func calculateTotalNumber() {
let todayItems = itemList.filter{ Calendar.current.isDateInToday($0.date) }
totalNumber = todayItems.map(\.int).reduce(0, +)
}
}
In the view delete the #AppStorage line because the value is calculated on demand and replace
Text("\(totalNumber)")
with
Text("\(vm.totalNumber)")
I’ve problem with fatalError(). I try to adding to a list of words but in this line there is problem it says:
fatal Error in ContentView.swift
and i don’t why this happened my code was totally correct.
The code of this error:
// id were are * here* then there was a problem - trigger a cradh and report the error
fatalError("could not load start.txt from bundle.")
And the whole code is;
import SwiftUI
struct ContentView: View {
#State private var usedWords = [String]()
#State private var rootWord = ""
#State private var newWord = ""
var body: some View {
NavigationView {
List {
Section {
TextField("enter your word", text: $newWord)
.textInputAutocapitalization(.never)
}
Section {
ForEach(usedWords, id: \.self) { word in
HStack {
Image(systemName: "\(word.count).circle")
Text(word)
}
}
}
}
.navigationTitle(rootWord)
.onSubmit(addNewWord)
.onAppear(perform: startGame)
}
}
func addNewWord() {
// lowercase and trim the word, to make sure we don:t add duplicate words with case differences
let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
// exit if the remaining string is empty
guard answer.count > 0 else { return }
// extra validation to come
withAnimation {
usedWords.insert(answer, at: 0)
}
newWord = ""
}
func startGame() {
// 1.find the URL for start.txt in our app bundle
if let startWordsURL = Bundle.main.url(forResource: "start",withExtension: "txt") {
// 2.load start.txt into a string
if let startWords = try? String(contentsOf: startWordsURL) {
// 3.split the string up into an array of strings, splitting on line breaks
let allWords = startWords.components(separatedBy: "\n")
// 4.pcik one random word, or use "yamori" as a sensible default
rootWord = allWords.randomElement() ?? "yamori"
// if we are here everything has worked, so. we can exit
return
}
}
// id were are * here* then there was a problem - trigger a cradh and report the error
fatalError("could not load start.txt from bundle.")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Any idea? How to fix that error
Thanks.
The reason for this failing is either the url for "start.txt" is nil or try? String(contentsOf: startWordsURL) fails. In order to help you debug and become an understanding why your current code design is bad consider this design:
func startGame() {
// 1.find the URL for start.txt in our app bundle
guard let startWordsURL = Bundle.main.url(forResource: "start",withExtension: "txt") else{
//assign the default value
rootWord = "yamori"
// if the url is not found this will print in the console
print("File could not be found")
return
}
do{
// 2.load start.txt into a string
let startWords = try String(contentsOf: startWordsURL)
// 3.split the string up into an array of strings, splitting on line breaks
let allWords = startWords.components(separatedBy: "\n")
// 4.pick one random word, or use "yamori" as a sensible default
rootWord = allWords.randomElement() ?? "yamori"
} catch{
//if the file couldn´t be read this will print the error in the console
print(error)
}
}
I'm struggling with saving some date to UserDefaults. I have a struct, an array of which I'm going to save:
struct Habit: Identifiable, Codable {
var id = UUID()
var name: String
var comments: String
}
Then, in the view, I have a button to save new habit to an array of habits and put it into UserDefaults:
struct AddView: View {
#State private var newHabit = Habit(name: "", comments: "")
#State private var name: String = ""
let userData = defaults.object(forKey: "userData") as? [Habit] ?? [Habit]()
#State private var allHabits = [Habit]()
var body: some View {
NavigationView {
Form {
Section(header: Text("Habit name")) {
TextField("Jogging", text: $newHabit.name)
}
Section(header: Text("Description")) {
TextField("Brief comments", text: $newHabit.comments)
}
}
.navigationBarTitle("New habit")
.navigationBarItems(trailing: Button(action: {
allHabits = userData
allHabits.append(newHabit)
defaults.set(allHabits, forKey: "userData")
}) {
addButton
})
}
}
}
When I tap the button, my app crashes with this thread: Thread 1: "Attempt to insert non-property list object (\n \"HabitRabbit.Habit(id: 574CA523-866E-47C3-B56B-D0F85EBD9CB1, name: \\\"Wfs\\\", comments: \\\"Sdfdfsd\\\")\"\n) for key userData"
What did I do wrong?
Adopting Codable doesn't make the object property list compliant per se, you have to encode and decode the object to and from Data.
Something like this
func loadData() -> [Habit]
guard let userData = defaults.data(forKey: "userData") else { return [] }
return try? JSONDecoder().decode([Habit].self, from: userData) ?? []
}
func saveData(habits : [Habit]) {
guard let data = try? JSONEncoder().encode(habits) else { return }
defaults.set(data, forKey: "userData")
}
so i've made a XCode project where you are able to add data to Firestore and read it by searching for the saved data (serial number).
I now basically tried to combine those 2 separate views (search view + "form view") to get a 3rd view where you can search for the "serial number" and instead of getting the whole list of data, you get Text Fields with the data in it (like the view when you add it except that the fields are already filled in with the stored data).
So here are my problems:
The text fields are kinda filled out, but just with the grey "placeholder" text. They should be filled out with normal Text which can be edited, just in case a dummie presses on save when they just edited 1 line and the whole data is gone
If i write something in the text field and then press "Speichern"("Save"), it always creates a whole new document (that has a really strange ID style ([96251AF5-164A-4CBF-9BE6-9C9A482652DE ]) instead of changing the fields, and i was sitting here the whole day now, trying to get it working.
The whole code is kinda combined from 2 different videos. I Hope you can help me (yes, i know, it looks messy)
Searchbar + Editing SwiftUI view
import SwiftUI
import Firebase
struct ContentViewVier: View {
#ObservedObject var data = getDataZwei()
var body: some View {
NavigationView{
ZStack(alignment: .top){
GeometryReader{_ in
// Home View....
Text("Bitte Seriennummer eingeben").foregroundColor(.black)
}.background(Color("FarbeSeriennummerStartbildschirm").edgesIgnoringSafeArea(.all))
CustomSearchBarEdit(data: self.$data.datas).padding(.top)
}.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
struct CustomSearchBarEdit : View {
#State var txt = ""
#Binding var data : [Gerät]
var body : some View{
VStack(spacing: 0){
HStack{
TextField("Nach Seriennummer suchen", text: self.$txt).opacity(100).foregroundColor(.black)
if self.txt != ""{
Button(action: {
self.txt = ""
}) {
Text("Abbrechen")
}
.foregroundColor(.black)
}
}.padding()
if self.txt != ""{
if
self.data.filter({$0.sn.lowercased() .contains(self.txt.lowercased())}).count == 0 {
Text("Es wurde kein Gerät mit dieser Seriennummer gefunden").foregroundColor(Color.red.opacity(0.6)).padding()
}
else{
List(self.data.filter{$0.sn.contains(self.txt.lowercased())}){i in
NavigationLink(destination: DetailZwei(data: i)) {
Text(i.sn)
}
Text(i.typ)
}.frame(height: UIScreen.main.bounds.height / 5)
}
}
}.background(Color.white)
.padding()
}
}
class getDataZwei : ObservableObject{
#Published var datas = [Gerät]()
init() {
let db = Firestore.firestore()
db.collection("Geräte").getDocuments { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documents{
let id = i.documentID
let sn = i.get("Seriennummer") as! String
let objekt = i.get("Objekt") as! String
let str = i.get("Strasse") as! String
let pos = i.get("Position") as! String
let typ = i.get("Gerätetyp") as! String
let ida = i.get("Installation")as! String
let lg = i.get("LeasingOderGekauft")as! String
let la = i.get("LeasingAblaufdatum")as! String
let ga = i.get("GarantieAblaufdatum")as! String
let nr = i.get("Hausnummer")as! String
let plz = i.get("Postleitzahl")as! String
let ort = i.get("Ort")as! String
let vp = i.get("Verantwortlich")as! String
let tel = i.get("Telefonnummer")as! String
let zusatz = i.get("Zusätzlich")as! String
self.datas.append(Gerät(id: id, sn: sn, objekt: pos, str: typ, nr: ida, ort: lg, vp: la, tel: ga, pos: objekt, typ: str, ida: nr, lg: plz, la: ort, ga: vp, zusatz: tel, plz: zusatz))
}
}
}
}
struct dataTypeZwei : Identifiable, Codable {
var id: String? = UUID().uuidString
var sn : String
var pos : String
var typ : String
var ida : String
var lg : String
var la : String
var ga : String
var objekt : String
var str : String
var nr : String
var plz : String
var ort : String
var vp : String
var tel : String
var zusatz : String
}
struct DetailZwei : View {
var data : Gerät
#State var viewModel = GerätEditieren()
#Environment(\.presentationMode) var presentationMode
enum Mode {
case new
case edit
}
var mode: Mode = .edit
var body : some View{
NavigationView{
ScrollView{
VStack {
Group{
Text("Gerät")
.font(.title)
TextField(data.sn, text: $viewModel.gerät.sn).textFieldStyle(RoundedBorderTextFieldStyle())
.navigationBarTitle("Geräteinformationen")
TextField(data.objekt, text: $viewModel.gerät.objekt).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.typ, text: $viewModel.gerät.typ).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.pos, text: $viewModel.gerät.pos).textFieldStyle(RoundedBorderTextFieldStyle())
Text("")
TextField(data.ida, text: $viewModel.gerät.ida).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.lg, text: $viewModel.gerät.lg).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.la, text: $viewModel.gerät.la).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.ga, text: $viewModel.gerät.ga).textFieldStyle(RoundedBorderTextFieldStyle())
}
Group {
Text("Adresse")
.font(.title)
TextField(data.str, text: $viewModel.gerät.str).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.nr, text: $viewModel.gerät.nr).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.plz, text: $viewModel.gerät.plz).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.ort, text: $viewModel.gerät.ort).textFieldStyle(RoundedBorderTextFieldStyle())
}
Group {
Text("Kontakt")
.font(.title)
TextField(data.vp, text: $viewModel.gerät.vp).textFieldStyle(RoundedBorderTextFieldStyle())
TextField(data.tel, text: $viewModel.gerät.tel).textFieldStyle(RoundedBorderTextFieldStyle())
}
Group {
Text("Zusätzliche Informationen")
.font(.title)
TextField(data.zusatz, text: $viewModel.gerät.zusatz).textFieldStyle(RoundedBorderTextFieldStyle())
}
}.padding()
.navigationBarTitle("Gerät hinzufügen", displayMode: .inline)
.navigationBarItems(leading: Button(action: { self.handleCancelTapped() }, label: {
Text("Abbrechen")
}),
trailing: Button(action: { self.handleDoneTapped() }, label: {
Text("Speichern")
})
// .disabled(!viewModel.modified)
)
}
}
}
func handleCancelTapped() {
dismiss()
}
func handleDoneTapped() {
viewModel.save()
dismiss()
}
func dismiss() {
presentationMode.wrappedValue.dismiss()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentViewVier()
}
}
View Model
import Foundation
import Firebase
import Combine
class GerätEditieren: ObservableObject {
#Published var gerät: Gerät = Gerät(sn: "", objekt: "", str: "", nr: "", ort: "", vp: "", tel: "", pos: "", typ: "", ida: "", lg: "", la: "", ga: "", zusatz: "", plz: "")
private var db = Firestore.firestore()
enum Mode {
case new
case edit
}
var mode: Mode = .edit
/*
private func addGerät(_ gerät: Gerät) {
do{
let _ = try db.collection("Geräte").addDocument(from: gerät)
}
catch {
print(error)
}
}
*/
private func updateGerät(_ gerät: Gerät) {
if let ID = gerät.id {
do {
try db.collection("Geräte").document(ID).setData(from: gerät)
}
catch {
print(error)
}
}
}
private func updateOrAddGerät() { // (1)
if let _ = gerät.id {
self.updateGerät(self.gerät) // (2)
}
else {
print ("error") // (3)
}
}
func save() {
func updateGerät(_ gerät: Gerät) {
if let ID = gerät.id {
do {
try db.collection("Geräte").document(ID).setData(from: gerät, merge: true)
}
catch {
print(error)
}
}
}
updateOrAddGerät()
}
}
Another Model (kinda)
import Foundation
import FirebaseFirestoreSwift
struct Gerät: Identifiable, Codable {
var id: String? = UUID().uuidString
var sn: String
var objekt: String
var str: String
var nr: String
var ort: String
var vp: String
var tel: String
var pos: String
var typ: String
var ida: String
var lg: String
var la: String
var ga: String
var zusatz: String
var plz: String
enum CodingKeys: String, CodingKey {
case sn = "Seriennummer"
case objekt = "Objekt"
case str = "Strasse"
case nr = "Hausnummer"
case ort = "Ort"
case vp = "Verantwortlich"
case tel = "Telefonnummer"
case pos = "Position"
case typ = "Gerätetyp"
case ida = "Installation"
case lg = "LeasingOderGekauft"
case la = "LeasingAblaufdatum"
case ga = "GarantieAblaufdatum"
case zusatz = "Zusätzlich"
case plz = "Postleitzahl"
}
}