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

时间:2019-03-14 06:33:54

标签: swift dependency-injection solid-principles dependency-inversion

我想在我的应用程序中的应用程序委托中实现依赖关系倒置,因为我的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()
}

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

var exploreStore = ExploreStore()

但是由于我想学习知识并使用依赖倒置学习S.O.L.I.D原理,因此我要坚持这一原理。

1 个答案:

答案 0 :(得分: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是您的演示项目。