/awesome-swift

🕶 A list of awesome helpers for Swift and iOS [WIP]

MIT LicenseMIT

Awesome Swift

A list of awesome helpers for Swift and iOS

Generic Builder DSL

Usage:

extension UIView: Builder {}

lazy var label = UILabel()
    .. \.text <- "Hello, World!"
    .. \.font <- .systemFont(ofSize: 40)
    .. \.textColor <- .white

Implementation:

infix operator ..: AdditionPrecedence
infix operator <-: MultiplicationPrecedence

struct Predicate<Element> {
    let code: (Element) -> Element

    func runCode(for element: Element) -> Element {
        return code(element)
    }
}

func <- <Element, T>(_ attribute: WritableKeyPath<Element, T>,
                     _ constant: T) -> Predicate<Element> {
    return Predicate(code: { value in
        var copy = value
        copy[keyPath: attribute] = constant
        return copy
    })
}

protocol Builder {}

extension Builder {
    @discardableResult
    static func .. (_ element: Self,
                    _ predicate: Predicate<Self>) -> Self {
        return element.with(predicate)
    }

    private func with(_ predicate: Predicate<Self>) -> Self {
        return predicate.runCode(for: self)
    }
}

Router/Coordinator pattern protocols

  • PresentableRouter
  • PushableRouter
  • TabBarRouter
  • TabPageRouter

PresentableRouter

Usage:

  1. Make your Router implement PresentableRouter
final class SomeModalRouter: PresentableRouter {
    // ...
    
}
  1. Simply call its start() method
let router = SomeModalRouter(...)
router.start()

Implementation:

import UIKit

protocol PresentableRouter: ShowableRouter {
    var presentingViewController: UIViewController { get }
}

extension PresentableRouter {
    func show(viewController: UIViewController, animated: Bool) {
        currentViewController = viewController
        presentingViewController.present(viewController, animated: animated)
    }
}

Obs.: It requires ShowableRouter and Router protocols.


PushableRouter

Usage:

  1. Make your Router implement PushableRouter
final class SomeRouter: PushableRouter {
    // ...
    
}
  1. Simply call its start() method
let router = SomeRouter(...)
router.start()

Implementation:

import UIKit

protocol PushableRouter: ShowableRouter {
    var presentingViewController: UINavigationController { get }
}

extension PushableRouter {
    func show(viewController: UIViewController, animated: Bool) {
        currentViewController = viewController
        presentingViewController.pushViewController(viewController, animated: animated)
    }
}

Obs.: It requires ShowableRouter and Router protocols.


Router

A prerequisite for ShowableRouter.

Implementation:

import UIKit

protocol Router: class {
    var currentViewController: UIViewController? { get }
    func start()
    func start(animated: Bool)
}

extension Router {
    func start() {
        start(animated: true)
    }
}

ShowableRouter

A prerequisite for PushableRouter, PresentableRouter, RootRouter and TabPageRouter

Implementation:

import UIKit

protocol ShowableRouter: Router {
    var currentViewController: UIViewController? { get set }
    func createViewController() -> UIViewController
    func show(viewController: UIViewController, animated: Bool)
}

extension ShowableRouter {
    func start(animated: Bool = true) {
        let viewController = createViewController()
        show(viewController: viewController, animated: animated)
    }
}

AppDelegateMock

Implementation steps:

  1. Remove @UIApplicationMain from AppDelegate.swift
  2. Create a main.swift file and add this:
// main.swift

import UIKit

let appDelegateClass: AnyClass =
    NSClassFromString("TestsAppDelegate") ?? AppDelegate.self

UIApplicationMain(
    CommandLine.argc,
    CommandLine.unsafeArgv,
    nil,
    NSStringFromClass(appDelegateClass)
)
  1. Create a TestsAppDelegate.swift file -- which will be our mock.
  2. Add it to the desired tests target
  3. Implement it:
import UIKit
@testable import YourProjectName

@objc(TestsAppDelegate)
class TestsAppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

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

        // do some testing setup here

        return true
    }

}