从按钮加载另一个故事板时故事板崩溃

storyboard crashes when loading another storyboard from a button

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    hideActivity()
    let products = response.products
    if products.count > 0{
        let controller = HAPurchaseViewController.instantiate() // crashes here
        controller.categories = dataManager.paidCategories()
        controller.products = products
        self.navigationController?.pushViewController(controller, animated: true)
    }
    else{
        DispatchQueue.main.async { [weak self] in
            let alertController = UIAlertController(title: "Oops!", message: "Unable to load or products not found, please try again later", preferredStyle: .alert)
            let okAction = UIAlertAction(title: "Ok", style: .default)
            alertController.addAction(okAction)
            self?.present(alertController, animated: true)
        }
    }
}

除了我使用它来加载我的 In App Purchase View Controller 时,此扩展在其他任何地方都有效。

import Foundation
import UIKit

protocol Storyboarded {
    static func instantiate() -> Self
}


extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        let id = String(describing: self)
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        if #available(iOS 13.0, *) {
            return storyboard.instantiateViewController(identifier: id) as! Self // App crash here when loading storyboard
        } else {
            return storyboard.instantiateViewController(withIdentifier: id) as! Self
        }
    }
    static func value() -> Self {
        return UIViewController() as! Self
    }
}


================================================================= 
Main Thread Checker: UI API called on a background thread: -[UIViewController initWithCoder:] > PID: 13525, TID: 4347723, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 0
Backtrace:

根据SO的建议,我更新了代码

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        DispatchQueue.main.async {
            let id = String(describing: self)
            let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
            if #available(iOS 13.0, *) {
                return storyboard.instantiateViewController(identifier: id) as! Self
            } else {
                return storyboard.instantiateViewController(withIdentifier: id) as! Self
            }
        }
    }
    static func value() -> Self {
        return UIViewController() as! Self
    }
}

问题出在这里:

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    // ... this is a background thread!
}

所以如果不进入主线程,此时你不能做任何涉及接口的事情。实际上,您已经在 second 部分这样做了:

hideActivity()
let products = response.products
if products.count > 0{
    let controller = HAPurchaseViewController.instantiate() 
    controller.categories = dataManager.paidCategories()
    controller.products = products
    self.navigationController?.pushViewController(controller, animated: true)
}
else{
    // LOOK! You are already doing it here
    DispatchQueue.main.async { [weak self] in
        let alertController = UIAlertController(title: "Oops!", message: "Unable to load or products not found, please try again later", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "Ok", style: .default)
        alertController.addAction(okAction)
        self?.present(alertController, animated: true)
    }
}

嗯?所以在第一部分做同样的事情!

hideActivity()
let products = response.products
if products.count > 0{
    DispatchQueue.main.async { [weak self] in
        let controller = HAPurchaseViewController.instantiate() 
        controller.categories = dataManager.paidCategories()
        controller.products = products
        self?.navigationController?.pushViewController(controller, animated: true)
    }
} else {
    DispatchQueue.main.async { [weak self] in
        let alertController = UIAlertController(title: "Oops!", message: "Unable to load or products not found, please try again later", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "Ok", style: .default)
        alertController.addAction(okAction)
        self?.present(alertController, animated: true)
    }
}