简体   繁体   中英

Wait for JSON parsing in a different class file in swift 3

I created a class as shown in the code below, and as u can see I am parsing a JSON file in the class outside the viewController.

When I create the AllCards object in the view controller obviously return 0 at the beginning but after a while it returns the correct number of cards.

here my questions:

1) How can I wait the object creation before the viewDidLoad so at the view did load the AllCard object will return the correct number of cards?

2) If I add a button in the viewController updating the number of cards it freezes until all the cards have been created. I think because in my code everything is in the main queue. How can I resolve that?

3) Is it a good practice parsing JSON in a separate class like I did?

AllCards class:

import Foundation
import Alamofire
import SwiftyJSON

 class AllCards {

var allCard = [Card]()
let dispatchGroup = DispatchGroup()
//gzt the JSON with Alamofire request
let allCardsHTTP: String = "https://omgvamp-hearthstone-v1.p.mashape.com/cards?mashape"
init() {
    dispatchGroup.enter()
    Alamofire.request(allCardsHTTP, method: .get).responseJSON { (response) in
        if response.result.isSuccess {
           let jsonCards : JSON = JSON(response.value!)
            print("success")
            //create the cards
            if jsonCards["messagge"].stringValue != "" {
                print(jsonCards["message"].stringValue)
            }
            else {
                for (set, value) in jsonCards {
                    if jsonCards[set].count != 0 {
                       for i in 0...jsonCards[set].count - 1 {
                            let card = Card(id: jsonCards[set][i]["cardId"].stringValue, name: jsonCards[set][i]["name"].stringValue, cardSet: set, type: jsonCards[set][i]["type"].stringValue, faction: jsonCards[set][i]["faction"].stringValue, rarity: jsonCards[set][i]["rarity"].stringValue, cost: jsonCards[set][i]["cost"].intValue, attack: jsonCards[set][i]["attack"].intValue, durability: jsonCards[set][i]["durability"].intValue, text: jsonCards[set][i]["text"].stringValue, flavor: jsonCards[set][i]["flavor"].stringValue, artist: jsonCards[set][i]["artist"].stringValue, health: jsonCards[set][i]["health"].intValue, collectible: jsonCards[set][i]["collectible"].boolValue, playerClass: jsonCards[set][i]["playerClass"].stringValue, howToGet: jsonCards[set][i]["howToGet"].stringValue, howToGetGold: jsonCards[set][i]["howToGetGold"].stringValue, mechanics: [""], img: jsonCards[set][i]["img"].stringValue, imgGold: jsonCards[set][i]["imgGold"].stringValue, race: jsonCards[set][i]["race"].stringValue, elite: jsonCards[set][i]["elite"].boolValue, locale: jsonCards[set][i]["locale"].stringValue)
                            if jsonCards[set][i]["mechanics"].count > 0 {
                                 for n in 0...jsonCards[set][i]["mechanics"].count - 1 {
                                    card.mechanics.append(jsonCards[set][i]["mechanics"][n]["name"].stringValue)
                                }
                            }
                            else {
                                card.mechanics.append("")
                            }
                        self.allCard.append(card)
                        }
                    }
                    else {
                        print("The set \(set) has no cards")
                    }
                }
               print(self.allCard.count)
            }
        }
        else {
             print("No network")
         }
        self.dispatchGroup.leave()
    }
}
}

View Controller:

import UIKit

class ViewController: UIViewController {
let allcards = AllCards()
let mygroup = DispatchGroup()

@IBAction func updateBtn(_ sender: Any) {

    print(allcards.allCard.count) //Button is frozen until all the cards have been created then it shows the correct number of cards

}
override func viewDidLoad() {
    super.viewDidLoad()

   print(allcards.allCard.count) / This returns 0

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


}

Here is an example of completion handler. First you have to write a function in a single class ex: APICall

func getDataFromJson(allCardsHTTP: String, completion: @escaping (_ success: Any) -> Void) {

     Alamofire.request(allCardsHTTP, method: .get).responseJSON { response in
        if response.result.isSuccess {
              completion(response)
         }
     }
  }

and call this method from any class.

let callApi = APICall()
callApi.getDataFromJson(allCardsHTTP: "https://omgvamp-hearthstone-v1.p.mashape.com/cards?mashape",completion: { response in
     print(response)
 })

1) if you pass an object via a UIStoryboard segue, it's set before viewDidLoad() is called. However if you want to wait for UI elements to be ready, I generally go for a didSet on the UI element, you could add a guard statement checking your object in there if you want.

2) first of all you'll probably want a closure so maybe read 3) first. you're using dispatchGroup.enter() here, DispatchQueue.global.async { } is the usual way to accomplish what you're doing. Add in a DispatchQueue.main.async { } when done if you want, or dip into the main thread in the view controller, up to you really. Look into the differences between [unowned self] and [weak self] when you have the time.

3) Give your Card object an init(from: JSON) initialiser where it parses its properties from the JSON object you're passing it. Let the function responsible for the download (Alamofire in your case) live in a different class (like for example APIClient ) and give it a download function with a closure in the argument list like completion: ((JSON?) -> ())? to return the JSON object. Let that class download a JSON object and then initialise your Card object with the init(from: JSON) initializer you wrote earlier. Note that this isn't an approach fit for use with Core Data NSManagedObjects so if you'll need local storage maybe keep that in mind.

In the end you should be able to build an array of Cards something like this:

APIClient.shared.fetchCards(completion: { cardJSONs in
    let cards = [Card]()
    for cardJSON: JSON in cardJSONs {
        let card = Card(from; JSON)
        cards.append(card)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM