Swift 4 uploading images to Firebase - firebase

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

How to write an Enum in Firestore written in SwiftUI?

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

Firestore : How can I get whole data in Firestore?

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!

Reference to member 'decor' cannot be resolved without a contextual type

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

Is there a way to give initial value in an instance in property initializer dynamically using SwiftUI and Firebase?

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.

Converting DataSnapshot to custom struct object - Swift 4

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
}

Resources