Abdulla2022 / Vegetarian-Food-Finder-

0 stars 0 forks source link

[7/6][Code Review] Avoid using one global struct in the `StringManager` file #12

Closed metamattrosemeier closed 2 years ago

metamattrosemeier commented 2 years ago

https://github.com/Abdulla2022/Vegetarian-Food-Finder-/blob/66b99fc4c799f14952b17c78cbdcf4f2aa9632c7/Vegetarian-Food-Finder/StringsManger.swift#L15

I think you're on the right track as far as mindset here. Whenever we're using strings, there's always the possibility of a typo, and that can be a real pain when it comes to random bugs you don't expect! So, great thinking on this one.

I think this would be a fantastic introduction to a practical use of Swift protocols. If you're unfamiliar, Swift protocols are like little rule books that any conforming type can obey. We can start with a simple protocol that can be expanded upon to add flexibility later.

Try this: 1.) Create a new Swift file and name it StoryboardIdentifiable.swift 2.) Copy and paste this into that file:

import Foundation
import UIKit

protocol StoryboardIdentifiable where Self: UIViewController {
    static var storyboard: UIStoryboard { get }
    static var viewController: UIViewController? { get }
}

extension StoryboardIdentifiable {
    static var storyboard: UIStoryboard {
        return UIStoryboard(name: "Main", bundle: nil)
    }

    static var viewController: UIViewController? {
        return Self.storyboard.instantiateViewController(withIdentifier: NSStringFromClass(self))
    }
}

What does this do? This sets some rules for what it means to be StoryboardIdentifiable. Any conforming class must obey these rules. Note the where Self: UIViewController portion. This says that only view controllers can conform to this protocol.

There are two "rules": 1.) Conforming classes must provided a UIStoryboard instance (the storyboard the controller lives inside) 2.) They must also provide a UIViewController? instance.

So what about the extension ? Well, this gives you built-in default behavior for any type conforming to StoryboardIdentifiable. This way you don't have to implement the two vars from the protocol yourself everywhere! Notice the viewController instance in the extension is using NSStringFromClass(self) This is because the name of the file, and the identifier of the view controller in the storyboard are typically made to be the same exact string. So all we have to do is...keep doing that.

Now, you can do this for example:

Instead of doing this: (In HomeViewController.swift)

self.view.window?.rootViewController = segues.loginViewController

You can replace this with:

self.view.window?.rootViewController = LoginViewController.viewController

And you can delete this line from your StringsManager file

    static let loginViewController = homeStoryBoard.instantiateViewController(withIdentifier: "LoginViewController") as? UIViewController

One last thing to note about NSStringFromClass in the extension, is this will prefix the class name with the module name, so you'll need to make sure you do the same when you implement this protocol. So, in your storyboard, change your LoginViewController storyboard identifier to Vegetarian_Food_Finder.LoginViewController

Then the last thing you have to do is conform LoginViewController to StoryboardIdentifier:

class LoginViewController: UIViewController, StoryboardIdentifiable {

You can repeat this process with Home/Search/SignUp view controllers as well.

Abdulla2022 commented 2 years ago

Done