Update TableView from Asynchronous Data in Swift - ios

I'm currently working on an iOS Application and I recently came across the issue that I was downloading and parsing data synchronously and as a result making the app completely unresponsive.
I've updated my code to download my data and parse it asynchronously however I'm now hitting another issue.
I've been trying to split my code up and follow the MVC pattern. I have my TableViewController as the delegate and DataSource of the TableView. I have another class that is managing downloading+parsing my data.
My issue is, once the data is parsed - how do I update the TableView since it's all being done asynchronously.
TableViewController
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
println(searchBar.text)
searchBar.resignFirstResponder() //Hide the keyboard
let result = ShowsDataParser.newSearchShows(searchBar.text) //Get search result based on input
searchResults = result.results
maxPages = result.maxPages
currentPage = 1
self.tableView.reloadData()
}
I've implemented all required methods(e.g cellForRowAtIndexPath) within this class too.
ShowsDataParser
class func newSearchShows(input: String, page:Int = 1) -> (results:[Show], maxPages: Int)
{
let searchUrl = "/search/tv" //Search URL
var searchResults = [Show]()
var totalPages = 1
let posterSize = "w92"
if let escapedSearch = escapeString(input) {
var search = apiLocation + searchUrl + "?query=" + escapedSearch + "&api_key=" + APIKey + "&page=\(page)"
if let url = NSURL(string: search)
{
let task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler:{data,response,error -> Void in
if let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary
{
var name: String
var id: Int
var status: String
var overview = "meow"
if let results = json["results"] as? NSArray
{
for result in results
{
id = result["id"] as! Int
name = result["original_name"] as! String
//overview = result["overview"] as! String
status = ""
var newShow = Show(showId: id, showName: name, showDesc: overview, showStatus: status)
if let date = result["first_air_date"] as? String
{
newShow.firstAired = self.formatDate(date)
}
if let poster = result["poster_path"] as? String
{
if let baseUrl = AppDelegate.config?.baseUrl
{
if let data = NSData(contentsOfURL: NSURL(string: baseUrl + posterSize + poster)!)
{
//newShow.posterImage = UIImage(data: data)
}
}
}
searchResults.append(newShow)
}
}
if let pages = json["total_pages"] as? Int
{
totalPages = pages
}
}
})
}
}
return (results: searchResults, maxPages: totalPages)
}
As you can see, the above class gets and parses the data from the specified url. I'm simply unsure of how to update the tableview after this is done. Calling upon self.tableView.reloadData() within searchBarSearchButtonClicked isn't working.

place the reloadData() call after the if statement
if let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary
{
.... more code
if let pages = json["total_pages"] as? Int
{
totalPages = pages
}
}
self.myTableView.reloadData()

Related

Swift - Load/save from CoreData generates duplicate entries

I have run into a problem where I can save and load into and from CoreData in Swift for my iOS app, but I run into a problem where I have tried to guard for duplicate entries, but it does not seem to work. can anyone tell me where I went wrong? Thanks!
My ViewController class:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var headerLabel:UILabel!
#IBOutlet weak var myTableView: UITableView!
var lenders = [LenderData]()
var lendersTemp = [LenderData]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.rowHeight = 90
myTableView.delegate = self
myTableView.dataSource = self
let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
do {
let lenders = try PersistenceService.context.fetch(fetchRequest)
self.lenders = lenders
} catch {
// Who cares....
}
downloadJSON {
for tempLender in self.lendersTemp {
if !self.lenders.contains(where: {$0.id == tempLender.id}) {
self.lenders.append(tempLender)
}
}
self.lendersTemp.removeAll()
PersistenceService.saveContext()
self.myTableView.reloadData()
}
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("JSON not downloaded")
} else {
if let content = data {
do {
let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
var imageID:Int64 = -1
var country:String = "N/A"
var latLongPair:String = "0.000000 0.000000"
var town:String = "N/A"
if let loans = myJSONData["loans"] as? NSArray {
for i in 0...loans.count-1 {
if let lender = loans[i] as? NSDictionary {
if let imageData = lender["image"] as? NSDictionary { imageID = imageData["id"] as! Int64 }
if let countryData = lender["location"] as? NSDictionary {
country = countryData["country"] as! String
town = countryData["town"] as! String
if let geo = countryData["geo"] as? NSDictionary {
latLongPair = geo["pairs"] as! String
}
}
let newLender = LenderData(context: PersistenceService.context)
newLender.id = lender["id"] as! Int64
newLender.name = lender["name"] as? String
newLender.image_id = imageID
newLender.activity = lender["activity"] as? String
newLender.use = lender["use"] as? String
newLender.loan_amount = lender["loan_amount"] as! Int32
newLender.funded_amount = lender["funded_amount"] as! Int32
newLender.country = country
newLender.town = town
newLender.geo_pairs = latLongPair
self.lendersTemp.append(newLender)
}
}
}
DispatchQueue.main.async {
completed()
}
} catch {
print("Error occured \(error)")
}
}
}
}
task.resume()
}
}
EDIT
Added the part of the code where I populate the lendersTemp array
I quote matt on this one from the comments:
So... You are appending to self.lendersTemp on a background thread but reading it on the main thread. Instead, get rid of it and just pass the data right thru the completed function.
Which is exactly what I did. And this worked

Trouble with JSON parsing in swift 1.2

Trouble with JSON parsing in swift 1.2
I have an issue with the JSON parsing, i am not getting the correct data response for my parameters. i have been stuck on the issue for quite some time now. i don’t know how to fix it. i am new to iOS development. also i haven’t used any third party library.[Don’t know how to use them]
I am getting the right response for my very first iteration but for the next iteration results of previous iteration is added along with the new results. Like that each loop result is ended up with the results of previous iteration
My JSON SAMPLE.[can provide link, please let me know]
struct AssetItems {
var Name:String
var Desc:String
var Image:String
var entityId:String
var aFileType:String
var aFileSize:String
var aFileCreatedDate:String
var aFileRevisionDate:String
var aFileModifiedDate:String
var aProductName:String
var aVersion:String
var aAssetType:String
var aIsFolder:String
init(assetName:String,assetDescription:String,assetImage:String,entityId:String,FileType:String,FileSize:String,FileCretedDate:String,FileReveisionDate:String,FileModifiedDate:String,ProductName:String,Version:String,AssetType:String,IsFolder:String) {
self.Name = assetName
self.Desc = assetDescription
self.Image = assetImage
self.entityId = entityId
self.aFileType = FileType
self.aFileSize = FileSize
self.aFileCreatedDate = FileCretedDate
self.aFileRevisionDate = FileReveisionDate
self.aFileModifiedDate = FileModifiedDate
self.aProductName = ProductName
self.aVersion = Version
self.aAssetType = AssetType
self.aIsFolder = IsFolder
}
}
struct AssetModel {
var aName:String
var aDesc:String
var aCount:String
var aEntityId:String
var items:[AssetItems]
init(rackName:String,rackDescription:String,totalNoAssets:String,EntityId:String,rackItems:[AssetItems]) {
self.aName = rackName
self.aDesc = rackDescription
self.aCount = totalNoAssets
self.aEntityId = EntityId
self.items = rackItems
}
}
var assetData:Array<AssetModel> = Array<AssetModel>()
var assetContents:Array<AssetItems> = Array<AssetItems>()
THIS IS HOW I AM PARSING JSON
func get_data_from_Url(url:String) {
let url = NSURL(string: url)
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content_Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var responseError : NSError?
var response : NSURLResponse?
var urlData: NSData? = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &responseError)
if(urlData != nil) {
var responseData:NSString = NSString(data: urlData!, encoding: NSUTF8StringEncoding)!
let res = response as! NSHTTPURLResponse
var err:NSError?
if (res.statusCode == 200)
{
var parseError: NSError?
let json:AnyObject = NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers , error: &err) as AnyObject!
if (parseError == nil) // no error while parsing
{
if let Asset_list = json as? NSArray
{
for (var i = 0; i < Asset_list.count ; i++ )
{
if let Asset_obj = Asset_list[i] as? NSDictionary
{
if let AssetGroupName = Asset_obj["Name"] as? String
{
if let AssetGroupDescription = Asset_obj["Description"] as? String
{
if let entityId = Asset_obj["EntityId"] as? String
{
if let totalAssets = Asset_obj["_NoOfAssets"] as? String
{
if let items = Asset_obj["Items"] as? NSArray
{
for (var j=0 ; j < items.count; j++)
{
if let asset_items = items[j] as? NSDictionary
{
if let AbsolutePath = asset_items["AbsolutePath"] as? String
{
if let Description = asset_items["_Description"] as? String
{
if let Name = asset_items["_Name"] as? String {
if let entityId = asset_items["EntityId"] as? String
{
if let FileType = asset_items["_FileType"] as? String
{
if let FileSize = asset_items["_FileSize"] as? String
{
if let FileCreatedDate = asset_items["CreatedDate"] as? String
{
if let FileModifiedDate = asset_items["ModifiedDate"] as? String
{
if let RevisionDate = asset_items["RevisionDate"] as? String
{
if let productName = asset_items["ProductName"] as? String
{
if let version = asset_items["Version"] as? String
{
if let AssetType = asset_items["_AssetType"] as? String
{
if let isFolder = asset_items["IsFolder"] as? String
{
var add = AssetItems(assetName: Name, assetDescription: Description, assetImage: AbsolutePath, entityId: entityId, FileType: FileType, FileSize: FileSize, FileCretedDate: FileCreatedDate, FileReveisionDate: RevisionDate, FileModifiedDate: FileModifiedDate, ProductName: productName, Version: version, AssetType: AssetType, IsFolder: isFolder)
assetContents.append(add)
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
var add_it = AssetModel(rackName: AssetGroupName, rackDescription: AssetGroupDescription, totalNoAssets: totalAssets, EntityId: entityId, rackItems: assetContents)
assetData.append(add_it)
}
}
}
}
}
}
}
}
}
}
}
do_table_refresh()
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(),
{
self.tableView.reloadData()
})
return
}
My Output
Please some one help me fix this, Any suggestion will also do.
THANKS
Special thanks to #SMi, With his GuidLines i was able to solve my issue.
I knew that i had to clear the inner array at the end of the loop[But,didn't knew how to clear that.]
var add_it = AssetModel(rackName: AssetGroupName, rackDescription: AssetGroupDescription, totalNoAssets: totalAssets, EntityId: entityId, rackItems: assetContents)
assetData.append(add_it)
assetContents.removeAll()
All i had to do is use the .removeAll() method to clear the assetContents Array.

Populating UICollectionView from Online Database

I'm creating a simple chat app, it has a loading screen with a segue to either the login screen if the user is not logged in or directly to his chats if he is. The chats are displayed in a UICollectionView. When I was first testing, I populated it with dummy data which I declared in the class itself, and everything worked fine. Now I am fetching the user's chats from an online database in the Loading Screen, and storing them in an array called user_chats which is declared globally.
I use the following code to populate the UICollectionView :
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// getUserChats()
return user_chats.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("chat_cell" , forIndexPath: indexPath) as! SingleChat
cell.chatName?.text = user_chats[indexPath.row].chat_partner!.name
cell.chatTextPreview?.text = user_chats[indexPath.row].chat_messages!.last!.text
let profile_pic_URL = NSURL(string : user_chats[indexPath.row].chat_partner!.profile_pic!)
downloadImage(profile_pic_URL!, imageView: cell.chatProfilePic)
cell.chatProfilePic.layer.cornerRadius = 26.5
cell.chatProfilePic.layer.masksToBounds = true
let dividerLineView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.5, alpha: 0.5)
return view
}()
dividerLineView.translatesAutoresizingMaskIntoConstraints = false
cell.addSubview(dividerLineView)
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-1-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
cell.addSubview(dividerLineView)
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[v0(1)]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showChat", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showChat") {
let IndexPaths = self.collectionView!.indexPathsForSelectedItems()!
let IndexPath = IndexPaths[0] as NSIndexPath
let vc = segue.destinationViewController as! SingleChatFull
vc.title = user_chats[IndexPath.row].chat_partner!.name
}
}
DATA FETCH :
func getUserChats() {
let scriptUrl = "*****"
let userID = self.defaults.stringForKey("userId")
let params = "user_id=" + userID!
let myUrl = NSURL(string: scriptUrl);
let request: NSMutableURLRequest = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let data = params.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 10
request.HTTPBody=data
request.HTTPShouldHandleCookies=false
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if (data != nil) {
do {
var dataString = String(data: data!, encoding: NSUTF8StringEncoding)
var delimiter = "]"
var token = dataString!.componentsSeparatedByString(delimiter)
dataString = token[0] + "]"
print(dataString)
let data_fixed = dataString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
// LOOP THROUGH JSON ARRAY AND FETCH VALUES
for anItem in jsonArray as! [Dictionary<String, AnyObject>] {
let curr_chat = Chat()
if let chatId = anItem["chatId"] as? String {
curr_chat.id = chatId
}
let friend = Friend()
let user1id = anItem["user1_id"] as! String
let user2id = anItem["user2_id"] as! String
if (user1id == userID) {
if let user2id = anItem["user2_id"] as? String {
friend.id = user2id
}
if let user2name = anItem["user2_name"] as? String {
friend.name = user2name
}
if let user2profilepic = anItem["user2_profile_pic"] as? String {
friend.profile_pic = user2profilepic
}
}
else if (user2id == userID){
if let user1id = anItem["user1_id"] as? String {
friend.id = user1id
}
if let user1name = anItem["user1_name"] as? String {
friend.name = user1name
}
if let user1profilepic = anItem["user1_profile_pic"] as? String {
friend.profile_pic = user1profilepic
}
}
curr_chat.chat_partner = friend
var chat_messages = [Message]()
if let dataArray = anItem["message"] as? [String : AnyObject] {
for (_, messageDictionary) in dataArray {
if let onemessage = messageDictionary as? [String : AnyObject] { let curr_message = Message()
if let messageid = onemessage["message_id"] as? String {
curr_message.id = messageid
}
if let messagedate = onemessage["timestamp"] as? String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateFormatter.dateFromString(messagedate)
curr_message.date = date
}
if let messagesender = onemessage["sender"] as? String {
curr_message.sender = messagesender
}
if let messagetext = onemessage["text"] as? String {
curr_message.text = messagetext
}
chat_messages.append(curr_message)
}}
}
curr_chat.chat_messages = chat_messages
user_chats.append(curr_chat)
}
}
catch {
print("Error: \(error)")
}
}
// NSUserDefaults.standardUserDefaults().setObject(user_chats, forKey: "userChats")
}
else {
dispatch_async(dispatch_get_main_queue(), {
let uiAlert = UIAlertController(title: "No Internet Connection", message: "Please check your internet connection.", preferredStyle: UIAlertControllerStyle.Alert)
uiAlert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { action in
self.dismissViewControllerAnimated(true, completion:nil)
}))
self.presentViewController(uiAlert, animated: true, completion: nil)
})
}
} catch _ {
NSLog("error")
}
})
}
The problem is that the collection view is always empty now. I have done some debugging and put a breakpoint inside the first function, and I saw that this method is called when the Loading Screen is still displayed to the user and the chat screen hasn't even been loaded. My suspicion is that this is called before the data is fetched from the internet in the Loading Screen, and as a result the size of the user_chats array is 0. I am used to working with Android and ListView where the ListView are never populated until the parent view is displayed on screen, hence why I am confused. The method which fetches the data from the online database works fine as I have already extensively debugged it, so the problem isn't there.
The best option is to add a completionHandler to your function to be notified when the data is return and/or when the async function is finished executing. The code below is a truncated version of your getUserCharts function with a completionHandler, which returns a true or false when the data is load (You could modify this to return anything you wish). You can read more about closures/ completion handlers https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html or google.
function
func getUserChats(completionHandler: (loaded: Bool, dataNil: Bool) -> ()) -> (){
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if (data != nil) {
do {
var dataString = String(data: data!, encoding: NSUTF8StringEncoding)
var delimiter = "]"
var token = dataString!.componentsSeparatedByString(delimiter)
dataString = token[0] + "]"
print(dataString)
let data_fixed = dataString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
// LOOP THROUGH JSON ARRAY AND FETCH VALUES
completionHandler(loaded: true, dataNil: false)
}
catch {
print("Error: \(error)")
}
}
}
else {
//Handle error or whatever you wish
completionHandler(loaded: true, dataNil: true)
}
} catch _ {
NSLog("error")
}
How to use it
override func viewDidLoad() {
getUserChats(){
status in
if status.loaded == true && status.dataNil == false{
self.collectionView?.reloadData()
}
}
}
It sounds like this is an async issue. I'm not sure how your project is setup but you need to call reloadData() on your collection view when the response is returned.
After you have received the data back from the server, and updated the data source for the collection view you need to refresh the collection view (Make sure you are on the main thread, since it is modifying the UI):
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
Edit:
Also, I'm not completely sure how you have your project setup, but you could create a delegate for your data fetch, so every time you get something back from the server it calls a delegate method that there are new messages. Your collection view controller would subscribe to that delegate, and every time the that method is called it would reload your collection view.
The Delegate:
protocol ChatsDelegate {
func didUpdateChats(chatsArray: NSArray)
}
In your Data Fetch:
user_chats.append(cur_chat)
self.delegate.didUpdateChats(user_chats)
In your collectionView controller:
class viewController: ChatsDelegate, ... {
...
func didUpdateChats(chatsArray: NSArray) {
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
}

How to populate my tableview with a mutablearray from json

So I'm fetching data from a url which is in a json format. I'm trying to display the data in my tableview but, even though it feels simple, I can't figure out how to do it.
class CompanyModel {
func getJSON() {
let companyArray: NSMutableArray = NSMutableArray()
let requestURL: NSURL = NSURL(string: "http://localhost/Companies/JSON.php")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
print("Everyone is fine, file downloaded successfully.")
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let companies = json["companies"] as? [[String: AnyObject]] {
for company in companies {
if let name = company["name"] as? String,
let phoneNumber = company["phone_number"] as? String,
let website = company["website"] as? String,
let email = company["email"] as? String,
let address = company["address"] as? String
{
let company = CompanyModel()
company.name = name
company.phoneNumber = phoneNumber
company.website = website
company.email = email
company.address = address
}
companyArray.addObject(company)
print(companyArray)
}
}
} catch {
print("Error with Json: \(error)")
}
}
print(companyArray) <- array is populated
}
print(companyArray) <- array is empty
task.resume()
}
}
I know i've done it before....I'm guessing in viewDidLoad() I'd call CompanyModel().getJSON() which would fetch the data, then store it in an empty array but my mind feels blank on how to do it.
I can't declare a variable of NSarray and store the data of it the variable directly for me to then populate the tableview. Nevertheless, I hope this explains what I'm trying to acheive.
Well first change the function to return your company array :
func getJSON() -> NSMutableArray {
}
By the end of the for loop return the company array
for company in companies {
}
After your array is populated, return the array inside this block:
dispatch_async(dispatch_get_main_queue(), {
return companyArray
})
And after task.resume() return the array:
return companyArray
From anywhere you wanna call this class and get the array :
Get a reference of the class
Let companyModal = CompanyModel()
And in anywhere you have your table view and the class let's say in viewDidLoad, you should first have NSMutableArray.
var arraySource = NSMutableArray()
And in viewDidLoad :
arraySource = companyModal.getJSON()
And to show the data in tableView do :
Mytableview.reloadData()
You can't use return within the closure of an asynchronous network request, you have to use a callback instead.
You need a NSMutableArray from the request, so first, let's make a callback for this:
completion: (array: NSMutableArray)->()
We add this callback to the method signature:
func getJSON(completion: (array: NSMutableArray)->())
And then at the location where the array will be available, we place this completion handler:
class CompanyModel {
func getJSON(completion: (array: NSMutableArray)->()) {
let companyArray: NSMutableArray = NSMutableArray()
let requestURL: NSURL = NSURL(string: "http://localhost/Companies/JSON.php")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let companies = json["companies"] as? [[String: AnyObject]] {
for company in companies {
if let name = company["name"] as? String,
let phoneNumber = company["phone_number"] as? String,
let website = company["website"] as? String,
let email = company["email"] as? String,
let address = company["address"] as? String {
let company = CompanyModel()
company.name = name
company.phoneNumber = phoneNumber
company.website = website
company.email = email
company.address = address
companyArray.addObject(company)
}
}
// CALLBACK HERE
completion(array: companyArray)
}
} catch {
print("Error with Json: \(error)")
}
}
}
task.resume()
}
}
Now to get the array from the network we use a trailing closure like this:
getJSON { (array) in
print(array)
}

Image url shows error while calling to display image in UITableview

I have stored the json image url in a array but while calling the url it shows error
Model class
import Foundation
class ProductDetails {
var productAuthor: String!
var productPrice: Int!
var artImages: [ArtImage]!
}
class ArtImage {
var imagepath: String!
var imgvideotype: Int!
}
TableView Controller Storage variable
var globalArr = [ProductDetails]()
Parsing function
func parseJSONData(data: NSData) -> [ProductDetails] {
var product_Detail = [ProductDetails]()
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
let jsonProductDetails = jsonResult?["data"] as! [AnyObject]
//print("the json response is",jsonProductDetails)
for jsonproductDetail in jsonProductDetails{
let productDetail = ProductDetails()
// let jsonProductImageDetails = jsonProductDetails["images"] as! [AnyObject]
productDetail.productAuthor = jsonproductDetail["first_name"]as! String
productDetail.productPrice = jsonproductDetail["prodprice"]as! Int
// Getting inside the json
let jsonProductImageDetails = jsonproductDetail["images"] as! [AnyObject]
var artImagesModelArray = [ArtImage]()
for image in jsonProductImageDetails {
let artImage = ArtImage();
artImage.imagepath = image["imagepath"] as! String
artImage.imgvideotype = image["imgvideotype"] as! Int
artImagesModelArray.append(artImage)
}
productDetail.artImages = artImagesModelArray;
product_Detail.append(productDetail)
}
}
catch {
print (error)
}
return product_Detail
}
Tableview DataSource
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! UserHomeScreenTableViewCell
// Configure the cell...
cell.artsAuthorName.text = globalArr[indexPath.row].productAuthor
cell.priceLabel.text = "\(globalArr[indexPath.row].productPrice)"
let productDetailsObject = globalArr[indexPath.row].artImages
print("#################",productDetailsObject)
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
if let url = (NSURL(string: self.globalArr[indexPath.row])) {
//The error comes here....in self.global array
if let data = NSData(contentsOfURL: url) {
if let image = UIImage(data: data) {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
cell.artImageView.image = image
}
}
}
}
})
return cell
}
Here i have stored the parse json and display some details in tableview cell upto here everything works fine .
but call async way to load images from array its shows error
any suggestion ..plz..
Thank you
You need to change the way how you're taking the image url from the ProductDetails.
I guess you need to use something like this:
if let url = NSURL(string: self.globalArr[indexPath.row].artImages[imageIndex].imagepath) { // your code }
Because when you perform
if let url = (NSURL(string: self.globalArr[indexPath.row]))
you get an object of ProductDetails, but not an image url.

Resources