Swift PHP post request set global variable from task - ios

I successfully retrieve data from database by using post request. (I don't want to use get request cuz I want to send a verification to php.) Don't worry about the php part, it should be fine.
import UIKit
class mainPage: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var toolBar: UIToolbar!
var values:NSArray = []
#IBOutlet weak var Open: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
Open.target = self.revealViewController()
Open.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
get()
}
func get(){
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.percyteng.com/orbit/getAllpostsTest.php")!)
request.HTTPMethod = "POST"
let postString = "user=\("ios")"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let array = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
self.values = array
}
task.resume()
dispatch_async(dispatch_get_main_queue()) { tableView.reloadData()}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if values.count > 20{
return 20
}
else{
return values.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as!postCell
let maindata = values[values.count-1-indexPath.row]
if maindata["category"] as? String == "Services"{
cell.postImg.image = UIImage(named: "tile_services")
}
else if maindata["category"] as? String == "exchange"{
cell.postImg.image = UIImage(named: "tile_exchange")
}
else if maindata["category"] as? String == "Tutors"{
cell.postImg.image = UIImage(named: "tile_tutoring")
}
else if maindata["category"] as? String == "Sports"{
cell.postImg.image = UIImage(named: "tile_sports")
}
else if maindata["category"] as? String == "Sublet"{
cell.postImg.image = UIImage(named: "tile_sublet")
}
else if maindata["category"] as? String == "Events"{
cell.postImg.image = UIImage(named: "tile_events")
}
else{
cell.postImg.image = UIImage(named: "tile_carpool")
}
if maindata["category"] as? String == "Services" || maindata["category"] as? String == "Tutors" || maindata["category"] as? String == "Events"{
cell.title.text = maindata["title"] as? String
}
else if maindata["category"] as? String == "Sublet" || maindata["category"] as? String == "Rideshare"{
cell.title.text = maindata["location"] as? String
}
else{
cell.title.text = maindata["item"] as? String
}
if maindata["category"] as? String == "Sublet" || maindata["category"] as? String == "Rideshare"{
cell.location.text = ""
}
else{
cell.location.text = maindata["location"] as? String
}
cell.category.text = maindata["category"] as? String
cell.price.text = maindata["price"] as? String
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
So I have a globale variable called values which is a NSArray, and I want to set the value of this array to be the array that I retrieve from database. However, in function get(), the post request acts as another thread and I have to write self.values = array which doesn't change the value of my global variable.
I need that value to organize my tableview in the main array.
Basically, my question is how can I get the value from a closure and set it to a global variable.
Thank you! Let me know if you guys don't understand what I'm saying.

You need a completion handler in your func get() as you're doing the dataTaskWithRequest which is an Async call. Try this:
func get(finished: (isDone: Bool) -> ()){
//your code
data, response, error in
if error != nil {
finished(isDone: false)
print("error=\(error)")
return
}
print("response = \(response)")
let array = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
self.values = array
}
}
task.resume()
finished(isDone: true)
}
And then in your viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
get { success in
if success{
//reload your tableview here
}
}

The closure implicitly retains self, so you are actually modifying that property. But you need to refresh your tableView after the data is retrieved within the closure, either using reloadCellsAtIndexPath, insertCellsAtIndexPath, or reloadData. The latter is the simplest approach, but completely replaces the current state of the table.
Additionally, you are causing a retain cycle in your closure. You should pass in self as a weak or unowned property to let ARC do its job. For more information on that: http://krakendev.io/blog/weak-and-unowned-references-in-swift
Example:
func get(){
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.percyteng.com/orbit/getAllpostsTest.php")!)
request.HTTPMethod = "POST"
let postString = "user=\("ios")"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let array = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
self.values = array
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
self.tableView?.reloadData();
}
}
task.resume()
}

Related

Multiple UICollection View on single UIView Scrolling is not working while cell data loading from API

In the home screen section I have three different UICollection view two of them(Top News and Accommodation) getting data from API and last one(Category) have static data and the problem is that while loading the data from API even I am not able to scroll the static section of UICollection view cell but as data loading complete every thing working fine I am not able to find the problem's solution please help me
override func viewDidLoad() {
super.viewDidLoad()
topNewCV.delegate = self
topNewCV.dataSource = self
accommodationCV.delegate = self
accommodationCV.dataSource = self
categoryCV.dataSource = self
categoryCV.delegate = self
//Loading getNearByPlace function
self.getNearByPlace()
}
//cellForItemAt indexPath function
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == accommodationCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AccommodationCollectionViewCell", for: indexPath) as! AccommodationCollectionViewCell
cell.titleContainer.text = self.accommodationObject.titleArray[indexPath.row]
if self.accommodationObject.titleArray.count == self.accommodationObject.imgArray.count {
if let img = cache.object(forKey: self.accommodationObject.imgArray[indexPath.row] as AnyObject) {
DispatchQueue.global().async {
DispatchQueue.main.async {
cell.imgContainer.image = img as? UIImage
}
}
} else {
DispatchQueue.global().async {
DispatchQueue.main.sync {
cell.imgContainer.image = UIImage(url: URL(string: "\(self.accommodationObject.imgArray[indexPath.row])"))
self.cache.setObject(UIImage(url: URL(string: "\(self.accommodationObject.imgArray[indexPath.row])"))!, forKey: self.accommodationObject.imgArray[indexPath.row] as AnyObject)
}
}
}
} else {
print("Both have not equal data")
}
return cell
} else if collectionView == categoryCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
cell.categorymodel = self.categoryModels?[indexPath.item]
if indexPath.row % 2 == 0 {
cell.categoryCVViewContainer.backgroundColor = colorLiteral(red: 0.3333333333, green: 0.7844525506, blue: 0.6620362924, alpha: 1)
} else {
cell.categoryCVViewContainer.backgroundColor = colorLiteral(red: 1, green: 0.4039215686, blue: 0.4039215686, alpha: 1)
}
return cell
}
return cell
}
// this fun is for getting data from api
func getNearByPlace() {
var strGoogleApi = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(user_latitude!), \(user_longitude!)&radius=1000&keyword=hotel&sensor=true&key=abc”
strGoogleApi = strGoogleApi.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
print(strGoogleApi)
var urlRequest = URLRequest(url: URL(string: strGoogleApi)!)
urlRequest.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error == nil {
if let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]{
if let allResults = json!["results"] as? [[String: Any]] {
print(allResults)
for result in allResults {
var geometry = [String: Any]()
geometry = result["geometry"] as! [String: Any]
var location = [String: Any]()
location = geometry["location"] as! [String: Double]
self.latitudeArray.append(location["lat"] as! Double)
self.longitudeArray.append(location["lng"] as! Double)
let name = result["name"]
var image = [[String: Any]]()
if result["photos"] != nil {
image = result["photos"] as! [[String: Any]]
var img = image[0]
let url = self.getImageFromApi(image: img["photo_reference"] as! String)
self.imgReferenceArray.append(url)
} else {
self.imgReferenceArray.append(self.icons)
}
let place_id = result["place_id"]
let address = result["vicinity"]
if result["name"] != nil {
self.nameArray.append(name as! String)
self.accommodationObject.titleArray = self.nameArray
}
if result["place_id"] != nil {
self.placeIdArray.append(place_id as! String)
} else if result["vicinity"] != nil {
self.addressArray.append(address as! String)
}
}
}
}
OperationQueue.main.addOperation({
if self.nameArray.count != 0 {
DispatchQueue.main.async {
self.accommodationCV.reloadData()
self.categoryCV.reloadData()
}
}
})
self.accommodationObject.imgArray = self.imgReferenceArray
}
}
task.resume()
}
Some very basic tips:
a) take in account you are dealing with multiple threads.. so adding to arrays must be done with a lot of care.
b) STOP previous calls of "task" var if reloading.. for example saving task in an instance var: call task.cancel()

Converting Image Data into Base64 String For MySQL Blob and Decoding it Back

I'm trying to send data (first name, last name, age, image) to a MySQL database. And I can. The following is what I have.
class PostViewController: UIViewController {
#IBAction func selectTapped(_ sender: UIButton) {
postData()
}
func postData() {
var request = URLRequest(url: URL(string: "http://www.mywebsite.tv/post.php")!)
request.httpMethod = "POST"
let fName = firstField.text!
let lName = lastField.text!
let ageStr = ageField.text!
let image = imageView.image!
guard let pictStr = convertImageBase64(image: image) else {
return
}
let postString = "a=\(fName)&b=\(lName)&c=\(ageStr)&d=\(pictStr)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
func convertImageBase64(image: UIImage) -> String? {
guard let pictData = UIImagePNGRepresentation(image) else {
return nil
}
let strBase64: String = pictData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
return strBase64
}
}
And populating UITableView with data from MySQL..
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, DataModelProtocol {
// MARK: - Variables
var myItems = NSArray()
// MARK: - IBOutlets
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// somehow loading data //
}
// MARK: - TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ProfileTableViewCell
let item: PictureModel = myItems[indexPath.row] as! PictureModel
cell.firstLabel.text = item.fName
cell.lastLabel.text = item.lName
cell.ageLabel.text = item.ageStr
print(item.pictStr!) // returning iVBORw0KGgoAAAANSUh...
if let img = convertBase64Image(base64String: item.pictStr!) {
cell.pictureImageView.image = img
}
return cell
}
func convertBase64Image(base64String: String) -> UIImage? {
if let pictData = Data(base64Encoded: base64String, options: Data.Base64DecodingOptions.ignoreUnknownCharacters) {
return UIImage(data: pictData)
} else {
return nil
}
}
}
The thing is cell.pictureImageView.image is always nil. And I now know why. When I post a picture, a sample base64 string is
iVBORw0KGgoAAAANSUhEUgAAAFYAAACACAYAAACRMZ7FAAAAAXNSR0IArs4c6QAA\r\nABxp...
And the decoded string that I get is
iVBORw0KGgoAAAANSUhEUgAAAFYAAACACAYAAACRMZ7FAAAAAXNSR0IArs4c6QAA ABxp...
So the Data.Base64DecodingOptions.ignoreUnknownCharacters option actually replaces \r\n with a white space. How can I encode an image and then decode it back the same?
Drop the .lineLength64Characters options:
func convertImageBase64(image: UIImage) -> String? {
guard let pictData = UIImagePNGRepresentation(image) else {
return nil
}
let strBase64: String = pictData.base64EncodedString(options: [])
return strBase64
}
That way the string will not have a \r\n in it in the first place.

UITable Won't display. Value type 'FilmsAPITableViewCell' has no member 'movieTitle'

I am getting build errors when trying to display jSON data in Xcode using Swift 3. I am going to copy a portion of my code to this page with hopes you guys can assist me.
I have found similar questions on this site however answers don't seem to help.
class FilmsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var tableView : UITableView!
var FilmArray = [String]()
let film_url = "https://www.testing.com/api/resources/films/1"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for:indexPath) as! FilmsAPITableViewCell
// Adding the right informations
cell.movieTitle.text = FilmArray[indexPath.row]
// Returning the cell
return cell
}
// #IBOutlet weak var FilmsView: UITableView!
// weak var tableView : UITableView!
// var FilmArray = [String]()
//
// let film_url = "https://www.distribber.com/api/resources/films/1"
//
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView (frame:view.bounds)
view.addSubview(tableView)
self.tableView = tableView
tableView.dataSource = self
tableView.delegate = self
// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return 1
// }
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// // Getting the right element
// //let films = FilmArray[indexPath.row]
//
//
// // Instantiate a cell
// //let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "moviecell")
// let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FilmsAPITableViewCell
// // cell.movieTitle.text = FilmArray[indexPath.row]
// // Adding the right informations
// cell.movieTitle.text = FilmArray[indexPath.row]
// // Returning the cell
// return cell
// }
// }
//}
let url:URL = URL(string: film_url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.setValue("740c94c51891c02b64d6c78840b478fe0b02fe2c", forHTTPHeaderField: "X-API-KEY")
request.setValue("Basic YmhlZW0uZW5nckBnbWFpbC5jb206YmgzM20=", forHTTPHeaderField: "Authorization")
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = ""
// for (key, value) in post_data
// {
// paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
// }
//
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data!, options: [])
// Prasing JSON
var parsedData = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
print(parsedData)
if let FilmArray = parsedData["films"] as? NSArray {
for movieTitle in FilmArray{
if let filmDict = movieTitle as? NSDictionary{
if let film = filmDict.value(forKey: "title") {
self.FilmArray.append(film as! String)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}
}
print("Hello")
self.tableView.reloadData()
print(self.FilmArray)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["data"] as? NSDictionary
{
if let session_data = data_block["session"] as? String
{
// self.login_session = session_data
let preferences = UserDefaults.standard
preferences.set(session_data, forKey: "session")
// DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is also output from FilmsAPITableViewCell.swift
import UIKit
import UIKit
class FilmsAPITableViewCell: UITableViewCell {
#IBOutlet weak var movieTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
The reason why movieTitle is nil is because your custom cell class does not have that label outlet. You need to create a UILabel in your .xib (or storyboard) and create an outlet connection inside your custom cell class.
It also seems like no self.tableView.reloadData() exists in your completion block. Try adding that right after the print("Hello") line.
P.S. Don't forget to dispatch the reload to the main queue.
Here is also your code which I edited as to get it working:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let url:URL = URL(string: film_url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.setValue("740c94c51891c02b64d6c78840b478fe0b02fe2c", forHTTPHeaderField: "X-API-KEY")
request.setValue("Basic YmhlZW0uZW5nckBnbWFpbC5jb206YmgzM20=", forHTTPHeaderField: "Authorization")
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = ""
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
var json:Any?
do
{
if let existingData = data {
json = try JSONSerialization.jsonObject(with: existingData, options: [])
}
// Prasing JSON
if let parsedData = json as? [[String:Any]] {
for dict in parsedData {
if let title = dict["title"] as? String {
self.FilmArray.append(title)
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["data"] as? NSDictionary
{
if let session_data = data_block["session"] as? String
{
// self.login_session = session_data
let preferences = UserDefaults.standard
preferences.set(session_data, forKey: "session")
// DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
}

Table View not updating with data fetched by HTTP Request

I'm trying to populate a table view with data fetched from a http request (JSON) but it's not working. If I populate the Table View with static data everything works fine.. code snippet:
import UIKit
import Alamofire
//import SwiftyJSON
class ViewControllerNews : UITableViewController{
#IBOutlet var table: UITableView!
var posts = [FacebookPost]()
override func viewDidLoad() {
pullFacebookNews()
//loadSampleNews()
super.viewDidLoad()
}
func pullFacebookNews() -> Void {
let params = ["limit":"100", "access_token": myaccesstoken]
Alamofire.request( "https://graph.facebook.com/<page-id>/posts", parameters: params).responseJSON{ response in
if let responsejson = response.result.value {
let JSON = responsejson as! NSDictionary
let itemsArray: NSArray? = JSON.object(forKey: "data") as? NSArray
if(itemsArray != nil) {
for item in itemsArray! {
let thisitem = item as! NSDictionary
print(thisitem.object(forKey: "message") as? String)
print(thisitem.object(forKey:"created_time") as? String)
let title1 = thisitem.object(forKey: "message") as? String
let value1=thisitem.object(forKey:"created_time") as? String
if(title1 != nil && value1 != nil) {
let news = FacebookPost(title: title1!, value: value1!)
self.posts.append(news)
}
}}
}
}
do_table_refresh()
}
func do_table_refresh() {
DispatchQueue.global(qos: .background).async {
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
func loadSampleNews() {
let news1 = FacebookPost(title: "ich bin ein datum", value: "Ich bin eine news")
let news2 = FacebookPost(title: "ich bin ein datum2", value: "Ich bin eine news2")
let news3 = FacebookPost(title: "ich bin ein datum3", value: "Ich bin eine news3")
posts += [news1, news2, news3]
}
override func numberOfSections(in: UITableView) -> Int {
return 1
}
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_: UITableView, cellForRowAt: IndexPath) -> UITableViewCell {
let cellIdentifier = "NewsTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: cellForRowAt as IndexPath) as! NewsTableViewCell
let news = posts[cellForRowAt.row]
cell.date.text = news.title
cell.news_text.text = news.value
return cell
}
}
The loadSampeNews() works perfectly, but pullFacebookNews() does not populate the table. While debugging the array is filled..
If you set breakpoints, you will see that do_table_refresh() gets called before you hit if let responsejson = response.result.value. This is because the Alamofire call has a completion handler. You have to wait until the internet call is complete before you call do_table_refresh().
So you essentially just have to move do_table_refresh() if all of your data is loading correctly like you say it is.
func pullFacebookNews() -> Void {
let params = ["limit":"100", "access_token": myaccesstoken]
Alamofire.request( "https://graph.facebook.com/<page-id>/posts", parameters: params).responseJSON{ response in
if let responsejson = response.result.value {
let JSON = responsejson as! NSDictionary
let itemsArray: NSArray? = JSON.object(forKey: "data") as? NSArray
if(itemsArray != nil) {
for item in itemsArray! {
let thisitem = item as! NSDictionary
print(thisitem.object(forKey: "message") as? String)
print(thisitem.object(forKey:"created_time") as? String)
let title1 = thisitem.object(forKey: "message") as? String
let value1=thisitem.object(forKey:"created_time") as? String
if(title1 != nil && value1 != nil) {
let news = FacebookPost(title: title1!, value: value1!)
self.posts.append(news)
}
}
}
self.do_table_refresh()
}
}
}
Additionally, don't name your methods with that syntax. It is not best practice. Should be doTableRefresh() or something.

In Swift, tableView data loaded from JSON only loads 80% of the time

I'm populating my tableView with JSON data, most of the time the data shows but for some strange reason other times it doesn't. I tested the JSON data in Chrome and the info is there. I also made print statements to print the info after it has downloaded and it appears to download correctly. I can't figure out why 80% of the time the data populates the tableView correctly and 20% of the time it doesn't. Here is a sample of my code, there are many more cells but I shortened it to 2 for this example:
var task : NSURLSessionTask?
var newURL : String?
var bannerArray: [String] = []
var overViewArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON(newURL!)
}
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.updateDetailShowInfo(data)
}
else {
"Not getting JSON"
}
}
}
task!.resume()
}
func updateDetailShowInfo (data: NSData!) {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
else { return }
_ = ""
print(overview)
bannerArray.append(banner)
overViewArray.append(overview)
}
catch {
print("It ain't working")
}
self.DetailTvTableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return bannerArray.count
case 1: return overViewArray.count
default: fatalError("Unknown Selection")
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("bannerCell", forIndexPath: indexPath) as! BannerCell
cell.bannerImage.sd_setImageWithURL(NSURL(string: bannerArray[indexPath.row]))
self.DetailTvTableView.rowHeight = 100
DetailTvTableView.allowsSelection = false
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("overviewCell", forIndexPath: indexPath) as! OverviewCell
let overViewText = overViewArray[indexPath.row]
if overViewText != "" {
cell.overView.text = overViewText
} else {
cell.overView.text = "N/A"
}
self.DetailTvTableView.rowHeight = 200
print(overViewArray[indexPath.row])
return cell
default: ""
}
return cell
}
I'm just doing this off the web. And I think there are some errors. You need to debug them yourself.
Your understanding of fetching the JSON and GCD is totally wrong. I believe these codes you got somewhere off the web. Go read up what is dispatch_async.
Basically, you need to create session to fetch JSON data, which you have done it correctly, however, within the NSJSONSerialization, you need to store them in a variable and append it to your array. This is fetched asynchronously. Your dispatch_async will reload data serially.
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
bannerArray.append(banner)
overViewArray.append(overview)
} dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.DetailTvTableView.reloadData()
}
else {
"Not getting JSON"
}
}
catch {
print("It ain't working")
}
}
}
task!.resume()
}

Resources