Leonardo Maia Pugliese
Holy Swift

Holy Swift

Using Xcode trick to quick generate Swift Protocols

Using Xcode trick to quick generate Swift Protocols

Trick or treat?

Leonardo Maia Pugliese's photo
Leonardo Maia Pugliese
·Dec 16, 2021·

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Hallo dames en heren, Leo Hier.

Today we will walkthrough on how easily you can transform any type you have in a protocol in very small steps. It's not rare we iOS developers have to transform a concrete type into a protocol. Thinking of decoupling layers and SOLID this is a great way to go. Also using protocol to communicate between objects enables testability and that's for itself is a big win.

Xcode is the number 1 tool to any platform development, it's very handy to know how can we leverage it power in our benefit.

Let's code! But first...

The Painting

Today art piece is called Witches’ Kitchen from Cornelis Saftleven. He was a Dutch Golden Age painter. He was born into a family of artists, and learned to paint from his father Herman, along with his brothers Abraham and Herman Saftleven the Younger.

His subject matter was varied, from rural genre scenes to portraits, beach scenes, and biblical and mythological themes. His images of Hell may be his most individual contribution to Dutch painting. Equally innovative were his satires and allegories. Saftleven excelled at painting animals and often portrayed animals as active characters, occasionally with a hidden allegorical role.

I choose this painting because we will talk about tricks today, and we all know that all witches knows some hidden tricks.

The Problem

Imagine you have a type, in this example the UserManager, that you want to transform into a protocol. How can Xcode help you do that?

First let's setup the code. Create a new project on your Xcode and remove all the storyboard. Them update the sceneDelegate with this code:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }

        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = ViewController(userManager: UserManager())

        self.window = window
        window.makeKeyAndVisible()
    }

Now let's create our UserManager, the class ( but it could be a struct) that will be have a protocol in the future. Create a new file called UserManager.swift and copy/paste the code below:

import Foundation

class UserManager {
    var userNames: [String] = ["Leo","Mike","Pep","Alan","Lucas"]
    private let id = UUID()

     func getUsers() -> [String] {
        // more complicated logic here
        return userNames
    }

     func updateUserName(at index: Int, userName: String) -> Bool {

        guard userName.count > 2, index < userNames.endIndex else { return false }

        userNames.remove(at: index)
        userNames.insert(userName, at: index)
        return true
    }

     func deleteUserName(userName: String) -> Bool {

        guard let index = userNames.firstIndex(where: { $0 == userName }) else { return false }

        userNames.remove(at: index)

        return true
    }

    func lastUser() throws -> String? {
        userNames.last
    }

    // other functions here...

}

extension UserManager: Equatable {
    static func == (lhs: UserManager, rhs: UserManager) -> Bool {
        lhs.id == rhs.id
    }
}

The exact content of each function is placeholder, don't focus on that.

Now edit your view controller so we can inject the UserManager on the initialiser:

import UIKit

class ViewController: UIViewController {

    let userManager: UserManager

    init(userManager: UserManager) {
        self.userManager = userManager
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red

        print(userManager.getUsers())
    }
}

We finished the initial setup, that's all we need.

The Xcode Trick

Now the magic begins. Go to the UserManager file and use this shortcut:

Control + Command + ⬆️ (arrow up)

Xcode will take a time on this screen and after that will generate for you what we want:

Screenshot 2021-12-16 at 07.55.32.png

The final result is this:

import Foundation

internal class UserManager {

    internal var userNames: [String]

    internal func getUsers() -> [String]

    internal func updateUserName(at index: Int, userName: String) -> Bool

    internal func deleteUserName(userName: String) -> Bool

    internal func lastUser() throws -> String?
}

extension UserManager : Equatable {

    /// Returns a Boolean value indicating whether two values are equal.
    ///
    /// Equality is the inverse of inequality. For any values `a` and `b`,
    /// `a == b` implies that `a != b` is `false`.
    ///
    /// - Parameters:
    ///   - lhs: A value to compare.
    ///   - rhs: Another value to compare.
    internal static func == (lhs: UserManager, rhs: UserManager) -> Bool
}

With this in hands you can copy it contents to another file and start to transform into a protocol. Create a new file called UserManaging.swift and copy the contents of the generated interface.

Now we have to turn it into a protocol. The steps are:

  1. Change the class UserManager name to protocol UserManaging.

  2. You can cleanup the internal access modifier with a replace tool.

Screenshot 2021-12-16 at 08.02.32.png

  1. You will need to explicit add { get } or { get set } specifier.

  2. In this example you can erase equatable extension and userNames var

The final protocol looks like this:

protocol UserManaging {
    func getUsers() -> [String]
    func updateUserName(at index: Int, userName: String) -> Bool
    func deleteUserName(userName: String) -> Bool
    func lastUser() throws -> String?
}

Now make UserManager conform to it:

Screenshot 2021-12-16 at 08.06.12.png

And your view controller receive the protocol in the initialiser:

Screenshot 2021-12-16 at 08.11.19.png

And the first part of refactoring is done!

Extra points

Whenever you do this kind of approach you always have to remember the Interface Segregation Principle it states that:

Many client-specific interfaces are better than one general-purpose interface.

This way we can split our newly created protocol even more, because the only function that is used is the getUsers. This way our final protocol could be like this:

protocol ViewControllerUserProviding {
    func getUsers() -> [String]
}

And if you change that to UserManager and the ViewController:

Screenshot 2021-12-16 at 08.17.32.png

And:

Screenshot 2021-12-16 at 08.17.50.png

Now our refactoring is perfect. The view controller doesn't need to know anything then one function from the UserManager.

And that's it!

Summary

Today we learn how to use a Xcode shortcut to generate protocols and how can you refactor a Service injection in the way the injected type only knows what it really need to know.

That's all my people, I hope you liked as I enjoyed write this article. If you want to support this blog you can Buy Me a Coffee or just leave a comment saying hello. You can also sponsor posts and I'm open to writing freelancing! Just reach me in LinkedIn or Twitter for details.

Thanks for the reading and... That's all folks.

credit: image

Did you find this article valuable?

Support Leonardo Maia Pugliese by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this