简体   繁体   English

在Swift 3中等待在其他类文件中进行JSON解析

[英]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. 我创建了一个类,如下面的代码所示,您可以看到我正在viewController之外的类中解析一个JSON文件。

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. 当我在视图控制器中创建AllCards对象时,显然在开始时返回0,但过一会儿它返回正确数量的卡。

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? 1)如何在viewDidLoad之前等待对象创建,以便在视图加载后,AllCard对象将返回正确数量的卡?

2) If I add a button in the viewController updating the number of cards it freezes until all the cards have been created. 2)如果我在viewController中添加一个按钮来更新卡片数量,它将冻结直到创建所有卡片。 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? 3)像我一样在单独的类中解析JSON是一个好习惯吗?

AllCards class: AllCards类:

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 首先,您必须在单个类中编写一个函数,例如: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. 1)如果通过UIStoryboard segue传递对象,则在调用viewDidLoad()之前设置该对象。 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. 但是,如果要等待UI元素准备就绪,通常我会在UI元素上使用didSet ,则可以添加guard语句,如果需要的话可以在其中检查对象。

2) first of all you'll probably want a closure so maybe read 3) first. 2)首先,您可能需要关闭,因此请先阅读3)。 you're using dispatchGroup.enter() here, DispatchQueue.global.async { } is the usual way to accomplish what you're doing. 如果您在此处使用dispatchGroup.enter() ,则DispatchQueue.global.async { }是完成您正在执行的操作的常用方法。 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. 如果需要,可以在完成后添加DispatchQueue.main.async { } ,或者真正地由您自己决定进入视图控制器的主线程。 Look into the differences between [unowned self] and [weak self] when you have the time. 有空的时候看看[unowned self][weak self]之间的区别。

3) Give your Card object an init(from: JSON) initialiser where it parses its properties from the JSON object you're passing it. 3)给您的Card对象一个init(from: JSON)初始化程序,在其中初始化您要传递的JSON对象的属性。 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?) -> ())? 让负责下载的函数(在您的情况下为Alamofire)生活在另一个类中(例如APIClient ),并为它提供一个下载函数,该函数在参数列表中带有闭包,例如completion: ((JSON?) -> ())? to return the JSON object. 返回JSON对象。 Let that class download a JSON object and then initialise your Card object with the init(from: JSON) initializer you wrote earlier. 让该类下载一个JSON对象,然后使用您先前编写的init(from: JSON)初始化程序初始化Card对象。 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. 请注意,这不是适合与Core Data NSManagedObjects一起使用的方法,因此,如果您需要本地存储,请记住这一点。

In the end you should be able to build an array of Cards something like this: 最后,您应该能够构建如下的Cards数组:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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