0

我想在我的应用程序中的应用程序委托中实现依赖倒置,因为我rootController是我的UITabBarController,但是当我想尝试它时出现错误

致命错误:在展开可选值时意外发现 nil

这是我在 appDelagate 中的代码

let exploreStore = ExploreStore()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    let rootController = (window?.rootViewController as? UITabBarController)?.children.first as? ExploreViewController
    // Inject Data
    rootController?.exploreStore = exploreStore
    return true
}

这是我的探索课

class ExploreStore {
    fileprivate var allItems = [ExploreItem]()
    func fetch() {
        for data in loadData() {
            allItems.append(ExploreItem(dict: data))
        }
    }
    func numberOfItem() -> Int {
        return allItems.count
    }
    func explore(at index: IndexPath) -> ExploreItem {
        return allItems[index.item]
    }
    fileprivate func loadData() -> [[String: AnyObject]] {
        guard
            let path = Bundle.main.path(forResource: "ExploreData", ofType: "plist"),
            let items = NSArray(contentsOfFile: path)
        else { return [[:]] }
        return items as! [[String: AnyObject]]
    }
}

这是我的 exlporeViewController

var exploreStore: ExploreStore!

override func viewDidLoad() {
    super.viewDidLoad()
    // This is where the error found nil
    exploreStore.fetch()
}

实际上,如果我不使用依赖反转,代码就可以工作,就像我的探索视图控制器不使用这样的强制展开

var exploreStore = ExploreStore()

但是由于我想获得知识并使用依赖倒置来学习 SOLID 原理,所以我想坚持这个原理。

4

1 回答 1

1

如果我正确理解了你的问题,你想在课堂上初始化你的AppDelegate类,然后你想把它传递给你UITabBarController的第一个children,为此你需要对你的didFinishLaunchingWithOptions方法进行一些修改,如下所示:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
    let vc = storyBoard.instantiateViewController(withIdentifier: "tabBar")
    self.window = UIWindow(frame: UIScreen.main.bounds)
    self.window?.rootViewController = vc
    let myTabBar = self.window?.rootViewController as! UITabBarController
    let firstViewController = myTabBar.children.first as? FirstViewController
    firstViewController?.exploreStore = exploreStore
    self.window?.makeKeyAndVisible()

    return true
}

在这里,我进行了一些修改,因为我正在Info.plist从中检索Bundle并且您的ExploreStore遗嘱如下所示:

class ExploreStore {

    var allItems = [ExploreItem]()

    func fetch() {
        if let dataObjectFromPlist = loadData() {
            allItems.append(ExploreItem(dict: dataObjectFromPlist))
        }
    }
    func numberOfItem() -> Int {
        return allItems.count
    }
    func explore(at index: IndexPath) -> ExploreItem {
        return allItems[index.item]
    }
    fileprivate func loadData() -> [String: AnyObject]? {

        var resourceFileDictionary: [String: AnyObject]?
        if let path = Bundle.main.path(forResource: "Info", ofType: "plist") {
            if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
                resourceFileDictionary = dict
            }
        }
        return resourceFileDictionary
    }
}

然后在我的我可以从类FirstViewController中获取数据ExploreStore

exploreStore.fetch()

我的代码UIViewController

class FirstViewController: UIViewController {

    var exploreStore: ExploreStore!

    override func viewDidLoad() {
        super.viewDidLoad()

        exploreStore.fetch()
        print(exploreStore.allItems[0].data)
    }
}

这里exploreStore.allItems[0].data将打印我的整个info.plist文件。

您可以使用这个演示项目自行尝试,并检查这是否是正确的行为。

编辑

您需要更新didFinishLaunchingWithOptions方法,例如:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    setupDefaultColors()

    let exploreStoryBoard = UIStoryboard(name: "Explore", bundle:nil)
    let navigationController = exploreStoryBoard.instantiateViewController(withIdentifier: "ExploreViewControllerNavigation") as! UINavigationController
    if let exploreViewController = navigationController.children.first as? ExploreViewController {
        exploreViewController.store = ExploreStore()
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.rootViewController = exploreViewController
        self.window?.makeKeyAndVisible()
    }

    return true
}

您还需要更新ExploreStore类,如下所示:

class ExploreStore {

    var allItems = [ExploreItem]()

    func fetch() {
        if let dataObjectFromPlist = loadData() {
            allItems.append(ExploreItem(dict: dataObjectFromPlist))
        }
    }

    func numberOfItem() -> Int {
        return allItems.count
    }

    func explore(at index: IndexPath) -> ExploreItem {
        return allItems[index.item]
    }

    fileprivate func loadData() -> [String: AnyObject]? {
        var resourceFileDictionary: [String: AnyObject]?
        if let path = Bundle.main.path(forResource: "ExploreData", ofType: "plist") {
            if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
                resourceFileDictionary = dict
            }
        }
        return resourceFileDictionary
    }
}

因为plist你会得到Dictionary<String, AnyObject>类型对象。

而且您仍然不会从plist文件中获取数据,因为它已添加到子文件夹中。因此,您需要首先为您的plist.

您还需要为导航控制器和标签栏控制器分配各自的标识符。

是您的演示项目。

于 2019-03-14T07:43:35.633 回答