Below is my ViewController code. The println in GetRequest prints the correct data that it receives from the HTTP GET request. At this point, tableData has 10 key-value pairs. However, if I put a break point after the call to GetRequest() in viewDidLoad(), tableData is empty and nothing is displayed in the tableView. Why is this? Where am I going wrong?
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
#IBOutlet var tableView: UITableView!
var tableData: [String:String] = [:]
let textCellIdentifier = "TextCell"
func GetRequest(urlPath: String)
{
var LatestWaitTimes: [String:String] = [:]
let url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
var jsonResult: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err)
if err != nil {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
let json = JSON(jsonResult!)
let count: Int? = json.array?.count
if let ct = count {
for index in 0...ct-1 {
let name = json[index]["CrossingName"].string
var wait = json[index]["WaitTime"].stringValue
if (wait == "-1")
{
wait = "No Info"
}
else
{
wait += " min"
}
println(name!+": "+wait)
LatestWaitTimes[json[index]["CrossingName"].stringValue] = wait as String?
}
}
self.tableData = LatestWaitTimes
})
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
var apiInfo = GetWaitTimes()
GetRequest(apiInfo.BorderCrossingApi+"?AccessCode="+apiInfo.AccessCode)
self.tableView.delegate = self
self.tableView.dataSource = self
tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(self.textCellIdentifier) as! UITableViewCell
let thisEntry = self.tableData.values.array[indexPath.row]
cell.textLabel?.text = thisEntry+": "+tableData[thisEntry]!
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
The problem is that GetRequest runs asynchronously. So you have to reloadData inside the completionHandler of dataTaskWithURL:
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
// do all of your existing stuff here
dispatch_async(dispatch_get_main_queue()) {
self.tableData = LatestWaitTimes
self.tableView.reloadData()
}
})
task.resume()
You can remove the reloadData from within viewDidLoad.
Rearrange the orders of the method calls:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
var apiInfo = GetWaitTimes()
GetRequest(apiInfo.BorderCrossingApi+"?AccessCode="+apiInfo.AccessCode)
tableView.reloadData()
}
Explanation: you set the delegate and dataSource before you GetRequest() so the dataSource won't be nil.
Try this it may help you
you can add a callback (for reload tablview) in GetRequest function and run the callback after parser json in completionHandler
Maybe When you set the delegate with tableview, tableData still be nil (waitting for http response)
this is my code .you can refer to
demand.loadDataFromServer(self.view, done: { () -> Void in
self.demands = demand.getDemandDataList()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
})
func loadDataFromServer(view:UIView,done:(() -> Void)!){
var request:HttpRequest = HttpRequest(view:view)
request.GET(getDemandListURL, parameters: nil, success: { (dict) -> Void in
self.demandLists = dict["data"] as NSMutableArray
done()
}, failed: { (dict) -> Void in
}) { (error) -> Void in
}
}
Related
I am new at Alamofire and I am following the book IOS Apps with REST APIs, where I try to parse JSON and populate the tableview but the problem is that it calls numberOfRowsInSection before the function loadGists finishes calling the Alamofire chained requests so I can populate the Array and then populate the tableview accordingly, it seems that .responseArray function is called Asynchronous or called after numberOfRowsInSection function.
var gists: [Gist]!
#IBOutlet var tabelView1: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tabelView1.delegate = self
self.tabelView1.dataSource = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
loadGists()
}
func loadGists() {
GitHubAPIManager.sharedInstance.getPublicGists() { result in
guard result.error == nil else {
print(result.error)
// TODO: display error
return
}
if let fetchedGists = result.value {
self.gists = fetchedGists
}
DispatchQueue.main.async {
self.tableView1.reloadData()
}
}
}
Where GitHubAPIManager Class has:
class GitHubAPIManager: NSObject {
static let sharedInstance = GitHubAPIManager()
func getPublicGists(completionHandler: (Result<[Gist]>) -> Void) {
Alamofire.request(GistRouter.GetPublic())
.responseArray { (response) in
completionHandler(response.result)
}
}
}
And responseArray is:
public func responseArray<T: ResponseJSONObjectSerializable>(
completionHandler: Response<[T]) -> Self {
let serializer = ResponseSerializer<[T]> { request, response, data, error in
guard error == nil else {
return .Failure(error!)
}
guard let responseData = data else {
let failureReason = "Array could not be serialized because input data was nil."
let error = Error.errorWithCode(.DataSerializationFailed,
failureReason: failureReason)
return .Failure(error)
}
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response,
responseData, error)
switch result {
case .Success(let value):
let json = SwiftyJSON.JSON(value)
var objects: [T] = []
for (_, item) in json {
if let object = T(json: item) {
objects.append(object)
}
}
return .Success(objects)
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: serializer, completionHandler: completionHandler)
}
For table view:
func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return gists.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let gist = gists[indexPath.row]
cell.textLabel!.text = gist.description
cell.detailTextLabel!.text = gist.ownerLogin
return cell
}
GistRouter is:
enum GistRouter: URLRequestConvertible {
static let baseURLString:String = "https://api.github.com"
case getPublic()
var method: HTTPMethod {
switch self {
case .getPublic:
return .get
}
}
func asURLRequest() throws -> URLRequest {
let result: (path: String, parameters: [String: AnyObject]?) = {
switch self {
case .getPublic:
return ("/gists/public", nil)
}
}()
let URL = Foundation.URL(string: GistRouter.baseURLString)!
var UrlRequest: URLRequest? = URLRequest(url: URL.appendingPathComponent(result.path))
UrlRequest = try URLEncoding.default.encode(UrlRequest!, with: result.parameters)
UrlRequest?.httpMethod = method.rawValue
return UrlRequest!
}
And Gist Class is:
class Gist: ResponseJSONObjectSerializable {
var id: String?
var description: String?
var ownerLogin: String?
var ownerAvatarURL: String?
var url: String?
required init(json: SwiftyJSON.JSON) {
self.description = json["description"].string
self.id = json["id"].string
self.ownerLogin = json["owner"]["login"].string
self.ownerAvatarURL = json["owner"]["avatar_url"].string
self.url = json["url"].string
}
required init() {
}
}
I have tried DispatchQueue.global(qos: .utility).async and also
let semaphore = DispatchSemaphore(value: 0) in different ways with no luck, I need to have Alamofire chained requests all done first and then numberOfRowsInSection called, please help.
It worked for me with:
var gists = [Gist]() {
didSet {
self.tabelView1.reloadData()
}
}
I am trying to use youtube api in ios swift, and following this tutorial http://www.appcoda.com/youtube-api-ios-tutorial/, but some how my didSelectRowAtIndexPath method is never being called.
Any suggestions will be helpful. Thanks
import UIKit
class SearchVideosViewController: UIViewController, UITableViewDataSource,UITableViewDelegate , UITextFieldDelegate{
#IBOutlet weak var wait: UIView!
#IBOutlet weak var tblShowVideoList: UITableView!
#IBOutlet weak var searchVideoText: UITextField!
var selectedVideoIndex:Int!
var videosArray: Array<Dictionary<NSObject,AnyObject>> = []
var apiKey = "my_api_key"
override func viewDidLoad() {
super.viewDidLoad()
searchVideoText.delegate = self
tblShowVideoList.delegate = self
tblShowVideoList.dataSource = self
wait.hidden = true
// Do any additional setup after loading the view.
}
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 {
return videosArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell!
cell = tableView.dequeueReusableCellWithIdentifier("videolistcell", forIndexPath: indexPath)
let videoTitle = cell.viewWithTag(10) as! UILabel
let videoThumbnail = cell.viewWithTag(11) as! UIImageView
let videoDetails = videosArray[indexPath.row]
videoTitle.text = videoDetails["title"] as? String
videoThumbnail.image = UIImage(data: NSData(contentsOfURL: NSURL(string: (videoDetails["thumbnail"] as? String)!)!)!)
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 140.0
}
// MARK: UITextFieldDelegate method implementation
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
wait.hidden = false
let type = "video"
// Form the request URL string.
var urlString = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(textField.text)&type=\(type)&key=\(apiKey)"
urlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
// Create a NSURL object based on the above string.
let targetURL = NSURL(string: urlString)
// Get the results.
performGetRequest(targetURL, completion: { (data, HTTPStatusCode, error) -> Void in
if HTTPStatusCode == 200 && error == nil {
// Convert the JSON data to a dictionary object.
do {
let resultsDict = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! Dictionary<NSObject, AnyObject>
// Get all search result items ("items" array).
let items: Array<Dictionary<NSObject, AnyObject>> = resultsDict["items"] as! Array<Dictionary<NSObject, AnyObject>>
// Loop through all search results and keep just the necessary data.
for var i=0; i<items.count; ++i {
let snippetDict = items[i]["snippet"] as! Dictionary<NSObject, AnyObject>
// Gather the proper data depending on whether we're searching for channels or for videos.
// Create a new dictionary to store the video details.
var videoDetailsDict = Dictionary<NSObject, AnyObject>()
videoDetailsDict["title"] = snippetDict["title"]
videoDetailsDict["thumbnail"] = ((snippetDict["thumbnails"] as! Dictionary<NSObject, AnyObject>)["default"] as! Dictionary<NSObject, AnyObject>)["url"]
videoDetailsDict["videoID"] = (items[i]["id"] as! Dictionary<NSObject, AnyObject>)["videoId"]
// Append the desiredPlaylistItemDataDict dictionary to the videos array.
self.videosArray.append(videoDetailsDict)
// Reload the tableview.
self.tblShowVideoList.reloadData()
}
} catch {
print(error)
}
}
else {
print("HTTP Status Code = \(HTTPStatusCode)")
print("Error while loading channel videos: \(error)")
}
// Hide the activity indicator.
self.wait.hidden = true
})
return true
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Selected row index \(indexPath.row)")
selectedVideoIndex = indexPath.row
performSegueWithIdentifier("playVideo", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "playVideo" {
let videosPlayerViewController = segue.destinationViewController as! VideosPlayerViewController
videosPlayerViewController.videoID = videosArray[selectedVideoIndex]["videoId"] as! String
}
}
// MARK: Custom method implementation
func performGetRequest(targetURL: NSURL!, completion: (data: NSData?, HTTPStatusCode: Int, error: NSError?) -> Void) {
let request = NSMutableURLRequest(URL: targetURL)
request.HTTPMethod = "GET"
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfiguration)
let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(data: data, HTTPStatusCode: (response as! NSHTTPURLResponse).statusCode, error: error)
})
})
task.resume()
}
}
Try comments on:
searchVideoText.delegate = self
I have async request to HTTP, i want to read JSON and fill my viewtable.
I get data with request, i make NSArray, but i can pass it inside my functions, tableView numberOfRowsInSection return only 1, please help.
import Foundation
import UIKit
class ThirdView : UITableViewController {
var jsonz:NSArray = ["Ray Wenderlich"];
let url = NSURL(string: "http://iweddings.ru/xmlrestaurant.json");
override func viewDidLoad() {
super.viewDidLoad()
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
let json = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as! NSArray
println(json)
// here i see in xCode output
// price = 4500;
// rating = 45;
// slogan = "\U041d\U0435\U043b\U0435\U0433\U0430\U043b\U044c\U043d\U043e";
// status = 1;
// type = 1;
// etc....
self.jsonz = json;
}
task.resume()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
println(self.jsonz.count);
return self.jsonz.count;
// here i see always "1" ???? why?
}
NSArray jsonz does not change.
Sorry for my english.
You need to call reloadData() method after json data is downloaded:
class ThirdView: UITableViewController {
var jsonz: NSArray = ["Ray Wenderlich"]
let url = NSURL(string: "http://iweddings.ru/xmlrestaurant.json")
override func viewDidLoad() {
super.viewDidLoad()
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
let json = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as! NSArray
println(json)
self.jsonz = json;
self.tableView.reloadData()
}
task.resume()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
println(self.jsonz.count);
return self.jsonz.count;
}
}
This is the code i have to far on the main table view view controller I just don't know how to go about transitioning to a new view controller to a selected cell to display more content about whatever cell was selected.
import UIKit
class MainVC: UITableViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var Table1: UITableView!
var postArray: [[String]] = []
var postSubject = ""
var postDescription = ""
var postDate = ""
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
func fetchData() {
println("success1")
postArray = []
let httpMethod = "GET"
let timeout = 15
let urlAsString = "http://cgi.soic.indiana.edu/~team12/main_requests.php"
let url = NSURL(string: urlAsString)!
let urlRequest = NSMutableURLRequest(URL: url,
cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 15.0)
urlRequest.HTTPMethod = httpMethod
let queue = NSOperationQueue()
println("success2")
NSURLConnection.sendAsynchronousRequest(urlRequest,
queue: queue,
completionHandler: {(response: NSURLResponse!,
data: NSData!,
error: NSError!) in
if data.length > 0 && error == nil{
let html = NSString(data: data, encoding: NSUTF8StringEncoding)
} else if data.length == 0 && error == nil{
println("Nothing was downloaded")
} else if error != nil{
println("Error happened = \(error)")
}
if data.length > 0 && error == nil {
let html = NSString(data: data, encoding: NSUTF8StringEncoding)
var newArrayofDicts : NSMutableArray = NSMutableArray()
var arrayOfDicts : NSMutableArray? = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error:nil) as? NSMutableArray
println("successmutable")
if arrayOfDicts != nil {
for item in arrayOfDicts! {
if var dict = item as? NSMutableDictionary{
if dict["subject"] != nil{
self.postSubject = dict["subject"] as String
self.postDescription = dict["subject"] as String
self.postDate = dict["date"] as String
self.postArray.append([self.postSubject,self.postDescription,self.postDate])
println("successpost")
}
}
}
}
}
}
)
sleep(1)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
fetchData()
tableView.reloadData()
println("successanime")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
println("successoverride")
let cell = tableView.dequeueReusableCellWithIdentifier("UpdateCell", forIndexPath: indexPath)
as UITableViewCell
println("successupdatecell")
cell.textLabel.text = postArray[indexPath.row][1]
cell.detailTextLabel?.text = postArray[indexPath.row][2]
println("success3")
return cell
}
Try this method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
...
}
Or you can do it straight in the storyboard by ctrl dragging the prototype cell to the next viewController.
Hope this helps!!
Edit
If you want to segue to the next view controller programmatically, then you can do either:
performSegueWithIdentifier("yourSegueName", sender: nil)
Or:
presentViewController(nextViewController, animated: true, completion: nil)
I have trouble in loading the table view when parsing json files in swift.
Parsing the data is doing well. But no data are displayed in the table view.
This is the code :
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var redditListTableView: UITableView!
var tableData = []
#IBAction func cancel(sender: AnyObject) {
self.dismissViewControllerAnimated(false, completion: nil)
println("cancel")
}
#IBAction func done(sender: AnyObject) {
println("done")
}
override func viewDidLoad() {
super.viewDidLoad()
searchJsonFile("blabla.json")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
println(tableData.count)
return tableData.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestCell")
let rowData: NSString = self.tableData[indexPath.row] as NSString
cell.textLabel.text = rowData as String
return cell
}
func searchJsonFile(searchFile: String) {
let urlPath = "http://data.../\(searchFile)"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
println("Task completed")
if(error != nil) {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if(err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
var results = [String]()
if let results1 = jsonResult["data"] as? NSDictionary{
for (key, value) in results1 {
if let eng = value["eng"] as? NSDictionary {
if let name = eng["name"] as? NSString{
results.append(name)
}
}
}
}
//println(results) OK!!!!
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.redditListTableView.reloadData()
})
})
task.resume()
}
}
You are returning 0 from numberOfSectionsInTableView - so you get no data displayed. You want 1 section -
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
If you are not having sections then just remove this function or comment
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 0
}
or else return 1