Firebase #DocumentID property wrapper not working - ios

I'm trying to update certain values in a specific Firebase document, however, whenever the code fires, I get the error:
[FirebaseFirestore][I-FST000001] WriteStream (7fc6c510be38) Stream error: 'Not found: No document to update: projects/htg-inspection/databases/(default)/documents/Projects/365A2F53-CB38-47A6-93C3-7D6DA14038D9
Based on the response it would seem that the Id I get back from my data struct is different from the document ID that I'm trying to update. I assumed that the #DocumentID property wrapper is supposed to solve this, or am I doing/not doing something correctly/incorrectly?
These are the corresponding DocumentIDs and what I'm getting back are some random strings (assumingly from the Swift's UUID initializer)
My Projects Data Struct:
import Foundation
import FirebaseFirestoreSwift
import FirebaseFirestore
import Firebase
struct ProjectsData: Identifiable, Codable{
#DocumentID var id: String? = UUID().uuidString
var title: String
var client: String
var description: String
var inspections: [InspectionsData]
enum CodingKeys: String, CodingKey{
case id
case title
case client
case description
case inspections
}
}
Sub-Collection:
struct InspectionsData: Identifiable, Codable {
var id = UUID()
var category: Category
var trade: String
var inspectionDate: Date
var dueDate: Date
var assigned: Bool
var location: String
var images: [InspectionImages]
var checklists: [ChecklistsData]
var dateToString: String{
let formatter = DateFormatter()
formatter.dateFormat = "MMMM dd, YYYY "
return formatter.string(from: inspectionDate)
}
var dueDateToString: String{
let formatter = DateFormatter()
formatter.dateFormat = "MMMM dd, YYYY "
return formatter.string(from: dueDate)
}
}
struct ChecklistsData: Identifiable, Codable{
var id = UUID()
var question: String
var hasPassed: Bool
var hasFailed: Bool
var isnotApplicable: Bool
}
enum ChecklistCodingKeys: String, CodingKey{
case id
case question
case hasPassed
case hasFailed
case isnotApplicable
}
My View Model:
class ProjectsViewModel: ObservableObject {
#Published var projects = [ProjectsData]()
#Published var inspectionData = [InspectionsData]()
#Published var assignedProjects: [AssignedProjectsModel] = []
#Published var retrievePhotos = [UIImage]()
#Published var inspectionImages = [InspectionImages]()
#Published var showImageViewer = false
#Published var selectedImageID: String = ""
#Published var checklists = [ChecklistsData]()
#Published var hasPassed = false
#Published var hasFailed = false
#Published var isnotApplicable = false
func updateChecklist(_ checklist: ProjectsData, hasPassed: Bool, hasFailed: Bool, isnotApplicable: Bool) {
if let documentId = checklist.id {
db.collection("Projects").document(documentId).updateData(["checklists" : ["hasPassed" : hasPassed, "hasFailed" : hasFailed, "isnotApplicable" : isnotApplicable]]) {
error in
if let error = error {
print(error.localizedDescription)
} else {
print("Document has been updated")
}
}
}
}
}
The View that I'm trying to update the database from:
struct ChecklistComponent: View {
#State var hasPassed: Bool
#State var hasFailed: Bool
#State var isnotApplicable: Bool
#ObservedObject var checklistvm = ProjectsViewModel()
var documentID: ProjectsData
var checklistItem: String = ""
var question: String = ""
var body: some View {
VStack{
HeaderOne(text: question, size: 16)
Spacer()
HStack{
ChecklistButton(text: "Pass", textColor: hasPassed ? "#00AA97" : "#696969", buttonColor: hasPassed ? "#B0EFE8" : "#DBDBDB",
action: {
hasPassed.toggle()
hasFailed = false
isnotApplicable = false
checklistvm.updateChecklist(documentID, hasPassed: hasPassed, hasFailed: hasFailed, isnotApplicable: isnotApplicable)
})
ChecklistButton(text: "Fail", textColor: hasFailed ? "#AA0000" : "#696969", buttonColor: hasFailed ? "#EFB0B0" : "#DBDBDB",
action: {
hasFailed.toggle()
hasPassed = false
isnotApplicable = false
checklistvm.updateChecklist(documentID, hasPassed: hasPassed, hasFailed: hasFailed, isnotApplicable: isnotApplicable)
})
ChecklistButton(text: "N/A", textColor: isnotApplicable ? "#89AA00" : "#696969", buttonColor: isnotApplicable ? "#EDEFB0" : "#DBDBDB",
action: {
isnotApplicable.toggle()
hasFailed = false
hasPassed = false
checklistvm.updateChecklist(documentID, hasPassed: hasPassed, hasFailed: hasFailed, isnotApplicable: isnotApplicable)
})
}
}
.padding()
.frame(width: UIScreen.main.bounds.width-30 ,height: 161)
.background(.white)
.cornerRadius(31)
}
}
Of course this is a lot of code and I can't share the entire project, but hopefully this can give enough context to lend your assistance.
Using #FirestoreQuery wrapper:
import SwiftUI
import Firebase
import FirebaseFirestore
import FirebaseFirestoreSwift
struct HomeView: View {
#State var searchable = ""
#State var navtoView = false
#State var showLogoutdialog = false
#ObservedObject var homevm = HomeViewModel()
var auth = AuthService()
#Environment(\.presentationMode) var presentationmode
#State var firstname = ""
#ObservedObject var projectvm = ProjectsViewModel()
#State var selectedProject: ProjectsData?
#FirestoreQuery(collectionPath: "Projects") var projects: [ProjectsData]
var body: some View {
NavigationView{
ScrollView(.vertical){
Spacer()
.height(70)
HeaderComponent(firstname: users.firstName)
SearchBar(searchText: $searchable)
.padding(.bottom)
VStack {
ForEach(projects, id: \.id) { item in
ProjectFeedCard(projectTitle: item.title, client: item.client)
.onTapGesture {
self.selectedProject = item
navtoView.toggle()
}
}
}
//MARK: NavigationLinks
NavigationLink(destination:ProjectView(inspections: selectedProject?.inspections ?? [], projectTitle: selectedProject?.title ?? "" , client: selectedProject?.client ?? "",description: selectedProject?.description ?? "", documentID: selectedProject ?? ProjectsData(title: "", client: "", description: "", inspections: []), checklists: []) , isActive: $navtoView){
EmptyView()
}
Spacer()
CustomButton(action: {showLogoutdialog.toggle()}, buttonText: "Log Out", buttonColor: .accentColor)
.padding()
.confirmationDialog("Are you sure?", isPresented: $showLogoutdialog, titleVisibility: .visible){
Button("Sign Out", role: .destructive){
homevm.logOutApp()
presentationmode.wrappedValue.dismiss()
}
}
.onAppear{
projectvm.fetchProjects()
}
}
.background(Color(hexadecimal: "#F8F8F8"))
.ignoresSafeArea()
.navigationBarHidden(true)
}
}
}

I managed to solve the issue by changing the id variable to a String and re-did the fetch projects function. So in short I just completely scrapped the #DocumentID wrapper which didn't seem to work. The #FirestoreQuery query wrapper worked after I changed the ID variable in my data model, but I didn't need it again since my View Model did the job
Data Struct:
struct ProjectsData: Identifiable, Codable{
var id: String
var title: String?
var client: String?
var description: String?
var inspections: [InspectionsData]
enum CodingKeys: String, CodingKey{
case id
case title
case client
case description
case inspections
}
}
I refactored the function that gets documents from Firestore to this:
func fetchFireStoreData() {
db.collection("Projects").getDocuments { snapshot, error in
if error == nil {
if let snapshot = snapshot{
//Get all documents
self.projects = snapshot.documents.map({ document in
let id = document.documentID
let title = document["title"] as? String ?? ""
let client = document["client"] as? String ?? ""
let description = document["description"] as? String ?? ""
let inspections = document["inspections"] as? [String : [String : Any]]
var inspectionsArray = [InspectionsData]()
if let inspections = inspections {
for inspection in inspections {
let categoryText = inspection.value["category"] as? String ?? "error"
let category = Category(rawValue: categoryText) ?? .Electrical
let trade = inspection.value["trade"] as? String ?? "error"
let inspectionDate = (inspection.value["inspectionDate"] as? Timestamp)?.dateValue() ?? Date()
let dueDate = inspection.value["dueDate"] as? Date
let assigned = inspection.value["assigned"] as? Bool ?? false
let location = inspection.value["inspection"] as? String ?? ""
let images = inspection.value["images"] as? [String : [String : Any]]
let checklists = inspection.value["checklists"] as? [String : [String : Any]]
var imagesArray = [InspectionImages]()
var checklistsArray = [ChecklistsData]()
if let checklists = checklists{
for list in checklists {
let question = list.value["question"] as? String ?? "error"
let hasPassed = list.value["hasPassed"] as? Bool ?? false
let hasFailed = list.value["hasFailed"] as? Bool ?? false
let isnotApplicable = list.value["isnotApplicable"] as? Bool ?? false
checklistsArray.append(ChecklistsData(question: question, hasPassed: hasPassed, hasFailed: hasFailed, isnotApplicable: isnotApplicable))
}
}
inspectionsArray.append(InspectionsData(category: category, trade: trade, inspectionDate: inspectionDate , dueDate: dueDate ?? Date.now, assigned: assigned, location: location, images: imagesArray, checklists: checklistsArray))
if let images = images {
for image in images {
let url = image.value["url"] as? String ?? "error"
imagesArray.append(InspectionImages(url: url))
}
}
}
}
self.projects.append(ProjectsData(id: id ,title: title, client: client, description: description, inspections: inspectionsArray))
return ProjectsData(id: id, title: title, client: client, description: description, inspections: inspectionsArray )
})
}
}
}
}
I'm now able to get the correct Document ID and now I can use my updateChecklist function.

Related

Firebase is not saving the data - swiftui

I have a problem that Firebase not saving the data and it shows like that :
enter image description here
First I created a model :
struct Box: Identifiable, Hashable, Codable {
#DocumentID var id: String?
var boxName: String
var boxSize: String
enum CodingKeys: String, CodingKey {
case id
case boxName
case boxSize
}
var dictionary: [String: Any] {
let data = (try? JSONEncoder().encode(self)) ?? Data()
return (try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]) ?? [:]
}
}
Then I created ViewModel:
class BoxViewModel: ObservableObject {
#Published var box: Box
private var db = Firestore.firestore()
init(box: Box = CashBox(boxName: "", boxSize: "")) {
self.box = box
}
private func addNewBox(_ box: Box){
do {
print("the name \(box.boxName)")
let _ = db.collection("Box")
.addDocument(data: box.dictionary)
}
}
func addBox() {
self.addNewBox(box)
}
func setBoxData(boxName: String, boxSize: String){
self.box.boxName = boxName
self.box.boxSize = boxSize
}
}
Finally here is the view:
struct CashBoxView: View {
#State var boxName = ""
#State var boxSize = ""
#EnvironmentObject var boxViewModel: BoxViewModel
var body: some View {
Text("Enter box name")
TextField("", text: $boxName)
Text("Enter box size")
TextField("", text: $boxSize)
Button( action: {
boxViewModel.setBoxData(boxName: boxName, boxSize: boxSize)
boxViewModel.addBox()
}) {
Text("Done")
}
}
First I thought that the problem is the box is empty but when I tried to print the box name print("the name \(box.boxName)") and printed it
is the problem the the viewModel is #EnvironmentObject ? or what is the problem ?
Thank you,

SwiftUI why doesn't my view update once this object property changes?

#ObservedObject var vm: PlayerViewModel
var body : some View {
Button("Test"){
Task{
await vm.getPlayer(username: username)
switch vm.state{
case .success(var player):
let docRef = vm.db.collection("players").document(player.displayname)
docRef.getDocument {document, error in
if let document = document, document.exists {
print("document exists")
player.todayFinalKills = document["todayFinalKills"] as? Int
print(player.todayFinalKills ?? 0)
}
}
}
}
So I just have some firestore database here(not really relevant to the question I don't think) I'm calling some function, I check the state of some enum and then check if the document exists, if it exists I change one property in the associated data of the enum. This is what this prints:
So it does seem that the property is getting updated in the player object for sure but then I have another view with a List
struct DailyView: View {
#ObservedObject var vm: PlayerViewModel
var body: some View {
List{
Text("\(player.todayFinalKills ?? 0)")
}
}
}
But this view always has a zero, implying that this still thinks todayFinalKills is nil? If it literally prints it out as 8291 why is this happening? I was thinking maybe if I switch on a enum like this and extract the associated data maybe it creates a copy but I wasn't able to find much information about this online. Can anyone help me figure out why this is happening? I am passing the same ViewModel into all these views.
Edit: PlayerViewModel.swift
import FirebaseFirestore
import Foundation
#MainActor
class PlayerViewModel: ObservableObject{
enum PlayerState {
case na
case loading
case success(data: Player)
case failed(error : Error)
}
var db = Firestore.firestore()
#Published private(set) var state: PlayerState = .na
private let service: PlayerService
init(service: PlayerService)
{
self.service = service
}
func getPlayer(username: String) async {
state = .loading
do{
let uuid = try await service.fetchUUID(username: username)
let player = try await service.fetchPlayer(uuid: uuid)
state = .success(data: player)
}
catch{
print(error)
state = .failed(error: error)
}
}
}
DailyView.swift
import SwiftUI
struct DailyView: View {
#ObservedObject var vm: PlayerViewModel
var body: some View {
switch vm.state{
case .success(let player):
List{
Text("\(player.todayFinalKills ?? 0)")
}
default:
EmptyView()
}
}
}
struct DailyView_Previews: PreviewProvider {
static var previews: some View {
DailyView(vm: PlayerViewModel(service: PlayerService()))
}
}
LoginView.swift
struct LoginView: View {
#ObservedObject var vm : PlayerViewModel
#State private var username = ""
#State private var profileLoaded = false
private var searchAllowed : Bool{
if(username.count>2)
{
return false
}
return true
}
var body: some View {
NavigationView{
ZStack{
VStack{
Button{
Task{
await vm.getPlayer(username: username)
switch vm.state{
case .success(var player):
let docRef = vm.db.collection("players").document(player.displayname)
docRef.getDocument {document, error in
if let document = document, document.exists {
player.todayFinalKills = document["todayFinalKills"] as? Int
print(player.todayFinalKills ?? 0)
}
else{
let stats : [String : Int] = [
"todayFinalKills": player.stats.Bedwars?.final_kills_bedwars ?? 0,
"todayFinalDeaths" : player.stats.Bedwars?.final_deaths_bedwars ?? 0,
"todayBedwarsWins" : player.stats.Bedwars?.wins_bedwars ?? 0,
"todayBedwarsLosses" : player.stats.Bedwars?.losses_bedwars ?? 0,
"todayTntRunLosses" : player.stats.TNTGames?.deaths_tntrun ?? 0,
"todayTntRunWins" : player.achievements.tntgames_tnt_run_wins ?? 0,
]
vm.db.newPlayerDocument(data: stats, username: player.displayname, uuid: player.uuid)
}
}
profileLoaded = true
default:
print("default")
}
}
} label:
{
ZStack{
RoundedRectangle(cornerRadius: 5)
.fill(.gray)
.frame(width: 200, height: 50)
Text("Search")
.foregroundColor(.white)
}
}
Model.swift
struct Player : Codable, Hashable, Equatable{
static func == (lhs: Player, rhs: Player) -> Bool {
lhs.uuid == rhs.uuid
}
// so a player contains many objects, like stats, achievements, etc
var stats: Stats
var achievements: Achievements
var displayname: String
var uuid : String
var networkExp: Int
var knownAliases : [String]
var karma: Int
var newPackageRank : String? //this could be MVP_PLUS
var rankPlusColor : String? //this could be LIGHT_PURPLE
//daily statistics for the player.
**var todayFinalKills : Int?**
var todayBedwarsLosses : Int?
var todayBedwarsWins: Int?
var todayFinalDeaths: Int?
var todayTntRunWins: Int?
var TodayTntRunLosses: Int?
//

SwiftUI: Having a plist to display

I am trying to read a plist and having it displayed as a List on SwiftUI. It compiles with no errors or warnings, but nothing gets displayed. I am not sure what I am doing wrong or the mistake I am making here. I've tried multiple things, but I still get a blank display.
import Foundation
struct PresidentModel: Decodable{
var Name: String
var Number: Int
var StartDate: String
var EndDate: String
var Nickname: String
var PoliticalParty: String
enum CodingKeys: String, CodingKey{
case Name = "Name"
case Number = "Number"
case StartDate = "Start Date"
case EndDate = "End Date"
case Nickname = "Nickname"
case PoliticalParty = "Political Party"
}
}
class PresidentViewModel: ObservableObject{
#Published var PresArray: [PresidentModel] = []
#Published var name: String = ""
#Published var number: Int = 0
#Published var startDate: String = ""
#Published var endDate: String = ""
#Published var nickname: String = ""
#Published var politicalParty: String = ""
func loadProperityListData(){
guard let path = Bundle.main.path(forResource: "presidents", ofType: "plist"), let xml = FileManager.default.contents(atPath: path) else {
fatalError("Unable to access property list states.plist")
}
do{
PresArray = try PropertyListDecoder().decode([PresidentModel].self, from: xml)
name = PresArray[0].Name
number = PresArray[0].Number
startDate = PresArray[0].StartDate
endDate = PresArray[0].EndDate
nickname = PresArray[0].Nickname
politicalParty = PresArray[0].PoliticalParty
}
catch {
fatalError("Unable to decode property list states.plist")
}
}//func
}//class
Where the plist will be displayed as a List :
import SwiftUI
struct ContentView: View {
let presidents = PresidentViewModel()
var body: some View {
List(presidents.PresArray.indices, id: \.self){ president in
Text(presidents.PresArray[].Name)
}
}//Body
}//View
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
First of all please conform to the naming convention and declare the struct member names with starting lowercase letters and add an id property
struct PresidentModel: Decodable, Identifiable {
let id = UUID()
let name: String
let number: Int
let startDate: String
let endDate: String
let nickname: String
let politicalParty: String
private enum CodingKeys: String, CodingKey {
case name = "Name"
case number = "Number"
case startDate = "Start Date"
case endDate = "End Date"
case nickname = "Nickname"
case politicalParty = "Political Party"
}
}
The main problem is that you don't load the data, a good place is the init method of the observable class. The properties are not needed because the array contains all data
class PresidentViewModel: ObservableObject{
#Published var presArray: [PresidentModel] = []
init() {
loadProperityListData()
}
func loadProperityListData(){
guard let url = Bundle.main.url(forResource: "presidents", withExtension: "plist"),
let data = try? Data(contentsOf: url) else {
fatalError("Unable to access property list states.plist")
}
do {
presArray = try PropertyListDecoder().decode([PresidentModel].self, from: data)
} catch {
print(error)
presArray = []
}
}//func
}//class
The second main problem is that PresidentViewModel is not declared as #StateObject. And due to the added id property you get rid of dealing with indices and specifying id: \.self
import SwiftUI
struct ContentView: View {
#StateObject var model = PresidentViewModel()
var body: some View {
List(model.presArray) { president in
Text(president.name)
}
}//Body
}//View

Updating Firestore data from a SwiftUI view in xcode not working

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"
}
}

pass values dynamically for network request

I have to pass the value of movie.id which is received from a View which is called ReviewView.
I need to pass the movie.id value received in this view to ReviewFetcher and then make a network request using that movie.id. As of now I have hard coded the movie id in ReviewFetcher but I require this to be received from ReviewView and then make a request and then update the list in ReviewView.
Below is the Code:-
ReviewFetcher.swift
import Foundation
import Alamofire
import SwiftUI
class ReviewObserver: ObservableObject {
#Published var review = ReviewArray(id: 1, page: 9, results: [])
// #State var movieID:Int
init() {
// self.movieID = movieID
getReviews(movieID : 181812)
}
func getReviews(movieID:Int) {
//self.review.results.removeAll()
let reviewURL = "https://api.themoviedb.org/3/movie/"+String(movieID)+"/reviews?api_key=a18f578d774935ef9f0453d7d5fa11ae&language=en-US&page=1"
Alamofire.request(reviewURL)
.responseJSON { response in
if let json = response.result.value {
if (json as? [String : AnyObject]) != nil {
if let dictionaryArray = json as? Dictionary<String, AnyObject?> {
let json = dictionaryArray
if let id = json["id"] as? Int,
let page = json["page"] as? Int,
let results = json["results"] as? Array<Dictionary<String, AnyObject?>> {
for i in 0..<results.count {
if let author = results[i]["author"] as? String,
let content = results[i]["content"] as? String,
let url = results[i]["url"] as? String {
let newReview = ReviewModel(author: author,
content: content,
url: url)
self.review.results.append(newReview)
}
}
}
}
}
}
}
}
}
ReviewView.swift
import SwiftUI
struct ReviewsView: View {
#State var movie: MovieModel
#Binding var reviews:[ReviewModel]
#ObservedObject var fetcher = ReviewObserver()
var body: some View {
VStack(alignment:.leading) {
Text("Review")
.font(.largeTitle)
.bold()
.foregroundColor(Color.steam_rust)
.padding(.leading)
Divider()
// Text(String(fetcher.movieID))
List(fetcher.review.results) { item in
VStack(alignment:.leading) {
Text("Written by : "+item.author)
.font(.body)
.bold()
.padding(.bottom)
Text(item.content)
.font(.body)
.lineLimit(.max)
}
}
}
}
}
MovieModel.swift
import Foundation
import SwiftUI
import Combine
struct MovieArray: Codable {
var page: Int = 0
var total_results: Int = 0
var total_pages: Int = 0
var results: [MovieModel] = []
}
struct MovieModel: Codable, Identifiable {
var id : Int
var original_title: String
var title: String
var original_language:String
var overview: String
var poster_path: String?
var backdrop_path: String?
var popularity: Double
var vote_average: Double
var vote_count: Int
var video: Bool
var adult: Bool
var release_date: String?
}
Remove the init() of your ReviewObserver class. and then call getReviews method in .onAppear modifier of your VStack. The idea of what you need:
class ReviewObserver: ObservableObject {
#Published var review = ReviewArray(id: 1, page: 9, results: [])
func getReviews(movieID:Int) {
//you block,, anything you wanna do with movieID.
//Assume you are going to change 'review' variable
}
}
struct ReviewsView: View {
#State var movie:MovieModel
#Binding var reviews:[ReviewModel]
#ObservedObject var fetcher = ReviewObserver()
var body: some View {
VStack(alignment:.leading){
Text("Review")
Divider()
Text(String(fetcher.movieID))
List(fetcher.review.results)
{
item in
VStack(alignment:.leading){
Text("Written by : "+item.author)
}
}.onAppear {
self.fetcher.getReviews(movieID: movie.id)
}
}
}

Resources