Good afternoon everyone, I am trying to upload an image to firebase and display back on a cell..I have just about all the content working except the image (i cant get rid of the error).
Here is the error I am getting
Cannot invoke initializer for type 'Posts' with an argument list of type '(postImageStringUrl: String, content: String!, postId: String)'
//[Save Image]
// Create data in the server
let data = UIImageJPEGRepresentation(self.addedImage.image!, 0.5)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let postId = "\(Auth.auth().currentUser!.uid)\(NSUUID().uuidString)"
// Create a reference to the file you want to upload
let imagePath = "postImages\(postId)/postPic.jpg"
storageRef.child(imagePath).putData(data!, metadata: metadata) { (metadata, error) in
if error == nil {
let postRef = self.databaseRef.child("posts").childByAutoId()
let post = Posts(postImageStringUrl: String (describing: metadata!.downloadURL()), content: descriptionTextView.text, postId: postId)
postRef.setValue(post.toAnyObject())
}else{
print(error.debugDescription)
}
}
//[Save Image]
Here is my postViewController
import Foundation
import Firebase
import FirebaseDatabase
import FirebaseStorage
struct Posts {
var postImageStringUrl: String!
var department: String!
var content: String!
var username: String!
var postId: String!
var ref: DatabaseReference?
var key: String!
init(postImageStringUrl: String, department: String, content: String, username: String,postId: String, key: String = ""){
self.postImageStringUrl = postImageStringUrl
self.department = department
self.content = content
self.username = username
self.postId = postId
self.key = key
self.ref = Database.database().reference()
}
init(snapshot: DataSnapshot){
let snapshotValue = snapshot.value as! NSMutableDictionary
self.postImageStringUrl = snapshotValue["postImageStringUrl"] as! String
self.department = snapshotValue["department"] as! String
self.content = snapshotValue["content"] as! String
self.username = snapshotValue["username"] as! String
self.postId = snapshotValue["postId"] as! String
self.key = snapshot.key
self.ref = snapshot.ref
}
func toAnyObject() -> [String: AnyObject] {
return ["postImageStringUrl": postImageStringUrl as AnyObject, "department": department as AnyObject,"content": content as AnyObject,"username": username as AnyObject, "postId": postId as AnyObject]
}
}
Any help will be greatly appreciated...Ty
Your Posts init is
init(
postImageStringUrl: String,
department: String,
content: String,
username: String,
postId: String,
key: String = "")
and it appears you are trying to initialize it with
let post = Posts(
postImageStringUrl: String (describing: metadata!.downloadURL()),
content: descriptionTextView.text,
postId: postId)
Basically just missing a couple of parameters in the initialization; department, username
Related
I've made a little app where I have categories, and in each categories I have different products, that works well with barcoded data, but now I want to write them in Firestore and fetch them in my application.
I have 2 Struct, one for the products :
struct Product : Identifiable, Hashable {
var id = UUID().uuidString
var type : ProductType
var title : String
var subtitle : String
var description : String = ""
var price : String
var productImage : String = ""
var quantity : Int = 1
}
and one for the category type
enum ProductType : String, CaseIterable {
case Wearable = "Wearable"
case Laptops = "Laptops"
case Phones = "Phones"
case Tablets = "Tablets"
}
This is how I wrote the Product in Firestore : Picture 1, but I do not know how to write the enum.
Also, this is how I'm getting the data from the firestore, but I have an error at:
return Product(id: d.documentID, type: d["type"] as? String ?? ""
Cannot convert value of type 'String' to expected argument type 'ProductType'
func getData() {
FirebaseManager.shared.firestore.collection("products").getDocuments { snapshot, error in
if error == nil {
if let snapshot = snapshot {
DispatchQueue.main.async {
self.products = snapshot.documents.map { d in
return Product(id: d.documentID, type: d["type"] as? String ?? ""
, title: d["title"] as? String ?? "",
subtitle: d["subtitle"] as? String ?? "",
price: d["price"] as? String ?? "", productImage: d["productImage"] as? String ?? ""
)
}
}
}
}
else {
}
}
}
This is for "Lore Ipsum comment "
func getData() {
FirebaseManager.shared.firestore.collection("products").getDocuments { snapshot, error in
if error == nil {
if let snapshot = snapshot {
DispatchQueue.main.async {
self.products = snapshot.documents.compactMap { document in
try? document.data(as: Product.self)
} ?? []
}
}
}
else {
}
}
}
Also, this is my FirebaseManager :
class FirebaseManager : NSObject {
let auth : Auth
let storage : Storage
let firestore : Firestore
static let shared = FirebaseManager()
override init() {
self.auth = Auth.auth()
self.storage = Storage.storage()
self.firestore = Firestore.firestore()
}
}
Question, how can I write that enum into firestore?
I think it could work like this
Even if the code readability is reduced
type: ProductType(rawValue: d["type"] as? String ?? "") ?? .Wearable // default value for ProductType
This ViewModel is about saving data in Firestore with specific data types.
import Foundation
import Firebase
import FirebaseStorage
import FirebaseStorageSwift
import UIKit
class UploadViewModel : ObservableObject {
#Published var loading : Bool = false
func storeImageWithUrl(images : [UIImage], completion : #escaping (_ urls : [String]) -> ()) {
self.loading = true
var count = 0
var urls : [String] = []
for image in images {
let ref = Storage.storage().reference(withPath: UUID().uuidString)
guard let imageData = image.jpegData(compressionQuality: 0.5) else {return}
ref.putData(imageData, metadata: nil) { metaData, error in
if let error = error {
print("failed to push image cause of error")
return
}
ref.downloadURL { url, error in
if let error = error {
print("error to make url")
return
}
guard let url = url else {return}
count += 1
urls.append(url.absoluteString)
if count == images.count {
completion(urls)
}
}
}
}
}
func storeItemInformation(title : String, description : String, category : String, contactInfo : String, price : String, imageUrls : [String], timeStamp : Date = Date(), saved : Bool = false, seller : String, completion : #escaping (_ result : Bool) -> ()) {
let uid = AuthService.instance.makeUid()
guard let userData = ["title": title, "description" : description, "category" : category, "price" : price, "imageURL" : imageUrls, "timestamp" : timeStamp, "contactInfo" : contactInfo, "saved" : saved, "seller" : seller] as? [String : Any] else { return }
Firestore.firestore()
.collection("Wholeitems")
.document(category)
.collection(uid)
.document(title)
.setData(userData) { error in
if let error = error {
print("Error to save whole Data")
completion(false)
return
}
print("Success to save whole data")
self.loading = false
completion(true)
}
}
}
Below code is about fetching data from Firestore with ItemModel.
import Foundation
import SwiftUI
import Firebase
import FirebaseAuth
class FeedViewModel : ObservableObject {
#Published var feeds : [ItemModel] = []
init() {
fetchItems()
print(feeds)
}
func fetchItems() {
Firestore.firestore()
.collection("Wholeitems")
.getDocuments { snapshot, error in
if let error = error {
print("error to get data")
return
}
if let snapshot = snapshot {
DispatchQueue.main.async {
self.feeds = snapshot.documents.map({ d in
return ItemModel(id: d.documentID,
category: d["category"] as? String ?? "",
contactInfo: d["contactInfo"] as? String ?? "",
description: d["description"] as? String ?? "",
price: d["price"] as? String ?? "",
timestamp: d["timestamp"] as? String ?? "",
title: d["title"] as? String ?? "",
saved : d["saved"] as? Bool ?? false,
seller: d["seller"] as? String ?? "",
imageURL: d["imageURL"] as? [String] ?? []
)
})
}
}
}
}
}
The problem is that I don't know why the feeds array is empty.
I tried to get all of the documents in the Firestore collection named Wholeitems. But, the array is empty.
Could you help me? Thanks!
I have been following the tutorial on youtube by Reality School. This part is for the firebase store model for storing usdz models with thumbnails. I have got stuck at .decor it is saying " Reference to member 'decor' cannot be resolved without a contextual type"
I do not have any reference to .decor in any other part of the code. And I can't find anything about it. Can anyone give some pointers in the correct direction?
import Foundation
import FirebaseFirestore
class ModelsViewModel: ObservableObject {
#Published var models: [Model] = []
private let db = Firestore.firestore()
func fetchData() {
db.collection("models").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("Firestore: No documents")
return
}
self.models = documents.map { (queryDocumentSnapshot) -> Model in
let data = queryDocumentSnapshot.data()
let name = data["name"] as? String ?? ""
let categoryText = data["category"] as? String ?? ""
let category = ModelCategory(rawValue: categoryText) ?? .decor
let scaleCompensation = data["scaleCompensation"] as? Double ?? 1.0
return Model(name: name, category: category, scaleCompensation: Float(scaleCompensation))
}
}
}
}
Here is also the model code.
import SwiftUI
import RealityKit
import Combine
enum ModelCategory: String, CaseIterable {
case porches
case gazebos
case pergolas
case garages
case roomabove
case loftedroomabove
var lable: String {
get {
switch self {
case .porches:
return "Porches"
case .gazebos:
return "Gazebos"
case . pergolas:
return "Pergolas"
case .garages:
return "Garages"
case .roomabove:
return "Room Above"
case .loftedroomabove:
return "Lofted Room Above"
}
}
}
}
class Model: ObservableObject, Identifiable {
var id: String = UUID().uuidString
var name: String
var category: ModelCategory
#Published var thumbnail: UIImage
var modelEntity: ModelEntity?
var scaleCompensation: Float
private var cancellable: AnyCancellable?
init(name: String, category: ModelCategory, scaleCompensation: Float = 1.0) {
self.name = name
self.category = category
self.thumbnail = UIImage(systemName: "photo")!
self.scaleCompensation = scaleCompensation
FirebaseStorageHelper.asyncDownloadToFilesystem(relativePath: "thumbnails/\(self.name).png") { localUrl in
do {
let imageData = try Data(contentsOf: localUrl)
self.thumbnail = UIImage(data: imageData) ?? self.thumbnail
} catch {
print("Error loading image: \(error.localizedDescription)")
}
}
}
//async model loading
func asyncLoadModelEntity() {
let filename = self.name + ".usdz"
self.cancellable = ModelEntity.loadModelAsync(named: filename)
.sink(receiveCompletion: { loadCompletion in
switch loadCompletion {
case .failure(let error): print("Unable to load modelEntity for \(filename) Error: \(error.localizedDescription)")
case .finished:
break
}
}, receiveValue: { modelEntity in
self.modelEntity = modelEntity
self.modelEntity?.scale *= self.scaleCompensation
print("modelEntity for \(self.name) has been loaded")
})
}
}
Hi I am currently making a dating app's chat page that you can have different rooms for every match using SwiftUI and Cloud Firestore.
I would like to show different chat room every time you tap different user on the top page depending on the matchId.
For now, I need to type the right one in the View file in order to make it work correctly, however, Id like to assign it dynamically.
How can I add the correct matchId to the instance in the View file? Or, should I try different ways?
First, this is the top page.
VStack{
Text("Match Users")
List(self.shareData.matchUserArray){ user in
NavigationLink(destination: MessageView(matchUserInfo: user)){
HStack{
Text(user.name)
Text(user.age)
}
}
}
}
And this is the View file. Without typing "Ll73RINefGxEcYQJoWSE" in the MessageViewModel instance and instead giving it "", I can see the messages in the debug area but don't see any in List.
struct MessageView: View {
var matchUserInfo: User
#ObservedObject var msgVM = MessageViewModel(matchId: "Ll73RINefGxEcYQJoWSE")
#EnvironmentObject var shareData : ShareData
#State var text = ""
#State var matchId = ""
var body: some View {
VStack{
List(self.msgVM.messages, id: \.id){ i in
if i.fromUser == self.shareData.currentUserData["id"] as? String ?? ""
{
MessageRow(message: i.msg, isMyMessage: true)
} else if i.toUser == self.shareData.currentUserData["id"] as? String ?? ""
{
MessageRow(message: i.msg, isMyMessage: false)
}
}
.onAppear { UITableView.appearance().separatorStyle = .none }
.onDisappear { UITableView.appearance().separatorStyle = .singleLine }
HStack{
TextField("message here", text: $text).textFieldStyle(RoundedBorderTextFieldStyle()).padding()
Button(action: {
if self.text.count > 0 {
self.msgVM.sendMsg(msg: self.text, toUser: self.matchUserInfo.id, fromUser: self.shareData.currentUserData["id"] as! String, matchId: self.msgVM.matchId)
self.text = ""
}
}) {
Image(systemName: "paperplane")
}.padding(.trailing)
}
}
.navigationBarTitle("\(self.matchUserInfo.name)", displayMode: .inline)
.onAppear{
DispatchQueue.global().async{
self.getMatchId(partner: self.matchUserInfo)
}
_ = MessageViewModel(matchId: self.matchId)
}
.onDisappear{
print(self.msgVM.messages)
}
}
func getMatchId(partner: User){
Firestore.firestore().collection("MatchTable").document(self.shareData.currentUserData["id"] as? String ?? "").collection("MatchUser").whereField("MatchUserId", isEqualTo: partner.id).getDocuments { (snap, err) in
if let snap = snap {
for id in snap.documents{
self.msgVM.matchId = id.data()["MatchRoomId"] as? String ?? ""
_ = MessageViewModel(matchId: self.msgVM.matchId)
self.matchId = self.msgVM.matchId
}
}
}
}
}
Also this is the firebase part.
import Foundation
import FirebaseFirestore
struct Message: Identifiable {
var id: String
var msg: String
var fromUser: String
var toUser: String
var date: Timestamp
var matchId : String
}
class MessageViewModel: ObservableObject {
var datas = FirebaseData()
let db = Firestore.firestore()
#Published var matchId:String
#Published var messages = [Message]()
init(matchId: String){
self.matchId = matchId
self.db.collection("Messages").whereField("matchId", isEqualTo: self.matchId).order(by: "date").addSnapshotListener { (snap, error) in
if let error = error {
print(error.localizedDescription)
return
}
if let snap = snap {
for i in snap.documentChanges {
if i.type == .added{
let toUser = i.document.get("toUser") as! String
let fromUser = i.document.get("fromUser") as! String
let message = i.document.get("message") as! String
let id = i.document.documentID
let date = i.document.get("date") as! Timestamp
let matchId = i.document.get("matchId") as! String
self.messages.append(Message(id: id, msg: message, fromUser: fromUser, toUser: toUser, date: date, matchId: matchId))
}
}
}
}
}
func sendMsg(msg: String, toUser: String, fromUser: String, matchId: String){
let data = [
"message": msg,
"toUser": toUser,
"fromUser": fromUser,
"date": Timestamp(),
"matchId": matchId
] as [String : Any]
Firestore.firestore().collection("Messages").addDocument(data: data){ error in
if let err = error {
print(err.localizedDescription)
return
}
print("Sent message")
}
}
}
Thank you
All you should really need is to construct your ObservedObject in an init function:
let matchUserInfo: User
#ObservedObject private var msgVM: MessageViewModel
init(_ user: User) {
self.matchUserInfo = user
self._msgVM = ObservedObject(initialValue: MessageViewModel(matchId: user.matchId))
}
Assuming, of course, that the matchId you care about is passed in via your User type. You know your data structures better than I do, the key here is to simply create your observed object based on your passed in User.
So, In my project I've created a custom struct:
import Foundation
import Firebase
struct CheckedIn {
let ref: DatabaseReference?
let key: String
let max_cm: String
let max_in: String
let weight_kg: Int
let weight_lb: Int
init(key: String = "", max_cm: String, max_in: String, weight_kg: Int, weight_lb: Int) {
self.ref = nil
self.key = key
self.max_cm = max_cm
self.max_in = max_in
self.weight_kg = weight_kg
self.weight_lb = weight_lb
}
init?(snapshot: DataSnapshot) {
guard
let value = snapshot.value as? [String: AnyObject],
let max_cm = value["checked_max_cm"] as? String,
let max_in = value["checked_max_in"] as? String,
let weight_kg = value["checked_weight_kg"] as? Int,
let weight_lb = value["checked_weight_in"] as? Int
else {
return nil
}
self.ref = snapshot.ref as DatabaseReference
self.key = snapshot.key
self.max_cm = max_cm
self.max_in = max_in
self.weight_kg = weight_kg
self.weight_lb = weight_lb
}
func toAnyObject() -> Any {
return [
"checked_max_cm": self.max_cm,
"checked_max_in": self.max_in,
"checked_weight_kg": self.weight_kg,
"checked_weight_in": self.weight_lb
]
}
}
This is the structure of my database:
And I'm trying to retrieve some data in a view controller using the following code:
ref.queryOrderedByKey().observe(.value) { (snapshot) in
var newItems: [CheckedIn] = []
for child in snapshot.children {
print(child)
if let snapshot = child as? DataSnapshot, let company = CheckedIn(snapshot: snapshot) {
newItems.append(company)
}
}
print(newItems)
self.companiesCheckedIn = newItems
self.tableView.reloadData()
UIViewController.removeSpinner(spinner: sv)
}
The problem is that, whenever I try to open the view controller (this chunk of code is inside the .viewDidLoad), the list 'newItems' is empty. The strange thing is that when I try to run
print(child)
all of the data is printed,
So I assume that the 'if let snapshot = child as? DataSnapshot, let company = CarryOn(snapshot: snapshot)' block is not running, what should I do?
Managed to solve the problem using a workaround:
This code goes before the viewDidLoad() method of your ViewController
let ref = Database.database().reference().child("alldata")
var companyName: [String] = []
var maxCm: [String] = []
var maxIn: [String] = []
var weightKg: [Int] = []
var weightLb: [Int] = []
And in the viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
ref.queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
let snapshotValue = snapshot.value as! [String:[String:AnyObject]]
let sorted = snapshotValue.sorted() { $0.key.lowercased() < $1.key.lowercased() }
for (key, _) in sorted {
let keys = snapshotValue[key]
let company_name = key
self.companyName.append(company_name)
let carry_max_cm = keys?["keyname"] as! String
self.maxCm.append(carry_max_cm)
let carry_max_in = keys?["keyname"] as! String
self.maxIn.append(carry_max_in)
let carry_weight_kg = keys?["keyname"] as! Int
self.weightKg.append(carry_weight_kg)
let carry_weight_lb = keys?["keyname"] as! Int
self.weightLb.append(carry_weight_lb)
}
self.tableView.reloadData()
}
}
let sorted = snapshotValue.sorted() { $0.key.lowercased() < $1.key.lowercased() }
Orders the dictionary alphabetically
your "checked_weight_kg" & "checked_weight_in" is a integer type but you assign String type.
your code ->
let weight_kg = value["checked_weight_kg"] as? String,
let weight_lb = value["checked_weight_in"] as? String
to Change ->
init?(snapshot: DataSnapshot) {
guard
let value = snapshot.value as? [String: AnyObject],
let max_cm = value["carry_max_cm"] as? String,
let max_in = value["carry_max_in"] as? String,
let weight_kg = value["checked_weight_kg"] as? Int,
let weight_lb = value["checked_weight_in"] as? Int
else {
return nil
}
self.ref = snapshot.ref as DatabaseReference
self.key = snapshot.key
self.max_cm = max_cm
self.max_in = max_in
self.weight_kg = weight_kg
self.weight_lb = weight_lb
}