How to transfer data from NSArray to String in Swift? - arrays

I have to put data in tableview, but even tough I get info from JSON, I can't pass data to postTitle variable. Why is that? Here is my code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var postTitle = [AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
var baseURL = "https://hacker-news.firebaseio.com/v0/topstories.json"
// https://hacker-news.firebaseio.com/v0/item/9324191.json
if let url = NSURL(string: baseURL) {
var taskURL = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if error != nil {
println("Error: \(error.localizedDescription)")
} else {
var jsonError: NSError?
if let topStories = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as? NSArray {
self.postTitle.append(topStories)
}
}
})
taskURL.resume()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
println(postTitle.count)
return postTitle.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
println(self.postTitle)
// cell.textLabel?.text = postTitle[indexPath.row]
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

topStories is an NSArray, but you are appending it to the postTitle array (which is of type [AnyObject]). Array.append adds a single item to the array. So you will have added one entry, an NSArray of a bunch of post IDs, to your postTitle array.
I am guessing what you want is to add the contents of topStories to postTitle? In which case you want to use the extend rather than the append method:
self.postTitle.extend(topStories)

Given that you apparently reload ALL the titles with each request, you could just as easily do this: self.titles = topStories
I just built a test app this way and it worked perfectly fine.
PS: self.postTitle.append would have yielded the wrong result anyway, as it would also append titles you already have in your array. The method you probably should be using would be self.postTitle.join as it uses intersection.

So the other guy from Reddit help me out. I was missing few things. First was data type Int, which needs to be passed to instance variable, and second was UITableView: here is working solution. Hope this will help someone.
import UIKit
class ViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
var postTitles = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
var baseURL = "https://hacker-news.firebaseio.com/v0/topstories.json"
if let url = NSURL(string: baseURL) {
var taskURL = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if error != nil {
println("Error: \(error.localizedDescription)")
} else {
var jsonError: NSError?
if let topStories = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as? [Int] {
self.postTitles = Array(topStories[0...9])
// Reload the table with our new results!
self.tableView.reloadData()
}
}
})
taskURL.resume()
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postTitles.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
let postTitle = String(self.postTitles[indexPath.row])
cell.textLabel?.text = postTitle
return cell
}
}

Related

Swift ios Kanna data returning empty first time in viewDidLoad

I am trying to load data from an API into my TableViewController but the first time it loads the data returns empty. I can't build a table because the data is empty.
import UIKit
import Kanna
class TableViewController: UITableViewController {
var country = [String]()
override func viewDidLoad() {
super.viewDidLoad()
gets()
print(country)// is empty view controller.
}
func gets(){
let url = "https://site"
let myURL = NSURL(string: url)
let URLTask = URLSession.shared.dataTask(with: myURL! as URL) {
myData, response, error in
guard error == nil else {return}
let myHTML = String(data: myData!, encoding: String.Encoding.utf8)
DispatchQueue.global(qos: .userInitiated).async {
if let doc = try? HTML(html: myHTML!, encoding: .utf8) {
DispatchQueue.main.async {
for fdata in doc.xpath("//*[#id='content']/table[3]") {
let i = fdata.text!
self.country.append(i)
}
}
}
}
}
URLTask.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return country.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = country[indexPath.row]
return cell
}
I know that there are many similar problems. I tried different options but I didn't succeed.
Since the data is coming from a network request, the table will always be empty initially. You could fetch the data from the previous view controller (displaying some kind of loading indicator) and wait to present this one until the fetch has completed.
Or, if you are just looking to reload your table when the data comes in, try adding a self.tableView.reloadData() after the data has loaded:
...
DispatchQueue.main.async {
for fdata in doc.xpath("//*[#id='content']/table[3]") {
let i = fdata.text!
self.country.append(i)
}
self.tableView.reloadData()
}
...
Found it! Use the viewWillAppear (_animated: Bool) method that is called before the view appears on the screen.
override func viewWillAppear(_ animated: Bool) {
gets()
}

Getting JSON data to populate a TableView from an Array

I'm able to successfully populate the array with my JSON data inside of the loop, and I'm trying to populate the cells of my TableView with the same information, but currently the list comes up with no content.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var table: UITableView!
var headlines = [String]()
let baseURL = "http://api.nytimes.com/svc/topstories/v1/business.json?api-key=123456789"
override func viewDidLoad() {
getJSON()
super.viewDidLoad()
self.table.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.table.dataSource = self
self.table.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getJSON() {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request){ (data, response, error) -> Void in
if error == nil {
let SwiftyJSON = JSON(data: data!)
let theTitle = SwiftyJSON["results"].arrayValue
for title in theTitle{
let titles = title["title"].stringValue
self.headlines.insert(titles, atIndex: 0)
//print("- " + titles)
}
print(self.headlines)
}
else {
print("there was an error")
}
}
task.resume()
}
// From the UITAbleViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return headlines.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.table.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel!.text = self.headlines[indexPath.row]
return cell
}
// From the UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped on cell # \(indexPath.row)")
}
}
The task is asynchronous, so when you've loaded the array you have to reload your table
func getJSON() {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request){ (data, response, error) -> Void in
if error == nil {
let SwiftyJSON = JSON(data: data!)
let theTitle = SwiftyJSON["results"].arrayValue
for title in theTitle{
let titles = title["title"].stringValue
self.headlines.insert(titles, atIndex: 0)
//print("- " + titles)
}
print(self.headlines)
self.table.reloadData()
}
else {
print("there was an error")
}
}
task.resume()
}

Cannot assign value of type '[[String : AnyObject]]' to type '[[String : AnyObject?]]'

I'm getting the following error:
Cannot assign value of type '[[String : AnyObject]]' to type '[[String : AnyObject?]]'
It's strange this assignment was working before then when I restarted my Xcode, I started to get this error. From what I have read online, this should not give the error.
Here is my code:
import UIKit
import Alamofire
import SwiftyJSON
class Signal Condo TableViewController: UITableViewController {
#IBOutlet var tableview: UITableView!
var singleCondoData = [[String:AnyObject]]()
var CondoIndivi = [[String:AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return singleCondoData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SignleTableTableViewCell
if singleCondoData.count != 0 {
let dict = singleCondoData[indexPath.row] as NSDictionary
//cell.label1.text? = (dict["name"] as? String)!
if let nullcheck = (dict["address"] as? String) {
cell.label2.text? = nullcheck
}
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let dict = singleCondoData[indexPath.row] as NSDictionary
if let unitNullCheck = (dict["mls_number"] as? String) {
let item_id = unitNullCheck
getCondoUnits(item_id)
print(item_id)
}
}
//get the individual condo id
func getCondoUnits(condo_id : String){
Alamofire.request(.GET, "http://android.goidx.com/search/?mls_number=" + String(condo_id)).validate().responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
if let resData = json.arrayObject {
self.CondoIndivi = (resData as? [[String:AnyObject]])!
print(self.CondoIndivi)
}
if self.CondoIndivi.count > 0 {
self.tableview.reloadData()
}
}
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "details" :
let buildingdDetailVC = segue.destinationViewController as! DetailsViewController
buildingdDetailVC.CondoIndivi2 = self.CondoIndivi // line of the error
default:
break
}
}
}
}
The type of CondoIndivi2 variable is [[String: AnyObject?]] but you are passing an array of type [[String: AnyObject]] where the dictionary values are non-optional.
Since any non-optional value with same type can be safely converted to its optional corresponding, you can do the following:
buildingdDetailVC.CondoIndivi2 = self.CondoIndivi.map { $0 as [String: AnyObject?] }

Fatal error : Index out of range

I m getting this error :fatal error: Index out of range .I can't get what i m doing wrong .What i'm trying to do is , access an array dictionary by using an integer index than pass a string to get the value mapped to it .The sample works fine on playground but not excode why ? (The array dictionary is not empty)
Here is my code
var CondoIndivi2 = [[String:AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
scrollView.contentSize.height = 1500
print(CondoIndivi2)
if let description_Condo = self.CondoIndivi2[0]["description"] as? String {
print(description_Condo)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is the view that sends data to CondoIndivi2
import UIKit
import Alamofire
import SwiftyJSON
class SignleCondoTableViewController: UITableViewController {
#IBOutlet var tableview: UITableView!
var singleCondoData = [[String:AnyObject]]()
var CondoIndivi = [[String:AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return singleCondoData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SignleTableTableViewCell
if singleCondoData.count != 0 {
let dict = singleCondoData[indexPath.row] as NSDictionary
//cell.label1.text? = (dict["name"] as? String)!
if let nullcheck = (dict["address"] as? String) {
cell.label2.text? = nullcheck
}
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let dict = singleCondoData[indexPath.row] as NSDictionary
if let unitNullCheck = (dict["mls_number"] as? String) {
let item_id = unitNullCheck
getCondoUnits(item_id)
print(item_id)
}
}
//get the individual condo id
func getCondoUnits(condo_id : String){
Alamofire.request(.GET, "http://android.goidx.com/search/?mls_number=" + String(condo_id)).validate().responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
if let resData = json.arrayObject {
self.CondoIndivi = resData as! [[String:AnyObject]]
print(self.CondoIndivi)
}
if self.CondoIndivi.count > 0 {
self.tableview.reloadData()
}
}
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "details" :
let buildingdDetailVC = segue.destinationViewController as! DetailsViewController
buildingdDetailVC.CondoIndivi2 = self.CondoIndivi
default:
break
}
}
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
As #James put in his comment, you're creating an empty array in your code:
var CondoIndivi2 = [[String:AnyObject]]()
And then you're trying to access indexing in the position 0:
if let description_Condo = self.CondoIndivi2[0]["description"] as? String {
print(description_Condo)
}
And of course, you will have a runtime error of Index of out Range because your array it's empty, you always need to be sure before index an array that the index is greater than zero, less than equal to the size of the array and the array is not empty.
I hope this help you.
Inside your getCondoUnits(condo_id : String) is an asynchronous block(Alamofire.request), the CondoIndivi2 is received later than the viewDidLoad is executed. You should just pass condo_id to next viewController and do the request in it.

Error subscript

I got that error a while ago and I'm not sure, what's wrong. The Post is just normal custom class with two attributes of type String and int.
web api :http://jsonplaceholder.typicode.com/posts
PostService :
class PostService {
var settings:Settings!
init(){
self.settings = Settings()
}
func getPosts(callback:(NSArray) ->()){
request(settings.viewPost, callback: callback)
}
func request(url:String , callback:(NSArray) ->() ){
let nsURL = NSURL(string: url)
let task = NSURLSession.sharedSession().dataTaskWithURL(nsURL!){(data , repsonse , error) in
//var error:NSError?
do {
let response = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers) as! NSArray
callback(response)
} catch {
print(error)
}
}
task.resume()
}
}
settings :
import Foundation
class Settings {
var viewPost = "http://jsonplaceholder.typicode.com/posts"
}
post:
class Post {
var _userId:Int
var _title: String
init(userid:Int , title:String){
self._userId = userid
self._title = title
}
func toJSON() ->String{
return ""
}
}
and tableViewController:
class TableViewController: UITableViewController {
var postsCollection = [Post]()
var service:PostService!
override func viewDidLoad() {
super.viewDidLoad()
service = PostService()
service.getPosts{
(response) in
self.loadPosts(response[" "] as! NSArray )
}
}
func loadPosts(posts:NSArray){
for post in posts{
var userId = post["userId"] as! Int
var title = post["title"] as! String
let postObj = Post(userid: userId, title: title)
postsCollection.append(postObj)
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return postsCollection.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let post = postsCollection[indexPath.row] as NSData
cell.textLabel?.text = post._userId
cell.detailTextLabel = post._title
return cell
}
why error Cannot subscript a value of type '[Post]' in line
I'm not sure that this is the issue but it is an issue.
Since postsCollection has a distinct type there is no need to cast the type.
NSData is the wrong type anyway.
let post = postsCollection[indexPath.row] // the compiler infers to Post
Side note: Put the dispatch block to reload the table view after the repeat loop to create the Post instances. Reloading the table view in each iteration of the loop is very expensive and not necessary.
for post in posts {
...
}
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
And (public) properties starting with a underscore are pretty unusual (in Swift).
PS: There are two further issues
cell.textLabel?.text = String(post._userId)
cell.detailTextLabel?.text = post._title
Edit: Simple working solution, there are only two classes
class Post
class Post : CustomStringConvertible {
var userId:Int
var title: String
init(userid:Int , title:String){
self.userId = userid
self.title = title
}
var description : String { return String(userId) }
}
class TableViewController
class TableViewController: UITableViewController {
var postsCollection = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://jsonplaceholder.typicode.com/posts")
NSURLSession.sharedSession().dataTaskWithURL(url!){ [unowned self] (data , repsonse , error) in
if error != nil {
print(error!)
} else {
do {
let posts = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers) as! [[String:AnyObject]]
for post in posts {
let postObj = Post(userid: post["userId"] as! Int, title: post["title"] as! String)
self.postsCollection.append(postObj)
}
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
} catch let error as NSError {
print(error)
}
}
}.resume()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 }
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return postsCollection.count }
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let post = postsCollection[indexPath.row]
cell.textLabel!.text = String(post.userId)
cell.detailTextLabel!.text = post.title
return cell
}
}

Resources