Is this a good way to display asynchronous data?
I would suggest using a DispatchGroup
rather than keeping count of the number of items in 2 arrays. This would also allow to keep track of the progress across multiple threads with each block registered with enter()
and leave()
on the group. Then once all blocks have entered and left the notify
block is called which could refresh your UI. Also using a dictionary with a place holder image in case the loading of one of the images fails. would be better than ignoring the failure case.
This may also be easier to read and reason since there is no tracking the count of 2 arrays. The intention is clearer with enter()
and leave()
var imgs = [String: UIImage]()
var dispatchGroup = DispatchGroup()
override func viewDidAppear(_ animated: Bool) {
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { [weak self] snapshot in // fetch list of objects from database
if (snapshot.value as? Bool) != nil { // User has no pets added
self?.loadScrollView() // Reload view
} else if let snap = snapshot.value as? NSDictionary { // User has pets
for value in snap {
self?.dispatchGroup.enter()
let imgRef = FIRStorage.storage().reference().child("profile_images").child(self!.pets[i]+".png")
imgRef.data(withMaxSize: 15 * 1024 * 1024) { (data, error) -> Void in
if let error = error {
print(error) // handle the error here but still call .leave() on the group to be sure the notify block is called.
imgs[value] = somePlaceHolderImage
self?.dispatchGroup.leave()
} else if let data = data, let image = UIImage(data: data) {
imgs[value] = image
self?.dispatchGroup.leave()
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
self?.loadScrollView()
})
}
}
}
})
}
How to read asynchronous data from real-time database using android Kotlin?
The top answer to this related question shows you how to make callbacks, but that doesn't really answer the question of how to use the async data, and isn't really helpful or relevant to this type of problem.
I don't see anything specifically wrong with your callback - but it silently swallows a number of possible error cases (e.g. if the user doesn't exist). The example below has some print statements that should help determine better what is happening.
A cleaner approach than the extra callback interface is to make a separate method to handle the async result. Here is a cleaned up example of how that might look - with some pseudo-code where parts of your example were missing. To help debug, you should get in the habit of using log or print statements if you don't understand what parts of the code are running, or if something doesn't look the way you expect it to.
private var currentImageUrl: String = ""
private var userId: String = ""
private lateinit var su_name: TextView
private lateinit var su_image : ImageView
private lateinit var su_layout : ConstraintLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
// get views
su_name = findViewById<TextView>(R.id.su_name)
su_image = findViewById<ImageView>(R.id.su_image)
su_layout = findViewById<ConstraintLayout>(R.id.su_layout)
su_layout.visibility = View.INVISIBLE
// get user id from intent
userId = intent.getStringExtra("su_userid").orEmpty()
// TODO: Handle what to do if userId is empty here!
if( userId.isEmpty() ) {
finish()
}
// onClick launches another activity - if the image
// hasn't loaded yet nothing happens
su_image.setOnClickListener { viewCurrentImage() }
// start the async loading right away - once it is loaded the
// su_layout view will be visible and the view data
// will be populated. It might be good to show a progress bar
// while it's loading
startLoading()
}
private fun startLoading() {
println("LOG: getting data for ${userId}")
val suRef = FirebaseDatabase.getInstance()
.getReference()
.child("Users")
.child(userId)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()) {
println("LOG: data snapshot exists")
val userData = dataSnapshot.getValue(profile_model::class.java)
showData(userData)
}
else {
println("LOG: user not found in database")
}
}
override fun onCancelled(error: DatabaseError) {
println("LOG: cancelled")
}
})
}
private fun showData(userData: profile_model?) {
su_layout.visibility = View.VISIBLE
currentImageUrl = userData?.getImageUrl() ?: ""
su_name.text = userData?.getnameOfsu() ?: "Error"
println("LOG: Got user data ${currentImageUrl}")
if( currentImageUrl.isNotEmpty() ) {
Picasso.get()
.load(currentImageUrl)
.placeholder(R.drawable.ic_baseline_image_200)
.into(su_image)
}
}
private fun viewCurrentImage() {
if( currentImageUrl.isEmpty() ) return
val imageViewer = Intent(this, suDetails::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", currentImageUrl)
startActivity(imageViewer)
}
Angular 2 - Displaying async Object data from promise
You do not need any special pipe. Angular 2 suppport optional field. You just need to add ? in your object
{{ (data | async)?.name }}
or
{{(name | async)?}}
Related Topics
Add Animations to Foreach Loop Elements (Swiftui)
In Swift, What Does It Mean for Protocol to Inherit from Class Keyword
Rxswift Merge Different Kind of Observables
Error: Use of Unresolved Identifier 'Process'
Global Function Sequence(State:Next:) and Type Inference
Swift - Drawing Text with Drawinrect:Withattributes:
Binding an Element of an Array of an Observableobject:'Subscript(_:)' Is Deprecated
Swiftui Exporting or Sharing Files
Multiplying Variables and Doubles in Swift
Get Image from Calayer or Nsview (Swift 3)
Draw a Hole in a Rectangle with Spritekit
Swift String Interpolation Displaying Optional
Swiftyjson - Call Can Throw, But It Is Marked with 'Try' and the Error Is Not Handled
Swift: How to Delete Part of Audio