Swift 4 proper way to display images from api in UITableView - ios

I am using UITableViewController to display a list of items from a web service and these items have images and I using AlamofireImage to get the data from image and display them in UITableViewCell like so:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postsCell", for: indexPath) as! PostsCell
let imgUrl = URL(string:"http://example.com/uploads/" + (self.array[indexPath.row]["cover"] as! String))
cell.delegate = self
Alamofire.request(imgUrl!).responseImage { response in
DispatchQueue.main.async {
if let image = response.result.value {
cell.message?.text = (self.array[indexPath.row]["title"] as! String)
cell.subMessage?.text = (self.array[indexPath.row]["username"] as! String)
cell.profileImage?.image = image
}
}
}
return cell
}
and here is how I am populating self.array
getPosts(username: self.username!) { result in
self.array = result
self.tableView.reloadData()
}
Here is getPosts:
func getPosts(username: String, completionHandler:#escaping (_ result:Array<Dictionary<String, Any>>) -> Void)
{
var returnedResults = Array<Dictionary<String, Any>>()
APIController().getUsersPosts(username: username)
{
(result: Array<Dictionary<String, Any>>) in
DispatchQueue.main.async {
//Return our results
returnedResults = result
completionHandler(returnedResults)
}
}
}
and here is my call to my api
func getPosts(username: String, completion: #escaping (_ result: Array<Dictionary<String, Any>>) -> Void)
{
let parameters: Parameters = [
"username": username
]
Alamofire.request(webservice + "?action=posts", method: HTTPMethod.post, parameters: parameters, encoding: URLEncoding.httpBody, headers: [:]).responseJSON { response in
if(response.error == nil)
{
if let result = response.result.value {
let jsonData = result as! Array<Dictionary<String, Any>>
completion(jsonData)
}
}
}
}
My question is, is there a better and more efficient way of displaying images from a URL?

You can use SDWebImage which maintains cache of images for your app,
You can use it in following manner:
import SDWebImage
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postsCell", for: indexPath) as! PostsCell
let imgUrl = URL(string:"http://example.com/uploads/" + (self.array[indexPath.row]["cover"] as! String))
cell.profileImage.sd_setImage(with: imgUrl, placeholderImage: UIImage(named: "placeholder.png"))
// REST OF YOUR CODE TO FILL OTHE DATA OF YOUR CELL
return cell
}
You can refer below git link for more information:
https://github.com/rs/SDWebImage

No need to write responseImage completionHandler you can do something like below:
cell.profileImage?.af_setImage(withURL: imgUrl)

Related

How do I parse a JSON with Dictionary in it?

I am trying to parse a JSON and get the parameter value of "Name". And I want to display the names of postal code areas in a label in table view cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ourCell") as! ourTableViewCell
let temp = pinCodeData!["Post Office"][indexPath.row][0].dictionaryObject as! Dictionary<String,String>
let temp2 = temp["Name"]
print(temp)
cell.ourLabel.text = temp2
return cell
}
let apiURL = "http://www.postalpincode.in/api/pincode/122001"
func ret(){
Alamofire.request(apiURL, method: .get).responseJSON { (response) in
DispatchQueue.main.async {
if response.result.isSuccess {
self.pinCodeData = JSON(response.result.value!)
self.outTableView.reloadData()
}
}
}
}

How to display JSON image on custom cell of tableview in Swift?

I'm new into coding, learning how to parse JSON image into table view
able to display the labels but not able to display the image file. How to display it? I used the code given below please check it.
import UIKit
class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
var dataArray = [[String:AnyObject]]()
#IBOutlet weak var myTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://jsonplaceholder.typicode.com/photos")! //change the url
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "GET" //set http method as POST
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String:Any]] {
self.dataArray = json as [[String : AnyObject]]
DispatchQueue.main.async {
self.myTable.reloadData()
}
print(json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! ModTableViewCell
cell.labout.text = String(describing: dataArray[indexPath.row]["id"]!)
cell.imagethum.image = UIImage(named :dataArray[indexPath.row]["thumbnailUrl"]! as! String)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "story") as? FinalViewController
var selectedindex = indexPath.row
vc?.jarray = dataArray
vc?.selectedindex1 = selectedindex
self.navigationController?.pushViewController(vc!, animated: true)
}
}
You need to download your image at first.
The basic solution is:
if let url = URL(string: "YOUR_URL") {
if let data = try? Data(contentsOf: url) {
cell.imagethum.image = UIImage(data: data)
}
}
For more advanced solution take a look on SDWebImage framework ( for example ) - it's beginner-friendly.
You need to download the image using thumbnailUrl String that you're getting from JSON response.
Replace the implementation of cellForRowAt method with the following code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! ModTableViewCell
cell.labout.text = String(describing: dataArray[indexPath.row]["id"] ?? "")
if let urlString = dataArray[indexPath.row]["thumbnailUrl"] as? String, let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { (data, urlResponse, error) in
if let data = data {
cell.imagethum.image = UIImage(data: data)
}
}.resume()
}
return cell
}
Also, don't use forced unwrapping (!) so extensively. It might result in crashing the app unnecessarily.
You can try this
We use this SDWebImage pod to load images from URL. This pod provides an async image downloader with cache support.
Example Of SDWebImage as below
let img1 = savedInspofflineData[indexPath.row].image1
if img1 == ""{
//Error
}else{
let imageUrl = URL(string: img1!)!
cell.img1.sd_setImage(with: imageUrl, placeholderImage: UIImage(named: "logo_grey"), options: .refreshCached, completed: nil)
}

error : type "UserAccView" does not conform to protocol 'UITableViewDataSource'

So I am trying to add some data returned from a function and I can only access that data from inside that function so I ended up putting the table inside the function but after I did so I received the error above.
Any ideas?
This is my code:
import Foundation
import UIKit
class UserAccView: UIViewController , UITableViewDataSource {
#IBAction func GetUserInfo(_ sender: UIButton) {
guard let url = URL(string: "https://goollyapp.azurewebsites.net/api/v0.1/Goolly/User/218910182109") else{return}
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print (response)
}
if let data = data {
let json = try? JSONSerialization.jsonObject(with: data, options: [])
guard let data_array = json as? NSArray else
{
return
}
for i in 0 ..< data_array.count
{
if let data_object = data_array[i] as? NSDictionary
{
if let Body = data_object["id"] as? String,
let InfoId = data_object["TransDate"] as? String,
let Title = data_object["Debt"] as? String,
let UserId = data_object["Crdit"] as? String,
let InfoType = data_object["Desc"] as? String
{}
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (data?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
cell.textLabel?.text = "cells"
return cell
}
}.resume()
}
}
Why you have added the dataSource methods inside your Api Call ? Write those methods outside of your GetUserInfo IBAction.
Secondly, now you want to reload the tableview. For that create IBOutlet for tableview first and when response comes from the api you can reload the tableview after filling the response in your data array.
Lastly don't use var cell = UITableViewCell() like this in cellForRowAt. It will freeze your tableview . Use it like this
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as UITableViewCell.
Hope it helps you

Could not cast value of type '__NSCFString' (0x10d33c4a0) to 'UIImage

How I represent the image in the firebase storage scored in a node called users with path name pc.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : viewTableViewCell = tableView.dequeueReusableCell(withIdentifier: "opa", for:indexPath) as! viewTableViewCell
if let post = posts[indexPath.row] {
cell.post.text = post
cell.name.text = self.loggedinuserdata!["name"] as! String
cell.handle.text = self.loggedinuserdata!["handle"] as! String
cell.pc.image = self.loggedinuserdata!["pc"] as! UIImage
}
return cell
}
Thank you in anticipation ))
First You need to write this function and pass url as parameter
func downloadImageFromUrl(imageUrl: String, completion: #escaping (_ success: UIImage) -> Void) {
let url = URL(string: imageUrl)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
completion(UIImage(data: data)!)
}
task.resume()
}
and Call it
self.downloadImageFromUrl(imageUrl: self.loggedinuserdata!["pc"] as! String , completion: { image in
cell.pc.image = image
})
Try this :
cell.pc.image = UIImage.init(named: self.loggedinuserdata!["pc"] as! String)
If image fetch from url
cell.pc.sd_setImage(with: URL(string: self.loggedinuserdata!["pc"] as! String), placeholderImage: nil)
SDWebImage Source

Json data not showing on tableView

I´m building a widget for iOS with Swift. The main app´s purpose is to connect to a URL news feed and get the latest news, while the widget only get the title to display in a tableView in the Today view.
I´ve written this method for the widget in order to get the data to populate the table, but for some reason nothing is showing. I´ve tried to debug it, but being a widget it seems to be practically imposible.
This is the cellForRowAt, where I connect to the feed and try to extract data. The funny part is, the main app uses basically the same code and it works perfectly.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let urlRequest = URLRequest(url: URL(string: "https://newsapi.org/v1/articles?source=techcrunch&sortBy=top&apiKey=c64849bc30eb484fb820b80a136c9b0a")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
if let articlesFromJson = json["articles"] as? [[String: AnyObject]] {
if !(error != nil) {
var resultArray: NSMutableArray = NSMutableArray()
for articlesFromJson in articlesFromJson {
if let title = articlesFromJson["title"] as? String{
resultArray.add(title)
}
let array:NSArray = resultArray.reverseObjectEnumerator().allObjects as NSArray
resultArray = array as! NSMutableArray
let title:String = resultArray.object(at: indexPath.row) as! String
cell.textLabel?.text = title
}
}
}
//reload on main thread to speed it up
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let error {
print(error)
}
}
task.resume()
return cell
}
If someone can help me figure out where is the mistake it would be a huge help, i´ve been stuck on this issue for days now. Thanks
You want to make your network request outside of cellForRow and then reloadData once it's complete to have the tableView reload the cells which calls cellForRow.
store the array of data outside of request so you can reference it from outside the function.
var resultArray: NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
func getData() {
let urlRequest = URLRequest(url: URL(string: "https://newsapi.org/v1/articles?source=techcrunch&sortBy=top&apiKey=c64849bc30eb484fb820b80a136c9b0a")!)
let task = URLSession.shared.dataTask(with: urlRequest) {[weak self] (data,response,error) in
guard let strongSelf = self else { return }
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
if let articlesFromJson = json["articles"] as? [[String: AnyObject]] {
if error == nil {
for articlesFromJson in articlesFromJson {
if let title = articlesFromJson["title"] as? String{
strongSelf.resultArray.add(title)
}
let array:NSArray = strongSelf.resultArray.reverseObjectEnumerator().allObjects as NSArray
strongSelf.resultArray = array as! NSMutableArray
DispatchQueue.main.async {
strongSelf.tableView.reloadData()
}
} catch let error {
print(error)
}
}
task.resume()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let title:String = resultArray.object(at: indexPath.row) as! String
cell.textLabel?.text = title
return cell
}
checks proper if TableView delegate or datasource proper connected. and check array count before load data in cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if (resultArray.count > 0){
let title:String = resultArray.object(at: indexPath.row) as! String
cell.textLabel?.text = title
}
else
{
print("Error: resultArray contain nil value ")
}
return cell
}

Resources