2

Inside my completion handler I am trying to return the http response code much like alert in JS. Once I get this sorted I hope to improve my if statement to identify a connection problem to the end user. I was looking at this article.

http://www.ioscreator.com/tutorials/display-an-alert-view-in-ios8-with-swift

I have also seen this answer Simple App Delegate method to show an UIAlertController (in Swift) but I get a similar method about the window not being named.

My code without all the noise is like this:

class NewsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

var tableView: UITableView!
var titleItems: NSMutableArray = []
var descritpionItems: NSMutableArray = []

class RemoteAPI {

    // base url
    let baseUrl = "http://api.dev/web/"
    // returned array of news titles + descriptions
    var newsTitle: NSMutableArray = []
    var newsDescription: NSMutableArray = []

    func getData(completionHandler: ((NSArray?, NSError?) -> Void)) -> Void {
        // get news feed url
        let url = NSURL(string: baseUrl + "news")
        // create session
        let session = NSURLSession.sharedSession()
        // set task
        let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in

            // if error isn't nil return error to getData
            if (error != nil) {
                return completionHandler(nil, error)
            }

            // check response
           if let httpResponse = response as? NSHTTPURLResponse {
                // if response doesn't equal 200 throw an alert
                 if httpResponse.statusCode != 200 {
                let alertController = UIAlertController(title: "Oh No!!", message: "Connection error",preferredStyle: UIAlertControllerStyle.Alert)

                alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))

                UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)

                } else { // no error continue

            // convert data object to json
            let json = JSON(data: data)

            for var i = 0; i < json.count; ++i {
                // get news title from json object
                var title = json[i]["title"].string
                var blurb = json[i]["description"].string
                // add to dictionary
                self.newsTitle.addObject(title!)
                self.newsDescription.addObject(blurb!)
            }

            if (error != nil) {
                return completionHandler(nil, error)
            } else {
                return completionHandler([self.newsTitle,self.newsDescription], nil)
            }
        }
    }
    })
        task.resume()
    }
}

// get data
var api = RemoteAPI()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    self.view.frame = CGRect(x: 5, y: 75, width: 365, height: 480)
    self.tableView = UITableView(frame:self.view!.frame)
    self.tableView!.delegate = self
    self.tableView!.dataSource = self
    self.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
    self.view?.addSubview(self.tableView)

    api.getData({data, error -> Void in
        if (data != nil) {
            self.titleItems = self.api.newsTitle
            self.descritpionItems = self.api.newsDescription
            self.tableView!.reloadData()
        } else {
            println("API failed :-(")
            println(error)
        }
    })
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{

    return self.titleItems.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

    var cell:UITableViewCell=UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "mycell")

    if let newsTitle = self.titleItems[indexPath.row] as? NSString {
        cell.textLabel?.text = newsTitle
    }
    if let newsDesc = self.descritpionItems[indexPath.row] as? NSString {
        cell.detailTextLabel?.text = newsDesc
    }
    return cell
}

}

I get an error of NewsViewController.RemoteAPI does not have member named presentViewController. I understand why, just not sure how to call this within the class being new to iOS

Community
  • 1
  • 1
Jonnny
  • 4,939
  • 11
  • 63
  • 93
  • Several things. You say: "ViewController does not have member named presentViewController". Later in a discussion you say the code is run from a class called `RemoteAPI`. Wouldn't the error read "RemoteAPI does not have member named presentViewController." If you're going to quote an error, quote it EXACTLY, letter for letter. The details matter. A lot. – Duncan C Mar 28 '15 at 22:58
  • Next, you say "... When I run this code I get the above method." You use the same term for an error message a few lines later. You get an error, not a method. A method is a named block of code defined in a class. Please learn and use the correct terminology. It makes it much easier to understand what you are talking about. – Duncan C Mar 28 '15 at 23:02
  • Possible duplicate of [How to present UIAlertController when not in a view controller?](http://stackoverflow.com/questions/26554894/how-to-present-uialertcontroller-when-not-in-a-view-controller) – Belle Jan 25 '16 at 13:58

4 Answers4

12

In my situation here, the following worked:

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
Jonnny
  • 4,939
  • 11
  • 63
  • 93
  • Although this works, it shows you haven't considered a good design for your application. You shouldn't rely on calling back to the appDelegate just to get the rootViewController. Perhaps if you posted more of your code we could assist. – Tim Mar 29 '15 at 17:12
3
self.presentViewController(alertController, animated: true, completion: nil)

You missed external name of second parameter.

Move your alert code inside api call completion closure

api.getData({data, error -> Void in
    if (data != nil) {
        self.titleItems = self.api.newsTitle
        self.descritpionItems = self.api.newsDescription
        self.tableView!.reloadData()
    } else {
        println("API failed :-(")
        println(error)
        // here
    }
}
mustafa
  • 15,254
  • 10
  • 48
  • 57
  • What is the type of self here ? – mustafa Mar 28 '15 at 22:47
  • The call is within a class called RemoteAPI which is within my NewsViewController which of UIViewController. – Jonnny Mar 28 '15 at 22:49
  • So the type of self is RemoteAPI then. presentViewController method defined inside UIViewController. You can't call it on another type. You need to find a way to call it on NewsViewController. May be showing a bit more code will help to solve the problem – mustafa Mar 28 '15 at 22:53
  • Ok, think about what you said. The call is within a class called RemoteAPI. So self is a RemoteAPI object. The `presentViewController:animated:completion:` method is a UIViewController method. Why would your RemoteAPI class have a `presentViewController:animated:completion:` method? – Duncan C Mar 28 '15 at 22:54
  • I have updated the code. I am very early into learning Swift and iOS. But I see your point – Jonnny Mar 28 '15 at 22:57
1

Your class RemoteAPI does not have a method called presentViewController(). presentViewController() is a method on a UIViewController. This is why you are getting the error.

To call presentViewController(), call back to a UIViewController instance when your HTTP response finishes.

Inside your else block of api.getData(), you can access the error object. Here you can create your alert controller and then present it. This block of code is called a callback, and your view controller should be responsible for creating the alert controller that indicates an error.

Tim
  • 8,932
  • 4
  • 43
  • 64
  • 1
    Thanks Jeff, would you be able to show me how to do that. As someone new to Swift and iOS i'm unfamiliar with it :-) – Jonnny Mar 29 '15 at 17:29
  • You would need to update your code, it's hard to know what the structure is. – Tim Mar 29 '15 at 20:27
0
if let appDelegate = UIApplication.shared.delegate, let window = appDelegate.window, let rootViewController = window?.rootViewController {
        
        var topViewController = rootViewController
        while topViewController.presentedViewController != nil {
            topViewController = topViewController.presentedViewController!
        }
        topViewController.present(yourViewController, animated: true, completion: nil)
        
    }
Murat Akdeniz
  • 424
  • 5
  • 7