iOS loading table data with nested request - ios

I am working with a UITableView which contains an image and some labels.
The text is loading from one server and the image is downloaded from another server. The image URL is dependent on the text value response but I have to show them in one cell. What I have to do is combine those data after they have loaded and to then to show them.
What could be the right approach?

You can combine response of two requests using DispatchGroup:
let group = DispatchGroup()
var text: String?
var image: UIImage?
group.enter()
requestText(completion: { response in
text = // extract text from response
group.leave()
})
group.enter()
requestImage(completion: { response in
image = // extract image from response
group.leave()
})
group.notify(queue: DispatchQueue.main, execute: {
let textWithImage = (text, image)
// show data in table view
})

You can simply display the textual data first than as soon as a image is downloaded you can than map that image to the textual data by having some common id in both the responses and reload that particular cell . In this way the user will be able to see the textual data and after some milliseconds the images will also show up nicely.

Related

How can I get selected text from ImageAnalysisInteraction on UIImageView?

I work on an iOS app that displays images that often contain text, and I'm adding support for ImageAnalysisInteraction as described in this WWDC 2022 session. I have gotten as far as making the interaction show up and being able to select text and get the system selection menu, and even add my own action to the menu via the buildMenuWithBuilder API. But what I really want to do with my custom action is get the selected text and do a custom lookup-like thing to check the text against other content in my app.
So how do I get the selected text from an ImageAnalysisInteraction on a UIImageView? The docs show methods to check if there is selected text, but I want to know what the text is.
I was trying to solve the same problem. However, there doesn't currently seem to be any straightforward way to get selected text from ImageAnalysisInteraction. The closest thing seems to be the ImageAnalysis.transcript property, but it contains all the OCR text, not just what the user selected.
My solution was to capture the text whenever the user taps on the copy button on the selection menu. You can do this by observing clipboard changes, which allows you to copy the selected text from the clipboard whenever a change is detected.
See:
Get notified on clipboard change in swift
How to copy text to clipboard/pasteboard with Swift
Hope this help you
// Step -1
import Vision
// Step -2
// converting image into CGImage
guard let cgImage = imageWithText.image?.cgImage else {return}
// Step -3
// creating request with cgImage
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
// Step -4
let request = VNRecognizeTextRequest { request, error in
guard let observations = request.results as [VNRecognizedTextObservation],
error == nil else {return}
let text = observations.compactMap({
$0.topCandidates(1).first?.string
}).joined(separator: ", ")
print(text) // text we get from image
}
// step -5
request.recognitionLevel = VNRequestTextRecognitionLevel
try handler.perform([request])
For Reference and more details

Downloading Images using AlamoFireImages

I am trying to download my images to my table view on the home page for a project. I have retrieved the URLs from the API and they are valid (Printed in terminal)
I am then dumping these URLs into an array let imageURL = [String]()
I looped through the urls and requested the images through the AlamofireImage, but they are not get displayed
for url in self.imagesURL {
ItemImages.removeAll()
Alamofire.request(url).responseImage(completionHandler: { (response) in
guard let image = response.result.value else {return}
self.ItemImages.append(image)
completed(true)
})
}
next I am looking to convert these urls into images to be displayed..If someone could help me on where to move forward next that would be great.Or if I am going wrong anywhere...

Adding image from Firebase to UITableViewCell

I want to retrieve the image that is stored in the storage of an user and place it next to his name in a custom UITableViewCell. The problem now is that the tableview will load when the images aren't done downloading (I think?), causing the application to crash because the image array is nil. So what is the correct way to load the tableview? I think, for the user experience, it is important that the tableviewcell image should be shown even if the images aren't done downloading, and present them a default image that is saved in the assists. I thought about making an array with UIImages that links to the default asset of loading an image and changing the image to the profile picture when it is done downloading. But I really have no clue how to do that. This is what I got so far about downloading the image:
let storage = FIRStorage.storage()
let storageRef = storage.reference(forURL: "link.appspot.com")
channelRef?.observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict{
let UIDs = each.value["userID"] as? String
if let allUIDS = UIDs{
let profilePicRef = storageRef.child((allUIDS)+"/profile_picture.png")
profilePicRef.data(withMaxSize: 1 * 500 * 500) { data, error in
if let error = error {
}
if (data != nil)
{
self.playerImages.append(UIImage (data: data!)!)
}
}
}
let userNames = each.value["username"] as? String
if let users = userNames{
self.players.append(users)
}
}
}
self.tableView.reloadData()
})
This is in the cellForRow
cell.playersImage.image = playerImages[indexPath.row] as UIImage
My rules, haven't changed it from the default rules:
service firebase.storage {
match /b/omega-towers-f5beb.appspot.com/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
Thank you.
Regarding user experience, you are correct. It is standard to have some sort of default image when loading an image from a URL. A great library to use for image caching and using default assets in its' place is AlamofireImage
Vandan Patel's answer is correct in saying you need to ensure your array is not nil when loading the tableview. You will be given a completion block to handle any extra work you would like to do with your image, using the AlamofireImage library.
This is all assuming you are getting a correct image URL back for your Firebase users.
You should call tableView.reloadData() when the images are done downloading. One important thing, initialize your playerImages as playerImages = [UIImage]() instead of playerImages: [UIImage]!. if it's empty, it wouldn't show your array is nil.
Update:
if let players = playerImages {
//code
}

Accessing URL and Array from within a Block of JSON Data

Let's say I have JSON data structured in the following way:
{ "fruits" : {
"apple": {
"name": "Gala"
"color": "red",
"picture": "//juliandance.org/wp-content/uploads/2016/01/RedApple.jpg",
"noOfFruit": [1, 2]
}
}
How would I access the picture and noOfFruit array using the iOS version of Firebase? I want to make a table view with a cell that lists the apple's name, the color, a picture of the apple, and then lists the number of fruit. I get how to obtain the "color" and "name" values but how would I access the array and turn it into a string and the image so that it shows the image in the table view? Any help is appreciated!
For the array, it's really simple. Wherever you have your function that listenes for the firebase changes, I'll imagine that you have the info under the apple key stored in a variable like let apple
Then, you could cast the value of noOfFruit to an array, like the following:
let apple = // what you had before
guard let noOfFruit = apple["noOfFruit"] as? [Int] else {
return
}
//Here you have the array of ints called noOfFruit
For the image, theres several options out there. The first (and bad one) is to synchrounsly fetch the data of the url and set it to an image view as the following:
let url = URL(string: picture)
let data = try? Data(contentsOf: url!) //this may break due to force unwrapping, make sure it exists
imageView.image = UIImage(data: data!)
The thing with this approach is that it's NOT OK. It will block the main thread while its making the request and dowloading the image, leaving the app unresponsive.
The better approach would be to go fetch it asynchronously.There are several libraries that really help, such as AlamofireImage, but it can be done with barebones Foundation really easy. To do that, you should use URLSession class as the following:
guard let url = URL(string: picture) else {
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print(error)
return
}
//Remember to do UI updates in the main thread
DispatchQueue.main.async {
self.myImageView.image = UIImage(data: data!)
}
}.resume()

Downloaded picture takes a very long time to be shown on UIImageView

I have two UIImageView objects on my view stored inside an array called imgViews and I try to download images for them asynchronously with this code:
func showPic(positionIndex: Int){
let urlStr = "https://www.friendesque.com/arranged/userpics/amir/1"
let url = NSURL(string: urlStr)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error == nil {
self.imgViews[positionIndex].image = UIImage(data: data)
//self.imgViews[positionIndex].image = UIImage(named: "11665489_10154101221228009_2542754962143804380_n.jpg")
print("Loading Done...")
}
else{
print(error)
}
})
task.resume()
}
and inside my viewDidLoad(), I have
showPic(0)
When I run the code, I see "Loading Done..." immediately which means the picture has been loaded but it takes a very long time (about 1 min) for the UIImageView to actually change to the loaded picture. It's a very small picture (~15K) and it can't be a processing time problem.
I tried loading a resource image (the comment part of the code) instead of the downloaded picture but it's still slow.
I'm really confused. Why is swift so slow at working with images inside a block?
Perhaps when the data task returns it is on a background thread? You will need to switch to the main thread to change a UIImageView. Regardless I would use the UIImageView+AFNetworking category to achieve this. It's simple, well tested and lets you provide a placeholder image to display while it is downloading.
https://github.com/AFNetworking/AFNetworking/blob/master/UIKit%2BAFNetworking/UIImageView%2BAFNetworking.h
to use:
myImageView.setImageWithURL(url!)

Resources