swift - JSON to NSManagedObject - ios

I have a JSON data which I want to map to CoreData when my tableview loaded at first launch.
I found a way to do it in cellForRowAtIndexPath, but this way I can only save the data to CoreData when the cell is displayed.
I want to do it once for all cells.
var yazarMakaleListesi: [JSON]? = []
var authorList = [AuthorList]() // My CoreData
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("YazarCell") as! YazarTableViewCell
cell.yazar = self.yazarMakaleListesi?[indexPath.row]
cell.yazar = self.yazarMakaleListesi?[indexPath.row]
let authorName = self.yazarMakaleListesi?[indexPath.row]["author_name"].string
let authorID = self.yazarMakaleListesi?[indexPath.row]["author_id"].int
let authorImage = self.yazarMakaleListesi?[indexPath.row]["author_image"].string
let newspaperID = self.yazarMakaleListesi?[indexPath.row]["newspaper_id"].string
let newspaperName = self.yazarMakaleListesi?[indexPath.row]["newspaper"].string
let newsPaperImage = self.yazarMakaleListesi?[indexPath.row]["author_image"].string
let articleEntity = NSEntityDescription.entityForName("AuthorList", inManagedObjectContext: self.context!)
let newAuthor = AuthorList(entity: articleEntity!, insertIntoManagedObjectContext: self.context!)
newAuthor.authorName = authorName!
newAuthor.authorImage = authorImage!
newAuthor.newspaperName = newspaperName!
newAuthor.newsPaperImage = newsPaperImage!
newAuthor.authorID = authorID!
var saveError: NSError?
self.context!.save(&saveError)
if let _error = saveError {
println("\(_error.localizedDescription)")
} else {
println("Author Saved!")
}
var error: NSError?
let request = NSFetchRequest(entityName: "AuthorList")
let results = self.context!.executeFetchRequest(request, error: &error) as! [AuthorList]
return cell
}
I get the JSON data here:
func loadYazar(){
if (gazeteid != nil){
let url = "http:myapi.com" + String(gazeteid)
Alamofire.request(.GET, url).responseJSON { (Request, response, json, error) -> Void in
if (json != nil){
var jsonObj = JSON(json!)
if let data = jsonObj["authors"].arrayValue as [JSON]?{
self.yazarMakaleListesi = data
self.tableView.reloadData()
}
}
}
}
}
EDIT : I get my jsonresponse here, implemented # thefredelement's recommendation.
But I get " 'JSON' does not have a member named 'valueForKey'" from line:
newFakeCoreDataObject.authorName = jsonResult.valueForKey("authorName") as! String
Alamofire.request(.GET, url).responseJSON { (Request, response, json, error) -> Void in
if (json != nil){
var jsonObj = JSON(json!)
if let jsonResults = jsonObj["authors"].arrayValue as [JSON]?{
self.yazarMakaleListesi = jsonResults
var error : NSError?
for jsonResult in jsonResults {
let newFakeCoreDataObject = FakeCoreDataObject()
newFakeCoreDataObject.authorName = jsonResult.valueForKey("authorName") as! String
self.context!.save(&error)
}
self.tableView.reloadData()
}
}

I would highly suggest taking that work out of cellForRowAtIndex path and making a separate function that will iterate through your JSON results and save them each, then load the data from core data as a custom object and put that object into an instance array, then use that array for your table data.
Edit: I wouldn't use this code exactly obviously, it's just an example of what I was trying to explain.
import UIKit
import CoreData
class FakeCoreDataObject : NSObject {
// this would really be your NSManagedObject subclass
var authorName = ""
var authorImage = NSData()
var newspaperName = ""
var newspaperImage = NSData()
var authorId = 0 as NSNumber
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var myEntries = [FakeCoreDataObject]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
fetchJson()
prepareTableData()
}
func fetchJson() {
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDel.managedObjectContext!
var error : NSError?
// Get your json reuslts like you are already
var jsonResults = [AnyObject]() // just for an example
for jsonResult in jsonResults {
let newFakeCoreDataObject = FakeCoreDataObject()
newFakeCoreDataObject.authorName = jsonResult.valueForKey("authorName") as! String
// etc
context.save(&error)
// save whatever else you want for other entities, etc, if you need track out of scope you can do that and then save after the loop
}
}
func prepareTableData() {
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDel.managedObjectContext!
var error : NSError?
let fetchTableDataRequest = NSFetchRequest(entityName: "whateverItIsCalled")
fetchTableDataRequest.returnsObjectsAsFaults = false
myEntries = context.executeFetchRequest(fetchTableDataRequest, error: &error) as! [FakeCoreDataObject]
// If you need to do this often, reload the table data here too and you can call it from notifications, etc.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myEntries.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// cell stuff you already have but just use the array
// just a tip to set set values to "" or nil if you're creating a big table so you don't get duplciate data while scrolling
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

you should parse the json you receive inside responseJSON:
let jsonObj = NSJSONSerialization.JSONObjectWithData(json, options:NSJSONReadingOptions(rawValue: 0), error: &err) as! NSDictionary
callback(jsonObj as! Dictionary<String, AnyObject>, nil)
var recs:NSArray = jsonObj.objectForKey("authors") as! NSArray
var ct:Int = 0
for item in recs{
var dict:NSDictionary = item as! NSDictionary
// use self.yazarMakaleListesi[ct] here
// process into the dict "author_name" , "author_id", etc
// increment ct
}
self.tableView.reloadData()

Related

UIImage returning nil from json request to mysql database for tableView cell image?

I am having trouble returning an image to the tableView cell.
When i change all the recipientImg parameters to string everything runs fine, but when I change the recipientImg parameters to UIImage I get (Data downloaded
fatal error: unexpectedly found nil while unwrapping an Optional value)
(lldb)
Ps i am trying to return the file path in the database as an image. The other two strings return in the tableView as strings but i cannot get the imageView to return the image.
Any Tips?
This is the JSON file on the server from the database
Also I'm using RecipientImg as [varchar] in the database
here is the viewermodel1.swift
import UIKit
class viewedMeModel1: NSObject {
//properties
var username: String?
var messageDetail: String?
var recipientImg: UIImage?
//empty constructor
override init()
{
}
//construct with parameters
init(username: String, messageDetail: String, recipientImg: UIImage) {
self.username = username
self.messageDetail = messageDetail
self.recipientImg = recipientImg
}
//prints object's current state
}
here is the viewedMeModel2.swift
import UIKit
protocol viewedMeModel2Protocol: class {
func itemsDownloaded(items: NSArray)
}
class viewedMeModel2: NSObject {
weak var delegate: viewedMeModel2Protocol!
var data = Data()
let urlPath: String = "http://" //this will be changed to the path where .php lives
func downloadItems() {
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let locations = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let location = viewedMeModel1()
//the following insures none of the JsonElement values are nil through optional binding
if let username = jsonElement["Username"] as? String,
let messageDetail = jsonElement["MessageDetail"] as? String,
let recipientImg = jsonElement["RecipientImg"] as? UIImage
{
location.username = username
location.messageDetail = messageDetail
location.recipientImg = recipientImg
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
}
here is the viewedMeController.swift
import UIKit
class ViewerViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, viewedMeModel2Protocol {
var feedItems: NSArray = NSArray()
var selectedLocation : viewedMeModel1 = viewedMeModel1()
#IBOutlet weak var viewedMe: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.viewedMe.delegate = self
self.viewedMe.dataSource = self
let ViewedMeModel2 = viewedMeModel2()
ViewedMeModel2.delegate = self
ViewedMeModel2.downloadItems()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.viewedMe.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return feedItems.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "basicCell"
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
// Get the location to be shown
let item: viewedMeModel1 = feedItems[indexPath.row] as! viewedMeModel1
// Get references to labels of cell
cell.UIImage?.image = item.recipientImg!
return cell
}
The problem is with your viewedMeModel2.swift, at this line:
let recipientImg = jsonElement["RecipientImg"] as? UIImage
the conditional unwrapped result will alway return recipientImg = nil, because jsonElement["RecipientImg"] is a string, it cannot be casted to UIImage.
You should rewrite your code to this:
if let username = jsonElement["Username"] as? String,
let messageDetail = jsonElement["MessageDetail"] as? String,
let recipientImgString = jsonElement["RecipientImg"] as? String
{
location.username = username
location.messageDetail = messageDetail
location.recipientImg = UIImage(named: recipientImgString)
}
My guess is that jsonElement["RecipientImg"] is returning a String not a UIImage, so you're getting nil because you can't cast the string as an image. Trying getting an image url from jsonElement["RecipientImg"] and then create a UIImage from that url. Something like this:
let imageURL = jsonElement["RecipientImg"] as? String
let myImage = UIImage(contentsOfFile: imageURL)
You can also use the UIImage initializer init?(data: Data) if you're getting some sort of data object.
Again, the object at jsonElement["RecipientImg"] probably can't be cast to UIImage, and my guess is that it's a String. Find out what it is and what you can cast it as, and then use the appropriate UIImage initializer from the documentation.
https://developer.apple.com/documentation/uikit/uiimage

How to display a playlist of a YouTube channel using YouTube API in Swift3

Question 1 :
I am using YouTube API to display a playlist of videos in a UITableView but it's not working. It's working fine when I make it for a single video, one video appears in the UITableView.
How can I display a playlist of any YouTube channel? I am using this code in my UITableView.
My UITableView code :
import UIKit
import AVFoundation
class YTViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, AVAudioPlayerDelegate {
#IBOutlet weak var txtSearch: UITextField!
#IBOutlet weak var searchResultTableView: UITableView!
// Set up a network session
let session = URLSession.shared
// ReST GET static String parts
let BASE_URL: String = "https://www.googleapis.com/youtube/v3/"
let SEARCH_VIDEO: String = "channels?part=snippet&q="
let VIDEO_TYPE: String = "&id=UCJIc9yX_3iHE2CfmUqoeJKQ&key="
let API_KEY: String = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#IBAction func btnSearchClicked(_ sender: UIButton) {
}
func getVideoList() {
let methodArguments: [String: AnyObject] = [
"query": txtSearch.text! as AnyObject
]
// Format the search string (video title) for http request
let videoTitle: String = escapedParameters(methodArguments)
// Make the query url
// sample: https://www.googleapis.com/youtube/v3/search?part=snippet&q=werewolf&type=video&key=AIzaSyDDqTGpVR7jxeozoOEjH6SLaRdw0YY-HPQ
let searchVideoByTitle = BASE_URL + SEARCH_VIDEO + videoTitle + VIDEO_TYPE + API_KEY
print("#####################\(searchVideoByTitle)")
if let url = URL(string: searchVideoByTitle) {
let request = URLRequest(url: url)
// Initialise the task for getting the data
initialiseTaskForGettingData(request, element: "items")
}
}
// Array to store all the desired values dictionaries
var videosArray: Array<Dictionary<String, AnyObject>> = [[String: AnyObject]]()
func initialiseTaskForGettingData(_ request: URLRequest, element: String) {
// Initialize task for getting data
// Refer to http://www.appcoda.com/youtube-api-ios-tutorial/
let task = session.dataTask(with: request, completionHandler: {(data, HTTPStatusCode, error) in
// Handler in the case of an error
if error != nil {
print(error as Any)
return
}
else {
// Parse that data received from the service
let resultDict: [String: AnyObject]!
do {
// Convert the JSON data to a dictionary
resultDict = try JSONSerialization.jsonObject(with: data! as Data, options: .allowFragments) as! [String: AnyObject]
print("***************************\(resultDict)")
// Get the first item from the returned items
if let itemsArray = (resultDict as AnyObject).value(forKey: element) as? NSArray {
// Remove all existing video data
self.videosArray.removeAll()
for index in 0..<itemsArray.count {
// Append the desiredVaules dictionary to the videos array
self.videosArray.append(self.unwrapYoutubeJson(arrayToBeUnwrapped: itemsArray, index: index))
}
// Asynchronously reload the data and display on the tableview
DispatchQueue.main.async {
// Reload the tableview
self.searchResultTableView.reloadData()
}
}
} catch let jsonError {
print(jsonError)
}
}
})
// Execute the task
task.resume()
}
func unwrapYoutubeJson(arrayToBeUnwrapped: NSArray, index: Int) -> [String: AnyObject]{
let firstItemDict = arrayToBeUnwrapped[index] as! [String: AnyObject]
// Get the snippet dictionary that contains the desired data
let snippetDict = firstItemDict["snippet"] as! [String: AnyObject]
// Dictionary to store desired video contents for display on tableview
// desired values - "Title", "Description", "Thumbnail"
var desiredValuesDict = [String: AnyObject]()
desiredValuesDict["title"] = snippetDict["title"]
desiredValuesDict["description"] = snippetDict["description"]
// Further unwrap to get the Thumbnail default URL
let thumbnailDict: [String: AnyObject]
thumbnailDict = snippetDict["thumbnails"] as! [String: AnyObject]
let defaultThumbnailDict = thumbnailDict["default"] as! [String: AnyObject]
desiredValuesDict["thumbnail"] = defaultThumbnailDict["url"]
//Get the id dictionary that contains videoId
let idDict = firstItemDict["id"] as? [String: AnyObject]
desiredValuesDict["videoId"] = idDict?["videoId"]
return desiredValuesDict
}
// Helper function: Given a dictionary of parameters, convert to a string for a url
func escapedParameters(_ parameters: [String : AnyObject]) -> String {
var urlVars = [String]()
for (key, value) in parameters {
// Make sure that it is a string value
let stringValue = "\(value)"
// Escape it
let escapedValue = stringValue.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
//Append it
urlVars += [key + "=" + "\(escapedValue!)"]
}
return (!urlVars.isEmpty ? "" : "") + urlVars.joined(separator: "&")
}
// MARK: UITableView method implementation
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SearchResultTableViewCell
let videoSelected = videosArray[indexPath.row]
cell.updateIU(video: videoSelected)
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
let id = videosArray[indexPath.row]["videoId"] as? String
print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\(id)")
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videosArray.count
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? playerViewController {
if let selectedRowIndexPath = searchResultTableView.indexPathForSelectedRow?.row {
destination.mytitle = videosArray[selectedRowIndexPath]["title"] as! String
destination.mydescript = videosArray[selectedRowIndexPath]["description"] as! String
destination.myvideoId = videosArray[selectedRowIndexPath] ["videoId"] as? String
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
getVideoList()
searchResultTableView.dataSource = self
searchResultTableView.delegate = self
}
}
Question 2 :
When I am trying to play a video using YTPlayerHelper it's not working:
fatal error: unexpectedly found nil while unwrapping an Optional value and the video ID appears as nil.
How can I play the video using the YTPlayerHelper? This is how I am playing the video:
import UIKit
import youtube_ios_player_helper
class playerViewController: UIViewController {
#IBOutlet weak var MyPlayer: YTPlayerView!
#IBOutlet weak var txtTitle: UITextView!
#IBOutlet weak var txtDescript: UITextView!
var mytitle: String!
var mydescript: String!
var myvideoId : String!
override func viewDidLoad() {
super.viewDidLoad()
print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\(myvideoId)")
MyPlayer.load(withVideoId: myvideoId!)
txtTitle.text = mytitle
txtDescript.text = mydescript
}
}
Here is my Alamofire implementation; you have to adjust the names to match yours:
func callAlamo(url : String) {
Alamofire.request(url).responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData : Data) {
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
pageToken = readableJSON["nextPageToken"] as! String
if previousPageButton.isEnabled {
previousPageToken = readableJSON["prevPageToken"] as? String
}
if previousPageToken == nil {
previousPageButton.isEnabled = false
}
if let items = readableJSON["items"] as? [JSONStandard] {
for i in 0..<items.count {
let item = items[i]
var name = String()
var previewURL1 = String()
if let id = item["id"] as? JSONStandard {
let url = id["videoId"] as! String
previewURL1 = url
}
let previewURL = previewURL1
if let snippet = item["snippet"] as? JSONStandard {
let title = snippet["title"] as! String
name = title
if let thumbnails = snippet["thumbnails"] as? JSONStandard {
if let images = thumbnails["high"] as? JSONStandard {
let mainImageURL = URL(string: images["url"] as! String)
imageURL = images["url"] as! String
let mainImageData = NSData(contentsOf: mainImageURL!)
let mainImage = UIImage(data: mainImageData! as Data)
posts.append(post.init(mainImage: mainImage, name: name, previewURL: previewURL, imageURL: imageURL))
self.tableView.reloadData()
nextPageButton.isEnabled = true
}
}
}
}
}
} catch {
print(error)
}
}
Then make a request by using callAlamo(url: yourURL), replacing yourURL with the actual URL.
For the second question, you have a great tutorial here: http://www.appcoda.com/youtube-api-ios-tutorial/
In the tutorial is another way to update UITableView with YouTube videos, but personally I prefer the Alamofire one, as it is much faster and easier to write. I recommend to view just the playing videos part.

Populate JSON data from REST API to table view in swift not working

I have got the response data in the log window but I am not able to populate on the tableView dynamically. I have tried many methods but not working
// send request to URL
let urlPath:String = "http://api.androidhive.info/contacts/"
var url:NSURL = NSURL(string: urlPath)!
var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request1.HTTPMethod = "POST"
var stringPost = "msg=123" ///key and value
let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
request1.timeoutInterval = 60
request1.HTTPBody = data
request1.HTTPShouldHandleCookies = false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler: {(response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
//print object response
println("response = \(response)")
//print response body
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("response data = \(responseString)")
The data is coming from the url. I can see it.
// Extract JSON
var err: NSError?
let json : NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &err) as! NSDictionary
if let items = json["contacts"] as? [[String:AnyObject]]
{
for item in items {
// construct your model objects here
self.contactList.append(Person(dictionary:item))
}
// dispatch_async(dispatch_get_main_queue()) {
// self.tableView.reloadData()
}
The above code line is not appending data (not working).
Table view code
//how many sections
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
//how many rows
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contactList.count
//return cellCount
}
//contents
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// var cell = UITableViewCell()
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
// cell.textLabel?.text = "aaa"
let person = contactList[indexPath.row]
cell.textLabel?.text = person.name
return cell
}
Please tell me where the problem is.
That's a good example to create a custom class
class Person { // can be also a struct
let id : String
let name : String
let email : String
let address : String
let gender : String
let phone : String
init(dictionary : [String : AnyObject]) {
id = dictionary["id"] as? String ?? ""
name = dictionary["name"] as? String ?? ""
email = dictionary["email"] as? String ?? ""
address = dictionary["address"] as? String ?? ""
gender = dictionary["gender"] as? String ?? ""
phone = dictionary["id"] as? String ?? ""
}
}
Then create contactList as
var contactList = [Person]()
and populate the list with
if let items = json["contacts"] as? [[String:AnyObject]]
{
for item in items {
// construct your model objects here
self.contactList.append(Person(dictionary:item))
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
and display the name in each cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// var cell = UITableViewCell()
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let person = contactList[indexPath.row]
cell.textLabel?.text = person.name
return cell
}
If all values of the dictionary containing the person data are of type String you can change the following lines to be still more specific
in Person
init(dictionary : [String : String]) {
id = dictionary["id"] ?? ""
...
phone = dictionary["id"] ?? ""
}
in the view controller
if let items = json["contacts"] as? [[String:String]]
Create NSObject class
public class Global: NSObject
{
let name : String!
.
.
}
Within for item in items
var object: ObjectClass = ObjectClass()
object.id = item["id"]!
.
.
self.contactList.append(object)
In cellForRowAtIndexPath
var object: ObjectClass = self.contactList [indexPath.row] as! ObjectClass;
///get the values as
cell.label.text = object.name;
Instead create model. you can create Class for Contact.
class Contact {
var id : String?
var name : String?
}
Create a Sample responses.
// Contact1
let cont1 : NSMutableDictionary = NSMutableDictionary.init(object: "7", forKey: "id");
cont1.setValue("vignesh", forKey: "name");
// Contact2
let cont2 : NSMutableDictionary = NSMutableDictionary.init(object: "8", forKey: "id");
cont2.setValue("karthi", forKey: "name");
let contactArray :NSArray = NSArray.init(array: [cont1,cont2]);
// Response Dictionary
let responseDic : NSMutableDictionary = NSMutableDictionary.init(object: contactArray, forKey: "contacts");
Parse Response value.
// Create Contact list Array.
var contactList : Array<Contact> = []
if let items = responseDic["contacts"] as? NSArray
{
for item in items {
// construct your model objects here
let id: NSString = item["id"] as! NSString
let name: NSString = item["name"] as! NSString
let contUser : Contact = Contact.init();
contUser.id = id as String;
contUser.name = name as String;
contactList.append(contUser)
}
}
List item
class ViewController: UIViewController , UITableViewDataSource , UITableViewDelegate {
#IBOutlet weak var tableViewCountry: UITableView!
var names: [String] = []
var contacts: [String] = []
var gender: [String] = []
var mob:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableViewCountry.dataSource = self
tableViewCountry.delegate = self
self.tableViewCountry.register(UINib(nibName: "ContactTableViewCell", bundle: nil), forCellReuseIdentifier: "ContactTableViewCell")
let url=URL(string:"http://api.androidhive.info/contacts/")
do {
let allContactsData = try Data(contentsOf: url!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
if let arrJSON = allContacts["contacts"] {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as! [String : AnyObject]
names.append(aObject["name"] as! String)
contacts.append(aObject["email"] as! String)
gender.append(aObject["gender"] as! String)
let phone = aObject["phone"]
mob.append(phone?["mobile"] as! String)
}
}
print(allContacts)
print(names)
print(contacts)
self.tableViewCountry.reloadData()
}
catch {
print("error")
}
}

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.

Swift - Manage tasks to populate UITableView

The view I'm developing does the following:
Sends a GET request to the API to retrieve a list of users
Sends GET requests to the API to retrieve profile images from the list of users
Display the images in TableViewCells
However, I'm having problem managing the tasks and the queues. What is the best way to be sure that all the requests and tasks are done before populating the Table View?
Here's the code:
import UIKit
class homeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var jsonData : [NSDictionary] = [NSDictionary]()
var imageUrls: NSDictionary = NSDictionary()
var urlsArray: [NSURL] = [NSURL]()
override func viewDidLoad() {
super.viewDidLoad()
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
self.refreshData()
self.getImage()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
})
}
override func viewWillAppear(animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jsonData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var type = jsonData[indexPath.row]["type"] as! Int
for typej in jsonData {
let t : Int = typej["type"] as! Int
println("type : \(t)")
}
if type == 1 {
let cell1 : cellTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! cellTableViewCell
/* //If images url are retrieved, load them. Otherwise, load the placeholders
if self.urlsArray.isEmpty == false {
println("Tiè: \(self.urlsArray[indexPath.row])")
if let data = NSData(contentsOfURL: self.urlsArray[indexPath.row]) {
cell1.profileImg?.image = UIImage(data: data)
}
} else {
cell1.profileImg?.image = UIImage(named: "placeholder.png")
}*/
let block: SDWebImageCompletionBlock! = {
(image: UIImage!, error: NSError!, cacheType: SDImageCacheType, imageURL: NSURL!) -> Void in
println(self)
}
println("url Array: \(self.urlsArray)")
let url = NSURL(string: "http://adall.ga/s/profile-1439584252497.png")
if UIApplication.sharedApplication().canOpenURL(urlsArray[indexPath.row]) {
cell1.profileImg.sd_setImageWithURL(urlsArray[indexPath.row], completed: block)
} else {
cell1.profileImg.sd_setImageWithURL(url, completed: block)
}
cell1.testLbl.text = (self.jsonData[indexPath.row]["author"] as? String)!
return cell1
} else {
let cell2 : cell2TableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell2") as! cell2TableViewCell
return cell2
}
}
func refreshData() {
let requestURL = NSURL(string:"http://adall.ga/api/feeds/author/mat/0")!
var request = NSMutableURLRequest(URL: requestURL)
request.HTTPMethod = "GET"
request.addValue(userToken, forHTTPHeaderField: "tb-token")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
data, response, error in
println(response)
var dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(dataString)
//let jsonResult : NSDictionary = (NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary)!
//jsonData = (NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers , error: nil) as? NSArray)!
self.jsonData = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil) as! [NSDictionary]
}
task.resume()
var index: Int
for index = 0; index < 10000; ++index {
print("Index: \(index), Task state: \(task.state)")
}
}
func getImage() {
var i = 0
for jsonSingleData in jsonData {
let author = jsonSingleData["author"] as! String
let requestURL2 = NSURL(string: "http://adall.ga/api/users/" + author + "/image")!
var request2 = NSMutableURLRequest(URL: requestURL2)
request2.HTTPMethod = "GET"
request2.addValue(userToken!, forHTTPHeaderField: "tb-token")
let session2 = NSURLSession.sharedSession()
let task2 = session2.dataTaskWithRequest(request2) {
data, response, error in
println("response= \(response)")
var dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(dataString)
self.imageUrls = (NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary)
if self.imageUrls["url"] != nil {
//check if exists
let imageUrl = self.imageUrls["url"] as! String
let url = NSURL(string: "http://" + imageUrl)
self.urlsArray.append(url!)
} else {
let imageUrl = "http://shackmanlab.org/wp-content/uploads/2013/07/person-placeholder.jpg"
let url = NSURL(string: imageUrl)
self.urlsArray.append(url!)
}
}
task2.resume()
self.tableView.reloadData()
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
The point of the issue is the following code:
dispatch_async(backgroundQueue, {
self.refreshData()
self.getImage()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
})
The NSURLSession working in the background thread, so your jsonData is empty when the self.getImage() and reloadData are executed.
You can call the self.getImage() after this line
self.jsonData = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil) as! [NSDictionary]
in the session.dataTaskWithRequest completed block and calls reloadData(on the dispatch_get_main_queue) in the completed block of the session2.dataTaskWithRequest.
I think this will solved your issue.

Resources