pretty new at swift. Currently, I have a program where on the click of a button I make an NSURL request, take that response, parse the XML, and store the data I need into a array[String]. Pressing this button also serves as a segue into a table view controller which will be dynamically populated with the info from the array.
What I'm seeing is that the segue is occurring and the next VC is being passed an empty array before my request is even complete and response is parsed. (this is all based on the print statements I've inserted in just about every other line to track the execution of my code).
How can I go about this so that the segue does not take me to the next VC until my response has been parsed?
class start: UIViewController, NSURLConnectionDelegate, NSXMLParserDelegate {
var timeArr:[String] = []
var url = "myurl.com"
#IBOutlet weak var get2: UIButton!
#IBAction func getAction(sender: UIButton) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
httpGet (request) {
(data, error) -> Void in
if error != nil {
print(error)
mainInstance.arrivalArr.append("no arrival times")
} else {
self.beginParsing()
print(data)
}
}
}
func httpGet(request: NSURLRequest!, callback: (String, String?) -> Void) {
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback("", error!.localizedDescription)
} else {
var result = NSString(data: data!, encoding:
NSUTF8StringEncoding)!
self.data2 = data!
callback(result as String, nil)
}
}
task.resume()
}
func beginParsing()
{
posts = []
parser = NSXMLParser(data: data2)
parser.delegate = self
parser.parse()
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String])
{
element = elementName
if (elementName as NSString).isEqualToString("prd")
{
// more code
}
}
func parser(parser: NSXMLParser, foundCharacters string: String)
{
if element.isEqualToString("prdtm") {
//code
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?)
{
if (elementName as NSString).isEqualToString("prd") {
if !title1.isEqual(nil) {
//code
}
//code
finalTimeStr = getTime(finalTimeStr)
timeArr.append(finalTimeStr)
}
}
func getTime (time: String) -> String{
var arrivalTime = time
//code to trim and format time here
var timeA = getTimeDiff(arrivalTime)
timeA = trimToMin(timeA)
return timeA
}
func trimToMin (timeToTrim : String) -> String {
var timeInMin = timeToTrim
//code to trim to minutes
return substring2
}
func getTimeDiff (time: String) -> String{
//code to find time difference
let ret = stringFromTimeInterval(diff)
return ret
}
func stringFromTimeInterval(interval: NSTimeInterval) -> String {
//code to format interval
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let CDview = segue.destinationViewController as? ListView {
CDview.timeArr = timeArr
//CDview.timeArr = mainInstance.arrivalArr
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
timeArr = testArr
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Not sure where I went wrong. It's particularly confusing because if I use hardcoded strings and jump straight into getTimeDiff then it works perfectly fine and my table view on the next VC also looks great. Would appreciate any help. I've literally moved this code around for the last 12 hours trying to understand why it's not working.
Add a segue from view controller ( and not from UIButton) in storyboard . And name it as you want ( storyboard identifier ) . Perform the segue programmatically with performSegue(...) after you parsed the data .
Related
I'm trying to parse XML with XMLParser and put it on a PickerView. This is my code:
Main code:
import UIKit
class ViewController: UIViewController, XMLParserDelegate {
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var Image: UIImageView!
#IBOutlet weak var Button: UIButton!
var arrayCategorias = [Categories]()
var parser = XMLParser()
override func viewDidLoad() {
super.viewDidLoad()
let urlString = URL(string: "http://thecatapi.com/api/categories/list")
self.parser = XMLParser(contentsOf: urlString!)!
let success:Bool = self.parser.parse()
parser.delegate = self
if success {
print("success")
} else {
print("parse failure!")
}
print(arrayCategorias.count)
}
#IBAction func botonPulsado(_ sender: Any) {
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if(elementName=="category")
{
let categoria = Categories()
for string in attributeDict {
let strvalue = string.value as NSString
switch string.key {
case "id":
categoria.id = strvalue.integerValue
break
case "name":
categoria.name = strvalue as String
break
default:
break
}
}
arrayCategorias.append(categoria)
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print("failure error: ", parseError)
}
}
}
Class Categories Code:
import Foundation
class Categories{
var id: Int = 0
var name: String = ""
}
I don't know exactly what the problem is, but when I try to read arrayCategorias() just to see if there is any data, it just shows me 0. And where I catch the error, it shows me: "parse failure".
If anyone can help me, I'm just an android programmer and I don't know why it doesn't work.
I need help with putting the data on the PickerView.
There are a whole bunch of issues:
You have set your delegate after you parsed, whereas you must do this before you call parse().
You have some of your delegate methods implemented inside didStartElement. These need to be top level methods of your parser delegate.
Minor unrelated issue, but your class name of Categories isn't quite right, because it represents a single "category", not many. So I'd rename this to be Category. (I'd personally also make it a struct, but that's up to you.)
Your id and name values are not attributes of the category element. They are their own elements, so you have to parse them separately (using foundCharacters and build the Category object on didEndElement of category.
The id of the categories appears to be an integer, so I'd make the id of Category an Int.
You're calling parse() of a XMLParser that is using a URL from the main thread. That's inadvisable because you're blocking the main thread while it does the request. Personally, I'd use URLSession to request the data asynchronously, process it on a background queue, and only dispatch the final update of the model object and UI update to the main queue.
Personally, I'd adopt asynchronous patterns myself with #escaping completion handlers, to help isolate UI updates that I trigger after a parse from the parsing itself
A stylistic matter, but I wouldn't put all of this XMLParserDelegate code in the view controller. At the very least, put it in an extension so it's nicely organized. Even better, define a stand-alone parser delegate object in the spirit of the single responsibility principle and ensuring that you're not accidentally updating view controller model objects while the UI might be referring to it. It more safely ensures thread-safety and keeps your code more nicely encapsulated.
Pulling this all together, you could do something like:
struct Category {
let id: Int
let name: String
}
class ViewController: UIViewController {
#IBOutlet weak var pickerView: UIPickerView!
var categories = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
startRequestAndParse() { categories, error in
guard let categories = categories, error == nil else {
print(error?.localizedDescription ?? "Unknown error")
return
}
// if you got here, everything is OK, so update model and UI on main thread
DispatchQueue.main.async {
self.categories = categories
print(self.categories)
// trigger whatever UI update you want here, too;
self.pickerView.reloadAllComponents()
}
}
}
/// Initiate request from server and parse results
///
/// - Parameters:
/// - completion: This is called when the request/parsing is done. This may be called
/// on background thread. If parsing failed, the array of categories
/// will be `nil` and we should have `error`.
/// - categories: First parameter of the `completion` closure is the array of `Category` objects, or `nil` on error.
/// - error: Second parameter of the `completion` closure is the resulting `Error`, if any.
private func startRequestAndParse(completion: #escaping (_ categories: [Category]?, _ error: Error?) -> Void) {
let url = URL(string: "http://thecatapi.com/api/categories/list")!
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil, error)
return
}
// ok, now parse
let parser = XMLParser(data: data)
let delegate = ParserDelegate()
parser.delegate = delegate
parser.parse()
completion(delegate.categories, parser.parserError)
}
task.resume()
}
}
// this assumes you set the picker's `delegate` to be the view controller (either in IB or programmatically in `viewDidLoad`
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return categories[row].name
}
}
// this assumes you set the picker's `dataSource` to be the view controller (either in IB or programmatically in `viewDidLoad`
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return categories.count
}
}
/// Parser delegate for categories
class ParserDelegate: NSObject, XMLParserDelegate {
private var id: Int?
private var name: String?
private var value: String?
var categories: [Category]?
// initialize `categories`
func parserDidStartDocument(_ parser: XMLParser) {
categories = []
}
// if `id` or `name`, initialize `value` so we'll capture the appropriate value
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
if elementName == "id" || elementName == "name" {
value = ""
}
}
// if `value` is not `nil`, go ahead and concatenate the additional characters
func parser(_ parser: XMLParser, foundCharacters string: String) {
value? += string
}
// if `id` or `name`, update the appropriate property
// if `category`, build a `Category` object and add it to our array
// regardless, reset `value` to `nil`
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
switch elementName {
case "id":
if let value = value {
id = Int(value)
}
case "name":
name = value
case "category":
if let id = self.id, let name = self.name {
categories!.append(Category(id: id, name: name))
}
id = nil
name = nil
default:
()
}
value = nil
}
// if any error, reset `categories` so caller knows there was an issue
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
categories = nil
}
}
I am having an issue in which I am parsing a String value from an API and I am trying to assign it to an UILabel. I have tested updating the label in the exact same position as it is now by setting the label (lblNamedata) to a String using the format:
self.lblNameData.text = "hello"
and it has worked fine.
I currently have:
if success {
print("parse success!")
print(strXMLData)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.lblNameData.text=self.strXMLData
})
} else {
print("parse failure!")
}
and this isn't working.
Below is my complete current code:
import UIKit
class ViewController: UIViewController,NSXMLParserDelegate {
var strXMLData:String = ""
var currentElement:String = ""
var passData:Bool=false
var passName:Bool=false
var parser = NSXMLParser()
#IBOutlet weak var lblNameData: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let url:String="http://www.stands4.com/services/v2/quotes.php?uid=idhere&tokenid=tokenhere&searchtype=xxxx&query=xxxx"
let urlToSend: NSURL = NSURL(string: url)!
// Parse the XML
parser = NSXMLParser(contentsOfURL: urlToSend)!
parser.delegate = self
let success:Bool = parser.parse()
if success {
print("parse success!")
print(strXMLData)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.lblNameData.text=self.strXMLData
})
} else {
print("parse failure!")
}
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
currentElement=elementName;
if(elementName=="quote" || elementName=="author" || elementName=="result")
{
if(elementName=="quote"){
passName=true;
}
passData=true;
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
currentElement="";
if(elementName=="quote" || elementName=="author" || elementName=="result")
{
if(elementName=="quote"){
passName=false;
}
passData=false;
}
}
func parser(parser: NSXMLParser, foundCharacters string: String) {
if(passName){
strXMLData=strXMLData+"\n\n"+string
}
if(passData)
{
// print(string)
print("work")
}
}
func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
NSLog("failure error: %#", parseError)
}
}
UPDATE: So I believe I found the error: in my parser function I'm setting strXMLData = strXMLData + "\n\n" + string but after I delete the \n\n I can see that my label updates...why is this?
Alright, so after many instances of trial and error, I think I figured out the answer.
Something was up in my Main.storyboard that didn't like me creating multiple lines in the UILabel, so what I did was click on the label and navigated to the Attributes Inspector. I set the "Lines" parameter to 0 and I was able to see my text!
So I've just updated my swift to 2.0 and obviously a couple of errors appeared in my code.
Thats what I have right now.
unc loadRssFeed(data: NSURL) {
var myRssParser : ParserManager = ParserManager.alloc().initWithURL(data) as! ParserManager
myRssFeed = myRssParser.feeds
tableView.reloadData()
}
Fixit suggests to change 'var' to 'let' and use object initializer instead of alloc(). But the problem is I've never used it before (granted my Swift experience is like four weeks).
Should I follow fixit instruction?
And how do I fix the alloc() problem.
Thanks!
Updated wit parser code:
class ParserManager: NSObject, NSXMLParserDelegate {
var parser = NSXMLParser()
var feeds = NSMutableArray()
var elements = NSMutableDictionary()
var element = NSString()
var ftitle = NSMutableString()
var link = NSMutableString()
var fdescription = NSMutableString()
var fdate = NSMutableString()
func initWithURL(url :NSURL) -> AnyObject {
startParse(url)
return self
}
func startParse(url :NSURL) {
feeds = []
parser = NSXMLParser(contentsOfURL: url)!
parser.delegate = self
parser.shouldProcessNamespaces = false
parser.shouldReportNamespacePrefixes = false
parser.shouldResolveExternalEntities = false
parser.parse()
}
func allFeeds() -> NSMutableArray {
return feeds
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
self.element = elementName
if self.element == "item" {
self.ftitle = ""
self.fdescription = ""
self.fdate = ""
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if (elementName as NSString).isEqualToString("item") {
if ftitle != "" {
elements.setObject(ftitle, forKey: "title")
}
if fdescription != "" {
elements.setObject(fdescription, forKey: "description")
}
if fdate != "" {
elements.setObject(fdate, forKey: "pubDate")
}
feeds.addObject(elements)
}
}
func parser(parser: NSXMLParser, foundCharacters string: String?) {
if element.isEqualToString("title") {
ftitle.appendString(string!)
}else if element.isEqualToString("description") {
fdescription.appendString(string!)
}else if element.isEqualToString("pubDate") {
fdate.appendString(string!)
}
}
}
The fix should look like this:
let myRssParser = ParserManager(URL: data) as! ParserManager
Or maybe like this, depends upon how ParserManager has been written:
let myRssParser = ParserManager(url: data) as! ParserManager
There's even a possibility that you don't have to use the argument name:
let myRssParser = ParserManager(data) as! ParserManager
As discussed above, you should not call alloc and init... methods directly. Furthermore, the ParserManager class should not have a initWithURL method, but rather an init method with a URL parameter. The existing code is appearing to follow Objective-C patterns, but Swift has its own conventions.
But more broadly, I would advise against performing synchronous network requests, and when you're calling NSXMLParser(contentsOfURL:), that's what's happening. It's better to request data asynchronously, and then refactor the code to follow asynchronous patterns (e.g. completionHandler closures). For example:
class ParserManager: NSObject, NSXMLParserDelegate {
/// The NSMutableArray used internally by this class
private var feeds = NSMutableArray()
/// Initiate parsing asynchronously; note, I'm returning the `NSURLSessionTask` in case you want to cancel it at some later point
func parse(URL: NSURL, completionHandler: (NSMutableArray?, NSError?)->()) -> NSURLSessionTask {
let task = NSURLSession.sharedSession().dataTaskWithURL(URL) { data, response, error in
guard data != nil else {
dispatch_async(dispatch_get_main_queue()) {
completionHandler(nil, error)
}
return
}
let parser = NSXMLParser(data: data!)
parser.delegate = self
if parser.parse() {
dispatch_async(dispatch_get_main_queue()) {
completionHandler(self.feeds, nil)
}
} else {
dispatch_async(dispatch_get_main_queue()) {
completionHandler(nil, parser.parserError)
}
}
}
task.resume()
return task
}
// NSXMLParserDelegate methods implemented here
}
Then, you can use the completion handler syntax to specify what you want to do when the network request and parsing is done:
let URL = NSURL(string: "...")
let myRssParser = ParserManager()
myRssParser.parse(URL) { feeds, error in
guard feeds != nil else {
print(error)
return
}
// use `feeds` here, e.g.
self.feeds = feeds // update your local property
self.tableView.reloadData() // and reload the table
}
// but don't use `feeds` here, since it won't be done by the time we get here
I have a web service which give xml response. I want to parse and show in table. But my XMLParser delegate not called. I am newer in swift. Please help any help would be apperciated,
class ViewController: UIViewController,NSXMLParserDelegate,UITableViewDelegate {
var element:String?
var titles:NSMutableString?
var link:NSMutableString?
var tableData:NSMutableArray?
var dict:NSMutableDictionary?
#IBOutlet weak var table: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
dict = NSMutableDictionary()
tableData = NSMutableArray()
let url = NSURL(string: "hp?keytext=s")
let theRequest = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(theRequest, queue: nil, completionHandler: {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
if data.length > 0 && error == nil {
//var parser=NSXMLParser(data: data)
var parser = NSXMLParser(contentsOfURL: url)
parser.delegate=self
parser.shouldResolveExternalEntities=false
parser.parse()
}
})
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return tableData!.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell
if !(cell != nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")}
var a:NSString
var b:String
a = tableData?.objectAtIndex(indexPath.row).objectForKey("title") as NSString
b = tableData?.objectAtIndex(indexPath.row).objectForKey("city") as NSString
cell?.textLabel?.text=a;
cell?.detailTextLabel?.text=b
return cell
}
func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat
{
return 78;
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String, qualifiedName qName: String, attributes attributeDict: [NSObject : AnyObject]) {
element=elementName
if element=="restaurant"
{
titles=NSMutableString()
link=NSMutableString()
}
}
func parser(parser: NSXMLParser, foundCharacters string: String) {
if element=="title"
{
titles?.appendString(string)
}
else if element=="city"
{
link!.appendString(string)
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String, qualifiedName qName: String) {
if elementName == "restaurant"
{
dict?.setValue(titles, forKeyPath: "title")
dict?.setValue(link, forKeyPath: "city")
tableData?.addObject(dict!)
}
}
func parserDidEndDocument(parser: NSXMLParser!){
table?.reloadData()
}
}
You don't need to use sendAsynchronousRequest in your viewDidLoad method instead of that use this code:
Declare parser variable outside of all function.
Then in your viewDidLoad method replace your code with this code:
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://images.apple.com/main/rss/hotnews/hotnews.rss") //this is example URL
parser = NSXMLParser(contentsOfURL: url)!
parser.delegate = self
parser.parse()
}
And your full code will be:
import UIKit
class ViewController: UIViewController,NSXMLParserDelegate,UITableViewDelegate, UITableViewDataSource { //You will need UITableViewDataSource here.
var parser : NSXMLParser = NSXMLParser()
var element:String?
var titles:NSMutableString?
var link:NSMutableString?
var tableData:NSMutableArray?
var dict:NSMutableDictionary?
#IBOutlet weak var table: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://images.apple.com/main/rss/hotnews/hotnews.rss")
parser = NSXMLParser(contentsOfURL: url)!
parser.delegate = self
parser.parse()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return tableData!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell
if !(cell != nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")}
var a:NSString
var b:String
a = tableData?.objectAtIndex(indexPath.row).objectForKey("title") as! NSString
b = tableData?.objectAtIndex(indexPath.row).objectForKey("city") as! NSString as String
cell?.textLabel?.text = a as String;
cell?.detailTextLabel?.text = b
return cell!
}
func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat
{
return 78;
}
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject]) {
element=elementName
if element=="restaurant"
{
titles=NSMutableString()
link=NSMutableString()
}
}
func parser(parser: NSXMLParser, foundCharacters string: String?) {
if element=="title"
{
titles?.appendString(string!)
}
else if element=="city"
{
link!.appendString(string!)
}
}
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "restaurant"
{
dict?.setValue(titles, forKeyPath: "title")
dict?.setValue(link, forKeyPath: "city")
tableData?.addObject(dict!)
}
}
func parserDidEndDocument(parser: NSXMLParser){
table?.reloadData()
}
}
I have updated your delegate functions too.
And check that you are getting data from your URL or not.
And I suggest you to follow THIS tutorial first which will help you to understand everything.
First thing your url is not valid url, you are passing only this string as a URL "hp?keytext=s".
Second thing put this method it will call when your parse fail.
func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
NSLog("failure error: %#", parseError)
}
I am totally new to SWIFT and to developing in general. I am currently building an RSS news feed to hone my (very limited) skills. I am basically parsing an RSS feed from a random news channel and then storing the titles in a simple table view.
I am trying to implement UIPullToRefresh using SWIFT, and truth be told I am kinda stuck! I have written the following code. As you can see I have added the necessary code to my viewDidLoad method. At this point the refresh spinner works well but I am now wondering what code is needed in my "refresh function" to actually refresh my table view.
I know this may seem trivial to most of you guys, but i am fairly new to this and i have been pulling my hair for the last 12 hours ... so anything would probably help at this point.
Thanks for your help!
import UIKit
class BRTableViewController: UITableViewController, NSXMLParserDelegate {
var parser: NSXMLParser = NSXMLParser()
var blogPosts: [BlogPost] = []
var postTitle: String = String()
var postLink: String = String()
var postDate: String = String()
var eName: String = String()
override func viewDidLoad() {
super.viewDidLoad()
let url:NSURL = NSURL(string: "http://rt.com/rss/")!
parser = NSXMLParser(contentsOfURL: url)!
parser.delegate = self
parser.parse()
// pull to refresh the table view
refreshControl!.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl!.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl!)
}
func refresh(sender:AnyObject){
let url:NSURL = NSURL(string: "http://rt.com/rss/")!
parser = NSXMLParser(contentsOfURL: url)!
parser.delegate = self
parser.parse()
}
// MARK: - NSXMLParserDelegate methods
func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {
eName = elementName
if elementName == "item" {
postTitle = String()
postLink = String()
postDate = String()
}
}
func parser(parser: NSXMLParser!, foundCharacters string: String!) {
let data = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
if (!data.isEmpty) {
if eName == "title" {
postTitle += data
} else if eName == "link" {
postLink += data
} else if eName == "pubDate" {
postDate += data
}
}
}
func parser(parser: NSXMLParser!, didEndElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!) {
if elementName == "item" {
let blogPost: BlogPost = BlogPost()
blogPost.postTitle = postTitle
blogPost.postLink = postLink
blogPost.postDate = postDate
blogPosts.append(blogPost)
}
}
override func parser(parser: NSXMLParser!, didEndElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!) {
if elementName == "item" {
self.tableView.reloadData();
self.refreshControl.endRefreshing();
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return blogPosts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let blogPost: BlogPost = blogPosts[indexPath.row]
cell.textLabel.text = blogPost.postTitle
cell.detailTextLabel?.text = blogPost.postDate.substringToIndex(advance(blogPost.postDate.startIndex, 25))
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 50.0
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "viewpost" {
let selectedRow = tableView.indexPathForSelectedRow()?.row
let blogPost: BlogPost = blogPosts[selectedRow!]
let viewController = segue.destinationViewController as PostViewController
viewController.postLink = blogPost.postLink
}
}
}
I think this fix the issue:
func refresh(sender:AnyObject){
parser = NSXMLParser(contentsOfURL: url)!
parser.delegate = self
parser.parse()
}
This will re-parse the XML and update the blogPosts variable.
Remove the following line in viewDidLoad:
refreshControl = UIRefreshControl()
You should also override the parserDidEndDocument function to include this:
func parserDidEndDocument(parser: NSXMLParser!){
self.tableView.reloadData();
self.refreshControl?.endRefreshing();
}
This will refresh the table view once the parser is done.