Skip to content
/ Plug Public

πŸ”Œ Plug implementation details for a cleaner Architecture on iOS

License

Notifications You must be signed in to change notification settings

s4cha/Plug

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

48 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Plug

Plug

Language: Swift 3|4|5 Platform: iOS 8+ Carthage compatible Build Status codebeat badge License: MIT GitHub tag

Reason - Example - Idea - Get Started - Installation

Router

Why

Because the classic way of building iOS Apps introduces tight coupling, often with the network layer. Dependencies grow and code is harder to test.

How

By providing a simple convention on how to cleanly plug implementations in an iOS App. Developers spend less time architecting and more time adding value for their users.

Benefits

  • Cleaner Architecture (think easier to maintain)
  • Faster build times
  • Less Dependencies (think Better Testability, & Faster tests)
  • Code 90% of the App without the backend implementation

Example Project

Download and launch the Example Project to see how a Typical LikePhoto use case would be implemented using this approach.

Main Idea

The main idea is that you want to decouple your App from it's delivery mechanisms.

Let me rephrases that for you in a classic iOS App context. Your ViewControllers should not know how an action is performed. For instance when you like a photo, the controller should'nt know if it's going to send a web request, a database command, or even a local request. The ONLY thing it should know is : "I want to like this photo"

This is based on Robert-C Martin (Uncle Bob) thoughts, the guy behind the SOLID principles. You can watch one of his terrific talks here : https://skillsmatter.com/skillscasts/2437-uncle-bob-web-architecture His examples are often written in Java, and the swift examples I came across were often Java directly translated into swift.

This is an alternative approach aiming at the same goal, but leveraging swift awesomeness.

Get Started

Action

The entire Approach is built on the concept of Actions.
An Action has an Input and an Output.

Here is what it looks like in swift :

public protocol IsAction {
    associatedtype Input
    associatedtype Output
    func perform(_ input: Input) -> Output?
}

Create Your Action

For instance in the case of liking a photo, the LikePhoto action has an input of Photo and an Output of Promise<Void>

A cool thing is that Output can be synchronous or asynchronous, it's yours to choose \o/.
Here is how our App defines the LikePhoto use-case :

class LikePhoto: Action<Photo,Promise<Void>> { }

Call it

action(LikePhoto.self, Photo()).then {
  // photo liked !
}

Note : This uses dependency injection behind the hood to provide the concrete LikePhoto implementation at runtime.

Add some Model sugar

This phase is optional. But software is built for humans and we want this to be as readable as possible !

extension Photo {
    func like() -> Promise<Void> {
      return action(LikePhoto.self, self)
    }
}

You can now like a photo like this:

photo.like().then {
  // Photo liked \o/
}

Providing A Concrete implementation

class MyLikePhoto: LikePhoto {

    override func perform(_ input: Photo) -> Promise<Void> {
       return network.post("/photos/\(input.identifier)/like")
    }
}

πŸ”Œ Plug it !

We can now Inject this implementation in our App form the AppDelegate :

Actions.plug(LikePhoto.self, to: MyLikePhoto())

Or if you prefer the short version :

LikePhoto.self <~ MyLikePhoto()

All the LikePhoto actions of our App will now use our concrete Implementation preforming a network call :).

This is now super easy to provide a dummy MockLikePhoto for testing purposes!

Sum Up

Using this approach we have:

  • A Type-Safe way to decouple and inject actions in our App.
  • A clean and readable way to call actions on models.

Installation

Swift Package Manager

https://github.com/s4cha/Plug

Carthage (Deprecated)

github "s4cha/Plug"

Swift Version

Swift 3 -> version 0.2.0
Swift 4 -> version 0.3.0
Swift 4.2 -> version 1.0.0
Swift 5.0 -> version 1.1.0
Swift 5.1 -> version 1.1.1
Swift 5.1.3 -> version 1.1.2