Unable to return value from Struct to View in SwiftUI - swift

So I'll keep it simple. I've seen questions with similar error.
Unexpected non-void return value in void function
But I already defined medicalContent as a string so I don't know what's goin wrong. I just wish to pass the return medicalContent string from the struct to the textEditor in the view. Any input shall be appreciated!
Code:
struct MedicalEdit: View {
#State var medicalContent: String = ""
init(){
fetchCalmStuff()
}
func fetchCalmStuff(){
let user = Auth.auth().currentUser
let uid = Auth.auth().currentUser!.uid
let db = Firestore.firestore()
let ref = db.collection("list").document(uid).collection("notes")
ref.document("medical").setData(["content":"medical content","identity":"medical" ])
db.collection("userslist").document(uid).collection("Themes").whereField("identity", isEqualTo: "medical")
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
let medicalContent = data["content"] as! String ?? ""
print(medicalContent)
return medicalContent // this is where I get error // that Unexpected non-void return value in void function.
}
}
}
}
var body: some View {
TextEditor(text: $medicalContent)
}
}
EDIT: Also how come it can print medicalContent but cannot return it? I'm kinda confused.

Related

How to grab the current users "firstname" from firebase store. Swift 5

I did more trial and error and a bit of online research and this is what I came back with:
func presentWelcomeMessage() {
//Get specific document from current user
let docRef = Firestore.firestore()
.collection("users")
.whereField("uid", isEqualTo: Auth.auth().currentUser?.uid ?? "")
// Get data
docRef.getDocuments { (querySnapshot, err) in
if let err = err {
print(err.localizedDescription)
} else if querySnapshot!.documents.count != 1 {
print("More than one document or none")
} else {
let document = querySnapshot!.documents.first
let dataDescription = document?.data()
guard let firstname = dataDescription?["firstname"] else { return }
self.welcomeLabel.text = "Hey, \(firstname) welcome!"
}
}
It works, but am not sure if it is the most optimal solution.
First I should say firstname is not really the best way to store a var. I would recommend using firstName instead for readability. I also recommend getting single documents like I am, rather than using a whereField.
An important thing to note is you should create a data model like I have that can hold all of the information you get.
Here is a full structure of how I would get the data, display it, and hold it.
struct UserModel: Identifiable, Codable {
var id: String
var firstName: String
private enum CodingKeys: String, CodingKey {
case id
case firstName
}
}
import SwiftUI
import FirebaseAuth
import FirebaseFirestore
import FirebaseFirestoreSwift
class UserDataManager: ObservableObject {
private lazy var authRef = Auth.auth()
private lazy var userInfoCollection = Firestore.firestore().collection("users")
public func getCurrentUIDData(completion: #escaping (_ currentUserData: UserModel) -> Void) {
if let currentUID = self.authRef.currentUser?.uid {
self.userInfoCollection.document(currentUID).getDocument { (document, error) in
if let document = document {
if let userData = try? document.data(as: UserModel.self) {
completion(userData)
}
} else if let error = error {
print("Error getting current UID data: \(error)")
}
}
} else {
print("No current UID")
}
}
}
struct ContentView: View {
#State private var userData: UserModel? = nil
private let
var body: some View {
ZStack {
if let userData = self.userData { <-- safely unwrap data
Text("Hey, \(userData.firstName) welcome!")
}
}
.onAppear {
if self.userData == nil { <-- onAppear can call more than once
self.udm.getCurrentUIDData { userData in
self.userData = userData <-- pass data from func to view
}
}
}
}
}
Hopefully this can point you in a better direction of how you should be getting and displaying data. Let me know if you have any further questions or issues.

How to make likes updateable without registration?

I made a code that adds likes and shows their number on the screen.
But there is a problem, when you download the application on 2 devices and press the button at the same time, then only one like is counted. How can I fix this without implementing registration?
There is an idea to make fields that will be created for everyone on the phone when the like is pressed and this number will be added to the total, but I do not know how to implement this.
Here's the current code:
struct LikeCounts {
var likecount: String
}
class LikeTextModel: ObservableObject {
#Published var likecounts: LikeCounts!
private var db = Firestore.firestore()
init() {
updateLike()
}
func updateLike() {
db.collection("likes").document("LikeCounter")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
if let likecount = data["likecount"] as? String {
DispatchQueue.main.async {
self.likecounts = LikeCounts(likecount: likecount)
}
}
}
}
#ObservedObject private var likeModel = LikeTextModel()
if self.likeModel.likecounts != nil{
Button(action:
{self.like.toggle()
like ? addlike(): dellike()
UserDefaults.standard.setValue(self.like, forKey: "like")
}) {
Text((Text(self.likeModel.likecounts.likecount))}
func addlike() {
let db = Firestore.firestore()
let like = Int.init(self.likeModel.likecounts.likecount)
db.collection("likes").document("LikeCounter").updateData(["likecount": "\(like! + 1)"]) { (err) in
if err != nil {
print(err)
return
}
}
}
func dellike() {
let db = Firestore.firestore()
let like = Int.init(self.likeModel.likecounts.likecount)
db.collection("likes").document("LikeCounter").updateData(["likecount": "\(like! - 1)"]) { (err) in
if err != nil {
print(err)
return
}
}
}
Firestore has the ability to reliably increment a value, like this:
db.collection('likes').doc('LikeCounter')
.updateData([
"likecount": FieldValue.increment(1)
]);

Firestore Swift update text realtime

I have this way of collecting information.
struct MainText {
var mtext: String
var memoji: String
}
class MainTextModel: ObservableObject {
#Published var maintext : MainText!
init() {
updateData()
}
func updateData() {
let db = Firestore.firestore()
db.collection("maintext").document("Main").getDocument { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
let memoji = snap?.get("memoji") as! String
let mtext = snap?.get("mtext") as! String
DispatchQueue.main.async {
self.maintext = MainText(mtext: mtext, memoji: memoji)
}
}
}
}
And such a way of displaying.
#ObservedObject private var viewModel = MainTextModel()
self.viewModel.maintext.memoji
self.viewModel.maintext.mtext
How can I update online without rebooting the view?
Instead of using getDocument, which only gets the document once and doesn't return updates, you'll want to add a snapshot listener.
Here's the Firestore documentation for that: https://firebase.google.com/docs/firestore/query-data/listen
In your case, you'll want to do something like:
db.collection("maintext").document("Main")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
if let memoji = data["memoji"] as? String, let mtext = data["mtext"] as? String {
self.maintext = MainText(mtext: mtext, memoji: memoji)
}
}

How to map fields from Firestore documents in Swift

In my Firestore database, I have "favorites" getting stored like this:
How can I get the values "S1533" and "S2017" based on itemActive = true?
Here is the Swift code I have, but I am stuck on how to look at itemActive and then go back and return the values that have that field as set to true.
db.collection("users").document(userId!).addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
print(data["favorites"])
}
The easiest way to map Firestore documents is to use Codable. This article to learn about the basics.
For your model, the following code should get you started:
Model
struct Favourite: Codable, Identifiable {
var itemActive: Bool
var itemAdded: Date
}
struct UserPreference: Codable, Identifiable {
#DocumentID public var id: String?
var displayName: String
var email: String
var favourites: [Favourite]?
}
Fetching data
public class UserPreferenceRepository: ObservableObject {
var db = Firestore.fireStore()
#Published var userPreferences = [UserPreference]()
private var listenerRegistration: ListenerRegistration?
public func subscribe() {
if listenerRegistration == nil {
var query = db.collection("users")
listenerRegistration =
query.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
self?.logger.debug("No documents")
return
}
self?.userPreferences = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: UserPreference.self)
}
}
}
}

How to populate loaded records from firebase?

I wrote the function to lad the records from firebase but there's an error
Escaping closure captures mutating 'self' parameter
The function is written as follows:
let db = Firestore.firestore()
#State var libraryImages: [LibraryImage] = []
mutating func loadImages() {
libraryImages = []
db.collection(K.FStore.CollectionImages.collectionName).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
for document in snapshotDocuments {
let documentData = document.data()
let title: String = documentData[K.FStore.CollectionImages.title] as! String
let thumbnailUrl: String = documentData[K.FStore.CollectionImages.thumbnailUrl] as! String
let svgUrl: String = documentData[K.FStore.CollectionImages.svgUrl] as! String
let libraryImageItem = LibraryImage(title: title, thumbnailUrl: thumbnailUrl, svgUrl: svgUrl)
self.libraryImages.append(libraryImageItem)
}
}
}
}
}
Does anyone know what is causing an error and how to get rid of it?
Move all this into reference type view model and use it as observed object in your view
Here is a demo of possible approach:
struct DemoView: View {
#ObservedObject var vm = ImagesViewModel()
// #StateObject var vm = ImagesViewModel() // << SwiftUI 2.0
var body: some View {
Text("Loaded images: \(vm.libraryImages.count)")
.onAppear {
self.vm.loadImages()
}
}
}
class ImagesViewModel: ObservableObject {
let db = Firestore.firestore()
#Published var libraryImages: [LibraryImage] = []
func loadImages() {
libraryImages = []
db.collection(K.FStore.CollectionImages.collectionName).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
var images = [LibraryImage]()
for document in snapshotDocuments {
let documentData = document.data()
let title: String = documentData[K.FStore.CollectionImages.title] as! String
let thumbnailUrl: String = documentData[K.FStore.CollectionImages.thumbnailUrl] as! String
let svgUrl: String = documentData[K.FStore.CollectionImages.svgUrl] as! String
let libraryImageItem = LibraryImage(title: title, thumbnailUrl: thumbnailUrl, svgUrl: svgUrl)
images.append(libraryImageItem)
}
DispatchQueue.main.async {
self.libraryImages = images
}
}
}
}
}
}