Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop MBProgressHUD and add a subview when the server is not returning data

Tags:

json

ios

swift

api

In my app I have this class to get data from my server:

class Api{

func loadOffers(completion:(([Offers])-> Void), offer_id: String, offerStatus:String){


    let myUrl = NSURL(string: "http://www.myServer.php/api/v1.0/offers.php")
    let request = NSMutableURLRequest(URL: myUrl!)
    request.HTTPMethod = "POST"
    let postString = "offer_id=\(offer_id)&offerStatus=\(dealStatus)&action=show"
    request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
        { data, response, error in              
            if error != nil {                  
                println("error\(error)")                 
            }else{   
               var err:NSError?

               let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)

                if let dict = jsonObject as? [String: AnyObject] {

                if let myOffers = dict["offers"] as? [AnyObject] {

                var offers = [Offers]()

                for offer in myOffers{

                let offer = Offers(dictionary: offer as! NSDictionary)

                offers.append(offer)                       

                let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
                                    dispatch_async(dispatch_get_global_queue(priority, 0 )){
                                                                                   dispatch_async(dispatch_get_main_queue()){

                completion(offers)

                             }         
                           }                                
                        }
                    }
                }                
            }            
      }
    task.resume()       
   }
 }

then in my View Controller I load the model:

    class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {


    var offers: [Offers]!

    func loadModel() {
    let loadingNotification = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
    loadingNotification.mode = MBProgressHUDMode.Indeterminate
    loadingNotification.labelText = "updating your offers..."
    offers = [Offers]()
    let api = Api()
    api.loadOffers(didLoadOffers , offer_id: dealIdent!, offerStatus: "open")

    }

    func didLoadOffers(offers:[Offers]){

    self.offers = offers
    self.tableView.reloadData()
    MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
    self.refreshControl.endRefreshing()
    }

    override func viewWillAppear(animated: Bool) {

    loadModel()

    }
 }

Everything works except that when the JSON dictionary is empty, meaning that there no offers the MBProgressHUD keep spinning.enter image description here

I would like stop the activity indicator adding a subview instead which says that there are no offers. Any Suggestion would be greatly appreciated.

I tried:

if offers.isEmpty{ MBProgressHUD.hideAllHUDsForView(self.view, animated: true) }

and also

if offers == 0 { MBProgressHUD.hideAllHUDsForView(self.view, animated: true) }

but it's not working

Thanks

like image 550
Mat Avatar asked May 10 '15 12:05

Mat


2 Answers

This is happening because you set the HUD in main queue but are trying to remove from another one. All UI related changes should be done in main_queue()

Try using this code

dispatch_async(dispatch_get_main_queue(), { 

  // your code to modify HUD here

});
like image 90
Penkey Suresh Avatar answered Oct 12 '22 02:10

Penkey Suresh


I recommend small redesign of code. I modified your original code a little. Here is API class:

class Api {

func loadOffers(offerID : String, offerStatus : String, completionHandler : ([Offer]?, NSError?) -> Void) {
    let myURL = NSURL(string: "http://www.myServer.php/api/v1.0/offers.php")!
    var request = NSMutableURLRequest(URL: myURL)
    request.HTTPMethod = "POST"
    let postString = "offer_id=\(offerID)&offerStatus=\(offerStatus)&action=show"
    request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)

    NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (result : NSData!, response : NSURLResponse!, error : NSError!) -> Void in
        if let existingError = error {
            NSLog("error \(existingError.code) - \(existingError.localizedDescription)")
            completionHandler(nil, existingError)
        } else {
            var parseError : NSError?

            if let dictionary = NSJSONSerialization.JSONObjectWithData(result, options: .allZeros, error: nil) as? [String : AnyObject] {
                if let myOffers = dictionary["offers"] as? [NSDictionary] {
                    var parsedOffers = [] as [Offer]
                    for jsonOffer in myOffers {
                        parsedOffers.append(Offer(dictionary: jsonOffer))
                    }
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
                        completionHandler(parsedOffers, nil)
                    }
                }
            } else {
                NSLog("JSON parsing failed")
                parseError = NSError(domain: "MyApp", code: 1, userInfo : nil)
                completionHandler(nil, parseError)
            }
        }
    }).resume()
}

}

Change is added calling of completion handler even in case error with network communication and in case of failure of JSON parsing.

Corresponding implementation of VC follows:

class ViewController: UITableViewController {

private var _offers : [Offer]?

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    loadModel()
}

private func loadModel() {
    var hud = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
    hud.mode = MBProgressHUDMode.Indeterminate
    hud.labelText = "updating your offers"
    Api().loadOffers("1", offerStatus: "1") { (offers : [Offer]?, error : NSError?) -> Void in
        self._offers = offers

        // TODO: Correct handling of error state ;)

        dispatch_async(dispatch_get_main_queue()) {
            self.tableView.reloadData()
            hud.hide(true)
        }
    }
}

}

From my point of view is using closure better, but depends on your implementation. You can use your implementation with method instead of closure and just define hud variable on instance level.

I hope it helps you to solve your problem (I've tested on simulator and iPhone and works well with some testing stub framework).

like image 44
Kasik Avatar answered Oct 12 '22 02:10

Kasik