使用依赖性反转尝试存储值时崩溃

问题描述 投票:0回答:1

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

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

这是我的appDelegate中的代码

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()

但由于我想获得知识并使用依赖倒置学习S.O.L.I.D原理,我想坚持这个原则。

swift dependency-injection solid-principles dependency-inversion
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文件。

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

编辑

你需要更新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找到正确的路径。

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

Here是您的演示项目。

© www.soinside.com 2019 - 2024. All rights reserved.