{"id":17202331,"url":"https://github.com/s4cha/plug","last_synced_at":"2025-04-13T21:11:17.345Z","repository":{"id":73320483,"uuid":"83877964","full_name":"s4cha/Plug","owner":"s4cha","description":"🔌 Plug implementation details for a cleaner Architecture on iOS","archived":false,"fork":false,"pushed_at":"2020-02-12T15:34:22.000Z","size":37542,"stargazers_count":29,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-11T03:59:29.318Z","etag":null,"topics":["actions","clean-architecture","decoupling","dependency-injection","dependency-inversion","di-container","inversion-of-control","ioc","plug","swift","swinject","use-case"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s4cha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-04T08:10:41.000Z","updated_at":"2024-04-15T03:07:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"9c821e4d-351d-4f21-976d-62624df4b9bf","html_url":"https://github.com/s4cha/Plug","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4cha%2FPlug","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4cha%2FPlug/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4cha%2FPlug/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4cha%2FPlug/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s4cha","download_url":"https://codeload.github.com/s4cha/Plug/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248782261,"owners_count":21160717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions","clean-architecture","decoupling","dependency-injection","dependency-inversion","di-container","inversion-of-control","ioc","plug","swift","swinject","use-case"],"created_at":"2024-10-15T02:14:22.697Z","updated_at":"2025-04-13T21:11:17.337Z","avatar_url":"https://github.com/s4cha.png","language":"Swift","readme":"![Plug](https://raw.githubusercontent.com/s4cha/Plug/master/banner.png)\n\n# Plug\n\n[![Language: Swift 3|4|5](https://img.shields.io/badge/language-swift3|4-f48041.svg?style=flat)](https://developer.apple.com/swift)\n![Platform: iOS 8+](https://img.shields.io/badge/platform-iOS%208%2B-blue.svg?style=flat)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Build Status](https://app.bitrise.io/app/c6d3096518e622f3/status.svg?token=7QmnkUstRGnMqQ-8eT_h3w\u0026branch=master)](https://app.bitrise.io/app/c6d3096518e622f3)\n[![codebeat badge](https://codebeat.co/badges/5d4c8283-3b5e-431e-a811-c43ae4f75374)](https://codebeat.co/projects/github-com-s4cha-plug-master)\n[![License: MIT](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/freshOS/Arrow/blob/master/LICENSE)\n[![GitHub tag](https://img.shields.io/github/release/s4cha/Plug.svg)]()\n\n[Reason](#why) - [Example](#example-project) -  [Idea](#main-idea) - [Get Started](#get-started) - [Installation](#installation)\n\n![Router](https://raw.githubusercontent.com/s4cha/Plug/master/PlugInfographics.png)\n\n## Why\nBecause the **classic way** of building iOS Apps introduces **tight coupling**, often with the network layer. **Dependencies grow** and **code is harder to test.**\n\n## How\nBy 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.**\n\n\n## Benefits\n- [x] Cleaner Architecture (think easier to maintain)\n- [x] Faster build times\n- [x] Less Dependencies (think Better Testability, \u0026 Faster tests)\n- [x] Code 90% of the App without the backend implementation\n\n\n## Example Project\nDownload and launch the `Example Project` to see how a Typical `LikePhoto` use case would be implemented using this approach.\n\n\n## Main Idea\nThe **main idea** is that you want to **decouple your App from it's delivery mechanisms.**\n\nLet me rephrases that for you in a classic iOS App context.\nYour **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`.\nThe ONLY thing it should know is : \"I want to like this photo\"\n\nThis is based on **Robert-C Martin** (Uncle Bob) thoughts, the guy behind the **SOLID** principles. You can watch one of his terrific talks here :\nhttps://skillsmatter.com/skillscasts/2437-uncle-bob-web-architecture\nHis examples are often written in Java, and the swift examples I came across were often Java directly translated into swift.\n\nThis is an **alternative approach** aiming at the **same goal**, but **leveraging swift awesomeness**.\n\n\n\n## Get Started\n\n### Action\nThe entire Approach is built on the concept of `Actions`.  \nAn `Action` has an `Input` and an `Output`.\n\nHere is what it looks like in swift :\n```swift\npublic protocol IsAction {\n    associatedtype Input\n    associatedtype Output\n    func perform(_ input: Input) -\u003e Output?\n}\n```\n\n### Create Your Action\n\nFor instance in the case of liking a photo, the `LikePhoto` action has an input of `Photo` and an Output of `Promise\u003cVoid\u003e`\n\nA cool thing is that Output can be **synchronous or asynchronous**, it's yours to choose \\o/.  \nHere is how our App defines the `LikePhoto` use-case :\n```swift\nclass LikePhoto: Action\u003cPhoto,Promise\u003cVoid\u003e\u003e { }\n```\n\n### Call it\n```swift\naction(LikePhoto.self, Photo()).then {\n  // photo liked !\n}\n```\n*Note : This uses dependency injection behind the hood to provide the concrete `LikePhoto` implementation at runtime*.\n\n### Add some Model sugar\nThis phase is optional. But software is built for humans and we want this to be as readable as possible !\n\n```swift\nextension Photo {\n    func like() -\u003e Promise\u003cVoid\u003e {\n      return action(LikePhoto.self, self)\n    }\n}\n```\nYou can now like a photo like this:\n```swift\nphoto.like().then {\n  // Photo liked \\o/\n}\n```\n\n### Providing A Concrete implementation\n```swift\nclass MyLikePhoto: LikePhoto {\n\n    override func perform(_ input: Photo) -\u003e Promise\u003cVoid\u003e {\n       return network.post(\"/photos/\\(input.identifier)/like\")\n    }\n}\n\n```\n\n### 🔌 Plug it !\nWe can now Inject this implementation in our App form the `AppDelegate` :\n```swift        \nActions.plug(LikePhoto.self, to: MyLikePhoto())\n```\nOr if you prefer the short version :\n```swift        \nLikePhoto.self \u003c~ MyLikePhoto()\n```\nAll the `LikePhoto` actions of our App will now use our concrete Implementation preforming a network call :).\n\nThis is now super easy to provide a dummy `MockLikePhoto` for testing purposes!\n\n\n\n## Sum Up\nUsing this approach we have:\n\n- A `Type-Safe` way to decouple and inject actions in our App.\n- A clean and readable way to call actions on models.\n\n\n## Installation\n\n#### Swift Package Manager\n```swift\nhttps://github.com/s4cha/Plug\n```\n#### Carthage (Deprecated)\n```\ngithub \"s4cha/Plug\"\n```\n\n## Swift Version\nSwift 3 -\u003e version **0.2.0**  \nSwift 4 -\u003e version **0.3.0**  \nSwift 4.2 -\u003e version **1.0.0**  \nSwift 5.0 -\u003e version **1.1.0**  \nSwift 5.1 -\u003e version **1.1.1**  \nSwift 5.1.3 -\u003e version **1.1.2**\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs4cha%2Fplug","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs4cha%2Fplug","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs4cha%2Fplug/lists"}