/Lame

A PoC MVVM-C using responder change based dependency injection and no external dependencies

Primary LanguageSwift

Lamé

Lamé is a simple example of an MVVM-C application written in Swift for iOS. It has zero external dependencies so should function fresh out of the box.

Setup

This project builds against Xcode 12.5.1 and Xcodde 13b5 (for async/await). If you wish to run this on a device you will need to:

  1. update the bundle identifier of the app to something other than com.jjrscott.Lame
  2. set the Team
  3. enable Automatically manage signing

The responder chain

Lame uses the responder chain to connect responders (e.g. UIViewController) with the coordinator which is itself a UIResponder (and UIApplicationDelegate). This functionality is implemented via UIResponderDelegate along with its associated default implementation

class SomeViewController: UITableViewController, UIResponderDelegate {
        
   weak var delegate: SomeViewControllerDelegate?
       
   override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      target?.selectItem(at: indexPath)
    }
}

If delegate is set, then it is used; otherwise, the view controller will search up the chain until it finds a responder that conforms to SomeViewControllerDelegate. In our example, the root UIResponder is Coordinator.

Segues

Apple encourages the use of storyboards but keeping the flow out of view controllers is made almost impossible by the existence of UIViewController.prepare(for:sender:).

However, back in the ObjC days, I used runtime magic and the knowledge that the whole process happens on the main thread to produce performSegue(withIdentifier:prepare:) which allows you to handle the preparation in the same code performSegue is called. In our case, the coordinator.

AsyncCompletion

AsyncCompletion provides a simple mechanism to convert functions with a variaty of common completion handler types to their async/await equivalents.

For example, withResultHandler can be used to convert functions that have a Result based completion handler:

func getFoo(..., completion: @escaping (Result<Foo,Error>)->Void)

@available(iOS 15.0.0, *)
func foo(...) async throws -> Foo {
   return try await withResultHandler { resultHandler in
      getFoo(page: pageIndex, result: resultHandler)
   }
}

While withCompletionHandler can be used to convert the other popular completion handler type:

func getFoo(..., completion: @escaping (Foo?, Error?)->Void)

@available(iOS 15.0.0, *)
func foo(...) async throws -> Foo {
   return try await withCompletionHandler { completionHandler in
      getFoo(..., completion: completionHandler)
   }
}

Very tidy.

Storyboard constants

Runtime values like storyboard identifiers are dangerous, and unneccessary. storyboard_constants enables compile time connection to the internals of a storyboard. It achieves this by generating a Swift file containing constants for:

  • segue identifiers
  • storyboard identifiers
  • table view cell reuse identifiers

If you remove a view from a storyboard and the constant will also be removed. Any code still using that constant will now fail to compile.

Credits

Gabriel Lamé

From Wikipedia:

Gabriel Lamé (22 July 1795 -- 1 May 1870) was a French mathematician who contributed to the theory of partial differential equations by the use of curvilinear coordinates, and the mathematical theory of elasticity (for which linear elasticity and finite strain theory elaborate the mathematical abstractions). 

Lamé was born in Tours, in today's département of Indre-et-Loire.

He became well known for his general theory of curvilinear coordinates and his notation and study of classes of ellipse-like curves, now known as Lamé curves or superellipses.

App Icon

The app icon comes from a collection of posters of the 6th season of the HBO series Game of Thrones. For the record, I still think the final scene should have been Night's King sat on the Iron Throne, breaking the fourth wall.

The imageset_generator script used to generate all the different app icons sizes (18 at the last count) is part of another project called Sidekick. Sidekick is a collection of scripts for Xcode based development, including