I run a python script on my raspberry pi that takes a picture, moves it in the cloud and uploads the link to firebase.
Because the picture taking and uploading takes approximately 15 seconds I'm using DispatchQueue.main.asyncAfter.
In my current state, I'm able to take pictures whenever I want to, but there are 2 things I can't do.
How to be able to get the firebase element whenever a change happened. Python can do that, I don't know what the swift method for that.
The picture doesn't show. Is it because I want to display it from DispatchQueue.main.asyncAfter?
Thanks
import UIKit
import Firebase
import FirebaseDatabase
class RpiOps: UIViewController {
#IBOutlet weak var ivImage: UIImageView!
#IBOutlet weak var LblResult: UILabel!
#IBOutlet weak var tvLink: UITextView!
#IBAction func btn1Pic(_ sender: Any) {
rpi2do(state: "single_pic")
rpiResults(state: "-")
let actityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
actityIndicator.center = self.view.center
actityIndicator.hidesWhenStopped = true
actityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(actityIndicator)
actityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
actityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
let ref = Database.database().reference()
//let post : [String: AnyObject] = ["2do": state as AnyObject]
ref.child("rpi_results").observeSingleEvent(of: .value, with: { (snapshot) in
let ud = snapshot.value as! [String: Any]
let asa = ud["got"] as! String
self.tvLink.text = asa
print("asa:", asa)
//self.LblResult
let url = URL(string: asa)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
self.ivImage.image = UIImage(data: data!)
self.displayAlert(title: "Finished!", message: "Your picture has been taken. See the link here: " + asa)
//self.rpi2do(state: "-")
})
}
rpi2do(state: "-")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// rpi operation
func rpi2do(state: String) {
let ref = Database.database().reference()
let post : [String: AnyObject] = ["2do": state as AnyObject]
ref.child("rpi2do").setValue(post)
}
// rpi operation
func rpiResults(state: String) {
let ref = Database.database().reference()
let post : [String: AnyObject] = ["got": state as AnyObject]
ref.child("rpi_results").setValue(post)
}
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
self.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
According to the Firebase docs, I think you've got the right method above in observeSingleElement to answer your first question. On the second one, make sure that you are getting the results of observeSingleEvent back onto the main thread before trying updating any UI elements, like below. You may be seeing the no change in the photo because you are not on the main thread after the database call.
ref.child("rpi_results").observeSingleEvent(of: .value, with: { (snapshot) in
let ud = snapshot.value as! [String: Any]
let asa = ud["got"] as! String
DispatchQueue.main.sync {
self.tvLink.text = asa
print("asa:", asa)
//self.LblResult
let url = URL(string: asa)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
self.ivImage.image = UIImage(data: data!)
self.displayAlert(title: "Finished!", message: "Your picture has been taken. See the link here: " + asa)
//self.rpi2do(state: "-")
}
})
Related
I am trying to debug a chunk of code used to upload an image and download that image from my own server.
The image path is "http://localhost/Twitter/Avatar/52/avatar.jpeg"
as we can see, there are two images in that folder, same image but different name. I got a weird result when I hard coded the path when downloading the image
if avatarPath != nil {
let x = "http://localhost/Twitter/Avatar/52/avatar.jpeg"
let imageURL = URL(string: x)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: imageURL!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let imageData = data {
self.avatarImage.image = UIImage(data: imageData)
}
}
})
task.resume()
}
// round courner of avatar
avatarImage.layer.cornerRadius = avatarImage.bounds.width/20
avatarImage.clipsToBounds = true
//Give title to navigation controller
self.navigationItem.title = username.uppercased()
activityIndicator.stopAnimating()
}
when I write let x = "http://localhost/Twitter/Avatar/52/pogba.jpeg"
I go the same image as the path, like this
but when I change to let x = "http://localhost/Twitter/Avatar/52/avatar.jpeg"
I got different image, like this
I once used that image actually when the first time uploading an image, but I don't know why that image appears again. I have not implemented caching image yet. why this happens?
here is the full source code
import UIKit
class HomepageVC: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var avatarImage: UIImageView!
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var fullnameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var editAvatarButton: UIButton!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimating()
// mendeklarasikan variable user yang berasal dari superglobal variable di appdelegate
let username = userInfo?["username"] as! String
let fullname = userInfo?["fullname"] as! String
let email = userInfo?["email"] as! String
let avatarPath = userInfo?["avatar"] as? String
// update user interface text & Label
usernameLabel.text = username.uppercased()
fullnameLabel.text = fullname.capitalized
emailLabel.text = email
// update user interface avatar
if avatarPath != nil {
let x = "http://localhost/Twitter/Avatar/52/pogba.jpeg"
let imageURL = URL(string: x)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: imageURL!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let imageData = data {
self.avatarImage.image = UIImage(data: imageData)
}
}
})
task.resume()
}
// round courner of avatar
avatarImage.layer.cornerRadius = avatarImage.bounds.width/20
avatarImage.clipsToBounds = true
//Give title to navigation controller
self.navigationItem.title = username.uppercased()
activityIndicator.stopAnimating()
}
#IBAction func logoutButtonDidPressed(_ sender: Any) {
//menghapus data userDefault yang sudah ada
UserDefaults.standard.removeObject(forKey: "parsedJSON")
UserDefaults.standard.synchronize()
//menuju ke login page dengan modal segue
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateViewController(withIdentifier: "loginVC")
present(loginVC, animated: true, completion: nil)
}
#IBAction func editProfilePictureButtonDidPressed(_ sender: Any) {
// user akan memilih photo dari library atau dari camera nya
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
let actionSheet = UIAlertController(title: "Photo Source", message: "please choose your source", preferredStyle: .actionSheet)
// action camera
let actionCamera = UIAlertAction(title: "Camera", style: .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
} else {
self.showAlert(alertTitle: "Opppss", alertMessage: "camera can't be used / not available", actionTitle: "OK")
print("camera can't be used / not available")
}
}
// action photo library
let actionPhotoLibrary = UIAlertAction(title: "Photo Library", style: .default) { (action) in
imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}
//action cancel
let actionCancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(actionCamera)
actionSheet.addAction(actionPhotoLibrary)
actionSheet.addAction(actionCancel)
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
avatarImage.image = image
picker.dismiss(animated: true, completion: nil)
// call func of uploading file to server
uploadAvatar()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// custom HTTP request body to upload image file
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, imageDataKey: Data, boundary: String) -> Data {
var body = Data();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
// kita set agar image yang di upload kemudian berformat .jpg
let filename = "avatar.jpeg"
let mimetype = "image/jpeg"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(imageDataKey)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
return body as Data
}
// uploading image ke server
func uploadAvatar() {
// mendapatkan ID dari User Default variable
let id = userInfo!["id"] as! String
// membuat request
let url = URL(string: "http://localhost/Twitter/uploadAvatar.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// parameter yang akan dikirim di dalam request body
// parameter ini dibutuhkan karena uploadAvatar.php membutuhkan inputan ID
let param = ["id" : id]
// membuat Boundary
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// mengassign image yang akan di upload dan melakukan kompresi
let imageData = UIImageJPEGRepresentation(avatarImage.image!, 0.5)
// if not compressed, return ... do not continue to code
if imageData == nil {
return
}
// constructing http body
request.httpBody = createBodyWithParams(param, filePathKey: "file", imageDataKey: imageData!, boundary: boundary)
// filePathKey berupa 'file' agar nanti di PHP $_FILES bisa didentifikasi, contohnya $_FILES['file'][tmp_name]
// launc session
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
// maka tampilkan $returnArray dari PHP (response message from server)
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
// declare new parseJSON to store json
guard let parsedJSON = json else {
print("Error while parsing")
return
}
print(parsedJSON)
// get id from $returnArray["id"] in PHP - parseJSON["id"]
let id = parsedJSON["id"]
// successfully uploaded
if id != nil {
// save user information yang berasal dari server
UserDefaults.standard.set(parsedJSON, forKey: "parsedJSON")
userInfo = UserDefaults.standard.object(forKey: "parsedJSON") as? NSDictionary
// jika tidak ada "id" kiriman dari server, maka ada error message
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = parsedJSON["message"] as! String
self.showAlert(alertTitle: "opppps", alertMessage: message, actionTitle: "OK")
})
}
// error ketika melakukan JSON serialization
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error.localizedDescription
self.showAlert(alertTitle: "SorryBroooo", alertMessage: message, actionTitle: "OK")
})
}
// error ketika koneksi ke server
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
self.showAlert(alertTitle: "oppps", alertMessage: message, actionTitle: "OK")
})
}
})
}.resume()
}
}
// extend data
extension Data {
mutating func appendString(_ string : String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
When you use this code
let session = URLSession(configuration: .default)
you had automatically signed up for default caching policies, it uses persistent disk based cache as specified in this link:
https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411560-default
if you want to remove all the caching policies, use this code instead
let session = URLSession(configuration: .ephemeral)
This forEach loop works sometimes and sometimes it skips. I am not sure what I am doing wrong here. The loop will skip the last item and will never exit. So the completion block does not get fired at all.
I am using firebase, Eureka forms and it's ImageRow extension.
I would appreciate some help here.
//MARK: - Get Form Values
var returnedValues: [String: Any] = [:]
fileprivate func getFormValues(values: [String: Any], completion: #escaping ([String:Any])->()) {
if let name = values["name"] as? String,
let description = values["description"] as? String,
let images = values["images"] as? [UIImage],
let category = values["category"] as? String,
let price = values["price"] as? Double,
let deliveryFee = values["deliveryFee"] as? Double,
let deliveryAreas = values["deliveryArea"] as? Set<String>,
let deliveryTime = values["deliveryTime"] as? String {
guard let uid = Auth.auth().currentUser?.uid else { return }
var imagesData = [[String: Any]]()
var counter = 0
images.forEach({ (image) in
let imageName = NSUUID().uuidString
let productImageStorageRef = Storage.storage().reference().child("product_images").child(uid).child("\(imageName).jpg")
var resizedImage = UIImage()
if image.size.width > 800 {
resizedImage = image.resizeWithWidth(width: 800)!
}
if let uploadData = UIImageJPEGRepresentation(resizedImage, 0.5) {
productImageStorageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print("Failed to upload image: \(error?.localizedDescription ?? "")")
return
}
//Successfully uploaded product Image
print("Successfully uploaded product Image")
if let productImageUrl = metadata?.downloadURL()?.absoluteString {
counter += 1
let imageData: [String: Any] = [imageName: productImageUrl]
imagesData.append(imageData)
if counter == images.count {
let deliveryAreasArr = Array(deliveryAreas)
self.returnedValues = ["name": name, "description": description, "images": imagesData , "category": category, "price": price, "deliveryFee": deliveryFee, "deliveryArea": deliveryAreasArr, "deliveryTime": deliveryTime, "creationDate": Date().timeIntervalSince1970, "userId": uid]
completion(self.returnedValues)
}
}
})
}
})
} else {
let alert = UIAlertController(title: "Missing Information", message: "All fields are required. Please fill all fields.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in
alert.dismiss(animated: true, completion: nil)
}))
UIActivityIndicatorView.stopActivityIndicator(indicator: self.activityIndicator, container: self.activityIndicatorContainer, loadingView: self.activityIndicatorLoadingView)
self.present(alert, animated: true, completion: nil)
}
}
There are a number of if statements inside your for loop that can result in counter not being incremented. If any of these fail then you will never call the completion handler.
I understand that you are using the counter in an attempt to know when all of the asynchronous tasks are complete, but a dispatch group is a better solution for this.
It is also important that your completion handler is called in all paths; such as when the initial guard fails or in the else clause of the initial if - Your completion handler should probably accept an Error parameter so that it knows that there was a problem.
//MARK: - Get Form Values
fileprivate func getFormValues(values: [String: Any], completion: #escaping ([String:Any]?)->()) {
var returnedValues: [String: Any] = [:]
if let name = values["name"] as? String,
let description = values["description"] as? String,
let images = values["images"] as? [UIImage],
let category = values["category"] as? String,
let price = values["price"] as? Double,
let deliveryFee = values["deliveryFee"] as? Double,
let deliveryAreas = values["deliveryArea"] as? Set<String>,
let deliveryTime = values["deliveryTime"] as? String {
guard let uid = Auth.auth().currentUser?.uid else {
completion(nil)
return
}
var imagesData = [[String: Any]]()
let dispatchGroup = DispatchGroup() // Create a Dispatch Group
images.forEach({ (image) in
let imageName = NSUUID().uuidString
let productImageStorageRef = Storage.storage().reference().child("product_images").child(uid).child("\(imageName).jpg")
var resizedImage = UIImage()
if image.size.width > 800 {
resizedImage = image.resizeWithWidth(width: 800)!
}
if let uploadData = UIImageJPEGRepresentation(resizedImage, 0.5) {
dispatchGroup.enter() // Enter the group
productImageStorageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
guard error == nil else {
print("Failed to upload image: \(error?.localizedDescription ?? "")")
dispatchGroup.leave() // Leave the dispatch group if there was an error
return
}
//Successfully uploaded product Image
print("Successfully uploaded product Image")
if let productImageUrl = metadata?.downloadURL()?.absoluteString {
let imageData: [String: Any] = [imageName: productImageUrl]
imagesData.append(imageData)
}
dispatchGroup.leave() // Leave the dispatch group in normal circumstances
})
}
})
// Schedule a notify closure for execution when the dispatch group is empty
dispatchGroup.notify(queue: .main) {
let deliveryAreasArr = Array(deliveryAreas)
returnedValues = ["name": name, "description": description, "images": imagesData , "category": category, "price": price, "deliveryFee": deliveryFee, "deliveryArea": deliveryAreasArr, "deliveryTime": deliveryTime, "creationDate": Date().timeIntervalSince1970, "userId": uid]
completion(self.returnedValues)
}
} else {
completion(nil)
let alert = UIAlertController(title: "Missing Information", message: "All fields are required. Please fill all fields.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in
alert.dismiss(animated: true, completion: nil)
}))
UIActivityIndicatorView.stopActivityIndicator(indicator: self.activityIndicator, container: self.activityIndicatorContainer, loadingView: self.activityIndicatorLoadingView)
self.present(alert, animated: true, completion: nil)
}
}
Some other points:
It would be better to pass structs rather than dictionaries. Using a struct for your input would get rid of that massive if let at the start of your function since you would know the types of the values and by making them non-optional properties of the struct you would know that the values were present.
It is unusual for a function such as this to present an alert; it would normally just return an error via the completion or perhaps throw back to the caller to indicate that there was a problem and let the caller handle it
I don't see why imagesData needs to be an array of dictionaries. Each dictionary in the array only has one entry, so you could just use a dictionary of [String:String] (There is no need to use Any when you know what the type will be.
Up until two days ago my code was working fine with no problems, out of the blue my code begins returning nil when I know for a fact that the value is there within my Firebase node. I have not touched the code in weeks nor have made anychanges to it any time recently. I have recently upgraded my Xcode to 9 but still running Swift 3.
I have the value radiusDistanceNumber declared above my viewDidLoad() as
class viewController: UIViewController {
var radiusDistanceNumber: Int()
override func viewDidLoad()
super.viewDidLoad {
}
func radiusValue(){
let user = Auth.auth().currentUser
guard let uid = user?.uid else{
return
}
let ref = Database.database().reference().child("Users").child(uid)
ref.observeSingleEvent(of: .value, with: {snapshot in
print("this is the snapshot value \(snapshot.value)")
//returns correct value of 14
if let dictionary = snapshot.value as? [String: AnyObject] {
self.radiusDistanceNumber = dictionary["radiusDistance"] as? Int
print(self.radiusDistanceNumber)
//returns nil
if self.radiusDistanceNumber == nil {
//let the user know it may be an error in connection
let alertController = UIAlertController(
title: "Error",
message: "Data not loading properly, make sure you have a strong connection and try again", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Got it", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
} else{
// pass the value to the slider so the user can see the distance
let radiusDistanceNumberFloat = Float(self.radiusDistanceNumber!)
self.radiusSlider.value = radiusDistanceNumberFloat
self.radiusLabel.text = String(self.radiusSlider.value)
}
}
})
}
Again, this code was working weeks ago
I think you should make these changes in your code . You are currently declaring the radiusDistanceNumber incorrectly so
Replace
var radiusDistanceNumber: Int()
with
var radiusDistanceNumber = Int()
I think you should also
replace
if let dictionary = snapshot.value as? [String: AnyObject]
with
if let dictionary = snapshot.value as? [String: Any]
I have a UITableView with a list of users. When you tap on a row, the uid of the user is passed to the UIViewController detail view. A URLRequest is made to retrieve JSON data of the user (username, avatar, etc). However, the detail view inconsistently updates the information. Sometimes it'll show the users' name, avatar, etc but other times it'll show nothing or it'll only show the username or only show the avatar, etc.
In the fetchUser() method, I have a print("Username: \(self.user.username)") that shows the correct data is being retrieved 100% of the time but it won't display it 100% of the time in the view.
Any help would be greatly appreciated.
Thanks!
class ProfileViewController: UIViewController {
#IBOutlet weak var avatarImageView: UIImageView!
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var networthLabel: UILabel!
var user: User!
var uid: Int?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
fetchUser()
}
func reloadView() {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
func fetchUser() {
// Post user data to server
let myUrl = NSURL(string: "http://localhost/test/profile")
let urlRequest = NSMutableURLRequest(url: myUrl! as URL);
urlRequest.httpMethod = "POST"
let postString = "uid=\(uid!)"
urlRequest.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
self.usernameLabel.text = username
self.networthLabel.text = networth
self.avatarImageView.downloadImage(from: avatar)
circularImage(photoImageView: self.avatarImageView)
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
print("Username: \(self.user.username)")
self.reloadView()
}
} catch let error {
print(error)
}
}
task.resume()
}
Firstly, call fetch user in viewWillAppear like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchUser()
}
Then, change the code here like I did, don't use the reloadView function you had, instead, update the UI elements on the main thread at the end of the fetchUser function. I also changed it so you weren't updating the UI twice because you have 4 lines at the bottom of the if let uid = ... statement in fetchUser which updated UI elements that wasn't in the main thread which is why in my version I removed those 4 lines of code. Let me know if this worked for you.
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
} catch let error {
print(error)
}
}
task.resume()
Two suggestions:
strictly speaking, all accesses to UIView object should be on the main thread. You're dispatching to the main thread to call reloadView, but should probably also do it when you're settings the "username" and "net worth" values on the labels
are you sure that the labels are blank? Could it be an autolayout problem instead? (Try setting the background colour of the labels to yellow, to check that they're the correct size. Sometimes autolayout can squash views down to nothing if there are conflicting constraints)
I'm trying to an event application. I can add events to Firebase database and retrieve them. When I click the add button from my viewController, event's informations adding to Firebase database. But it can't show on my tableViewController until I logged out and log in again.
Here my retrieve code:
private func loadPlaces() {
let ref = FIRDatabase.database().reference()
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
let images = snapshot.value as! [String : AnyObject]
// self.places.removeAll()
for (_, value) in images {
let userToShow = events()
if let img = value["pathToImage"] as? String,
let eventDate = value["event date"] as? String,
let name = value["event name"] as? String
//let information = value["information"] as? String
{
// self.places.append(historicalPlaces(img: img , name: name2, inf:information))
userToShow.eventImages = img
userToShow.eventDates = eventDate
userToShow.eventnames = name
// userToShow.information = information
self.event.append(userToShow)
}
}
self.tableView.reloadData()
})
// ref.removeAllObservers()
}
and this is my uploadPost code:
import UIKit
import os.log
import Firebase
class UploadPost: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var eventImage: UIImageView!
#IBOutlet weak var selectImageBtn: UIButton!
#IBOutlet weak var eventName: UITextField!
#IBOutlet weak var eventInf: UITextView!
#IBOutlet weak var addBtn: UIButton!
#IBOutlet weak var eventDate: UITextField!
#IBOutlet weak var addEvent: UIBarButtonItem!
var picker = UIImagePickerController()
let datePicker = UIDatePicker()
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
createDatePicker()
}
func createDatePicker(){
//format for picker
datePicker.datePickerMode = .dateAndTime
// toolbar
let toolbar = UIToolbar()
toolbar.sizeToFit()
// bar button item
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressed))
toolbar.setItems([doneButton], animated: false)
eventDate.inputAccessoryView = toolbar
//assigning date picker to text field
eventDate.inputView = datePicker
}
func donePressed(){
//format date
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
eventDate.text = dateFormatter.string(from: datePicker.date)
self.view.endEditing(true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
self.eventImage.image = image
selectImageBtn.isHidden = true
addBtn.isHidden = false
}
self.dismiss(animated: true, completion: nil)
}
#IBAction func selectImage(_ sender: Any) {
picker.allowsEditing = true
picker.sourceType = .photoLibrary
self.present(picker, animated: true, completion: nil)
}
#IBAction func addEventPressed(_ sender: Any) {
}
#IBAction func addPressed(_ sender: Any) {
AppDelegate.instance().showActivityIndicator()
let uid = FIRAuth.auth()!.currentUser!.uid
let ref = FIRDatabase.database().reference()
let storage = FIRStorage.storage().reference(forURL: "gs://bauventdemo.appspot.com")
let key = ref.child("events").childByAutoId().key
let imageRef = storage.child("events").child(uid).child("\(key).jpg")
let data = UIImageJPEGRepresentation(self.eventImage.image!, 0.6)
let uploadTask = imageRef.put(data!, metadata: nil) { (metadata, error) in
if error != nil {
print(error!.localizedDescription)
AppDelegate.instance().dismissActivityIndicatos()
return
}
imageRef.downloadURL(completion: { (url, error) in
if let url = url {
let feed = ["userID" : uid,
"pathToImage" : url.absoluteString,
"event name" : self.eventName.text!,
"event date" : self.eventDate.text!,
"author" : FIRAuth.auth()!.currentUser!.displayName!,
"event information" : self.eventInf.text!,
"postID" : key] as [String : Any]
let postFeed = ["\(key)" : feed]
ref.child("posts").updateChildValues(postFeed)
AppDelegate.instance().dismissActivityIndicatos()
self.dismiss(animated: true, completion: nil)
}
})
}
uploadTask.resume()
}
}
My code check database only when I login so, how can I check firebase database and retrieve them when I add new Event ?
The reason its not updating the table is because you are trying to reload the table in background thread.
Instead, reload it in the main thread. Like this:
DispatchQueue.main.async {
self.tableView.reloadData()
}
Updated code
private func loadPlaces() {
let ref = FIRDatabase.database().reference()
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
let images = snapshot.value as! [String : AnyObject]
// self.places.removeAll()
for (_, value) in images {
let userToShow = events()
if let img = value["pathToImage"] as? String,
let eventDate = value["event date"] as? String,
let name = value["event name"] as? String
//let information = value["information"] as? String
{
// self.places.append(historicalPlaces(img: img , name: name2, inf:information))
userToShow.eventImages = img
userToShow.eventDates = eventDate
userToShow.eventnames = name
// userToShow.information = information
self.event.append(userToShow)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
// ref.removeAllObservers()
}
I tried add refresh button which retrieve data from database as a workaround.
#IBAction func refresh(_ sender: Any) {
loadPlaces()
}
and I put this code to prevent duplicate events.
self.event.removeAll()