├── .github └── swift-logo.jpg ├── .gitignore ├── .swift-version ├── .swiftlint.yml ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md └── creational ├── AbstractFactory.playground ├── Contents.swift ├── Sources │ ├── AbstractFactory.swift │ ├── BedroomChair.swift │ ├── BedroomFactory.swift │ ├── BedroomSofa.swift │ ├── BedroomTable.swift │ ├── Chair.swift │ ├── KitchenChair.swift │ ├── KitchenFactory.swift │ ├── KitchenSofa.swift │ ├── KitchenTable.swift │ ├── Sofa.swift │ └── Table.swift └── contents.xcplayground ├── Factory.playground ├── Contents.swift ├── Sources │ ├── Exercise.swift │ ├── FactoryExercise.swift │ ├── Jumping.swift │ └── Squats.swift └── contents.xcplayground └── Singleton.playground ├── Contents.swift ├── Sources └── Singleton.swift └── contents.xcplayground /.github/swift-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aitemr/design-patterns/cef0734a462255202bf97e6224107e164789a4d2/.github/swift-logo.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | xcshareddata/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | # Pods/ 28 | 29 | # Carthage 30 | # 31 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 32 | # Carthage/Checkouts 33 | 34 | Carthage/Build -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2.1 -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - force_try 4 | - force_cast 5 | - operator_whitespace 6 | - trailing_whitespace 7 | - todo 8 | - closure_parameter_position 9 | - large_tuple 10 | - unused_optional_binding 11 | 12 | line_length: 13 | error: 120 14 | 15 | type_body_length: 16 | error: 100 17 | 18 | file_length: 19 | error: 200 20 | 21 | function_body_length: 22 | error: 20 23 | 24 | reporter: "xcode" 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode10 3 | env: 4 | global: 5 | - SDK=iphoneos 6 | - TARGET=armv7-apple-ios10 7 | matrix: 8 | - PLAYGROUND_DIR="creational/Singleton.playground" 9 | - PLAYGROUND_DIR="creational/Factory.playground" 10 | - PLAYGROUND_DIR="creational/AbstractFactory.playground" 11 | script: 12 | xcrun swift --version && 13 | cd "${PLAYGROUND_DIR}" && 14 | xcrun -sdk "${SDK}" 15 | swiftc -target "${TARGET}" 16 | -emit-library -emit-module -module-name AuxiliarySources 17 | Sources/*.swift && 18 | if ! xcrun swiftc -emit-imported-modules Contents.swift | 19 | grep -q "PlaygroundSupport"; 20 | then 21 | 22 | cat <(echo "import AuxiliarySources") Contents.swift > main.swift && 23 | xcrun -sdk "${SDK}" 24 | swiftc -target "${TARGET}" 25 | -I "." -L "." -lAuxiliarySources -module-link-name AuxiliarySources 26 | -o Playground main.swift; 27 | fi -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Please ensure your pull request adheres to the following guidelines: 4 | 5 | - Make an individual pull request for each suggestion. 6 | - Choose the corresponding patterns section for your suggestion. 7 | - List, after your addition, should be in lexicographical order. 8 | 9 | ## Commit Messages Guidelines 10 | 11 | - The message should be in imperative form and uncapitalized. 12 | - If possible, please include an explanation in the commit message body 13 | - Use the form `/: ` (e.g. `creational/singleton: refactor singleton constructor`) 14 | 15 | ## Pattern Template 16 | 17 | Each pattern should have a single markdown file containing the important part of the implementation, the usage and the explanations for it. This is to ensure that the reader doesn't have to read bunch of boilerplate to understand what's going on and the code is as simple as possible and not simpler. 18 | 19 | Please use the following template for adding new patterns: 20 | 21 | ```markdown 22 | # 23 | 24 | 25 | ## Implementation 26 | 27 | ## Usage 28 | 29 | // Optional 30 | ## Rules of Thumb 31 | ``` -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Islam Temirbek (https://aitemr.github.io) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | Swift Patterns 5 |
6 | build-status 7 | progress 8 | language 9 | contact 10 | license 11 |

12 |

13 | 14 | A curated collection of idiomatic design & application patterns for Swift language. 15 | 16 | ## Creational Patterns 17 | 18 | | Pattern | Description | Status | 19 | |:-------:|:----------- |:------:| 20 | | [Abstract Factory](/creational/AbstractFactory.playground) | Provides an interface for creating families of releated objects | ✔ | 21 | | [Builder](/creational/builder.md) | Builds a complex object using simple objects | ✘ | 22 | | [Factory Method](/creational/Factory.playground) | Defers instantiation of an object to a specialized function for creating instances | ✔ | 23 | | [Object Pool](/creational/object-pool.md) | Instantiates and maintains a group of objects instances of the same type | ✘ | 24 | | [Singleton](/creational/Singleton.playground) | Restricts instantiation of a type to one object | ✔ | 25 | 26 | ## Structural Patterns 27 | 28 | | Pattern | Description | Status | 29 | |:-------:|:----------- |:------:| 30 | | [Bridge](/structural/bridge.md) | Decouples an interface from its implementation so that the two can vary independently | ✘ | 31 | | [Composite](/structural/composite.md) | Encapsulates and provides access to a number of different objects | ✘ | 32 | | [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | ✘ | 33 | | [Facade](/structural/facade.md) | Uses one type as an API to a number of others | ✘ | 34 | | [Flyweight](/structural/flyweight.md) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✘ | 35 | | [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✘ | 36 | 37 | ## Behavioral Patterns 38 | 39 | | Pattern | Description | Status | 40 | |:-------:|:----------- |:------:| 41 | | [Chain of Responsibility](/behavioral/chain_of_responsibility.md) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | ✘ | 42 | | [Command](/behavioral/command.md) | Bundles a command and arguments to call later | ✘ | 43 | | [Mediator](/behavioral/mediator.md) | Connects objects and acts as a proxy | ✘ | 44 | | [Memento](/behavioral/memento.md) | Generate an opaque token that can be used to go back to a previous state | ✘ | 45 | | [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✘ | 46 | | [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ | 47 | | [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | ✘ | 48 | | [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✘ | 49 | | [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | ✘ | 50 | | [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ | 51 | 52 | ## Synchronization Patterns 53 | 54 | | Pattern | Description | Status | 55 | |:-------:|:----------- |:------:| 56 | | [Condition Variable](/synchronization/condition_variable.md) | Provides a mechanism for threads to temporarily give up access in order to wait for some condition | ✘ | 57 | | [Lock/Mutex](/synchronization/mutex.md) | Enforces mutual exclusion limit on a resource to gain exclusive access | ✘ | 58 | | [Monitor](/synchronization/monitor.md) | Combination of mutex and condition variable patterns | ✘ | 59 | | [Read-Write Lock](/synchronization/read_write_lock.md) | Allows parallel read access, but only exclusive access on write operations to a resource | ✘ | 60 | | [Semaphore](/synchronization/semaphore.md) | Allows controlling access to a common resource | ✘ | 61 | 62 | ## Concurrency Patterns 63 | 64 | | Pattern | Description | Status | 65 | |:-------:|:----------- |:------:| 66 | | [N-Barrier](/concurrency/barrier.md) | Prevents a process from proceeding until all N processes reach to the barrier | ✘ | 67 | | [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✘ | 68 | | [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | ✘ | 69 | | [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | ✘ | 70 | | [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | ✘ | 71 | | [Reactor](/concurrency/reactor.md) | Demultiplexes service requests delivered concurrently to a service handler and dispatches them syncronously to the associated request handlers | ✘ | 72 | | [Parallelism](/concurrency/parallelism.md) | Completes large number of independent tasks | ✘ | 73 | | [Producer Consumer](/concurrency/producer_consumer.md) | Separates tasks from task executions | ✘ | 74 | 75 | ## Messaging Patterns 76 | 77 | | Pattern | Description | Status | 78 | |:-------:|:----------- |:------:| 79 | | [Fan-In](/messaging/fan_in.md) | Funnels tasks to a work sink (e.g. server) | ✘ | 80 | | [Fan-Out](/messaging/fan_out.md) | Distributes tasks among workers (e.g. producer) | ✘ | 81 | | [Futures & Promises](/messaging/futures_promises.md) | Acts as a place-holder of a result that is initially unknown for synchronization purposes | ✘ | 82 | | [Publish/Subscribe](/messaging/publish_subscribe.md) | Passes information to a collection of recipients who subscribed to a topic | ✘ | 83 | | [Push & Pull](/messaging/push_pull.md) | Distributes messages to multiple workers, arranged in a pipeline | ✘ | 84 | 85 | ## Stability Patterns 86 | 87 | | Pattern | Description | Status | 88 | |:-------:|:----------- |:------:| 89 | | [Bulkheads](/stability/bulkhead.md) | Enforces a principle of failure containment (i.e. prevents cascading failures) | ✘ | 90 | | [Circuit-Breaker](/stability/circuit-breaker.md) | Stops the flow of the requests when requests are likely to fail | ✘ | 91 | | [Deadline](/stability/deadline.md) | Allows clients to stop waiting for a response once the probability of response becomes low (e.g. after waiting 10 seconds for a page refresh) | ✘ | 92 | | [Fail-Fast](/stability/fail_fast.md) | Checks the availability of required resources at the start of a request and fails if the requirements are not satisfied | ✘ | 93 | | [Handshaking](/stability/handshaking.md) | Asks a component if it can take any more load, if it can't, the request is declined | ✘ | 94 | | [Steady-State](/stability/steady_state.md) | For every service that accumulates a resource, some other service must recycle that resource | ✘ | 95 | 96 | ## Profiling Patterns 97 | 98 | | Pattern | Description | Status | 99 | |:-------:|:----------- |:------:| 100 | | [Timing Functions](/profiling/timing.md) | Wraps a function and logs the execution | ✘ | 101 | 102 | ## Idioms 103 | 104 | | Pattern | Description | Status | 105 | |:-------:|:----------- |:------:| 106 | | [Functional Options](/idiom/functional-options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | ✘ | 107 | 108 | ## Anti-Patterns 109 | 110 | | Pattern | Description | Status | 111 | |:-------:|:----------- |:------:| 112 | | [Cascading Failures](/anti-patterns/cascading_failures.md) | A failure in a system of interconnected parts in which the failure of a part causes a domino effect | ✘ | 113 | 114 | ## Credits 115 | 116 | Inspired by: 117 | 118 | - [@tmrts's go-pattern](https://github.com/tmrts/go-patterns) 119 | 120 | ## Dependencies 121 | 122 | - [SwiftLint](https://github.com/realm/SwiftLint) 123 | 124 | ## Support 125 | 126 | If you have a question, find a bug, or just want to say hi, please open an [issue on GitHub](https://github.com/aitemr/design-patterns/issues/new). 127 | 128 | If you want to contribute, please read the [guide](./CONTRIBUTING.md). 129 | 130 | ## License 131 | 132 | [MIT License](./LICENSE.md) © Islam Temirbek -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | func createOutlets(on factory: AbstractFactory) -> (chair: Chair, table: Table, sofa: Sofa) { 2 | let chair = factory.createChair() 3 | let table = factory.createTable() 4 | let sofa = factory.createSofa() 5 | 6 | return (chair, table, sofa) 7 | } 8 | 9 | let kitchenOutlets = createOutlets(on: KitchenFactory()) 10 | kitchenOutlets.chair 11 | kitchenOutlets.table 12 | kitchenOutlets.sofa 13 | 14 | let bedRoomOutlets = createOutlets(on: BedroomFactory()) 15 | bedRoomOutlets.chair 16 | bedRoomOutlets.table 17 | bedRoomOutlets.sofa 18 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/AbstractFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol AbstractFactory { 4 | func createChair() -> Chair 5 | func createTable() -> Table 6 | func createSofa() -> Sofa 7 | } 8 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/BedroomChair.swift: -------------------------------------------------------------------------------- 1 | public struct BedroomChair: Chair { 2 | public var name: String = "Chair" 3 | public var type: String = "Chair for bedroom" 4 | } 5 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/BedroomFactory.swift: -------------------------------------------------------------------------------- 1 | public struct BedroomFactory: AbstractFactory { 2 | // MARK: - Init 3 | 4 | public init() { } 5 | 6 | // MARK: - AbstractFactory 7 | 8 | public func createChair() -> Chair { 9 | print("Bedroom chair created") 10 | return BedroomChair() 11 | } 12 | 13 | public func createTable() -> Table { 14 | print("Bedroom table created") 15 | return BedroomTable() 16 | } 17 | 18 | public func createSofa() -> Sofa { 19 | print("Bedroom sofa created") 20 | return BedroomSofa() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/BedroomSofa.swift: -------------------------------------------------------------------------------- 1 | public struct BedroomSofa: Sofa { 2 | public var name: String = "Sofa" 3 | public var type: String = "Sofa for bedroom" 4 | } 5 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/BedroomTable.swift: -------------------------------------------------------------------------------- 1 | public struct BedroomTable: Table { 2 | public var name: String = "Table" 3 | public var type: String = "Table for bedroom" 4 | } 5 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/Chair.swift: -------------------------------------------------------------------------------- 1 | public protocol Chair { 2 | // MARK: - Properties 3 | 4 | var name: String { get } 5 | var type: String { get } 6 | } 7 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/KitchenChair.swift: -------------------------------------------------------------------------------- 1 | public struct KitchenChair: Chair { 2 | public var name: String = "Chair" 3 | public var type: String = "Chair for kitchen" 4 | } 5 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/KitchenFactory.swift: -------------------------------------------------------------------------------- 1 | public struct KitchenFactory: AbstractFactory { 2 | // MARK: - Init 3 | 4 | public init() { } 5 | 6 | // MARK: - AbstractFactory 7 | 8 | public func createChair() -> Chair { 9 | print("Kitchen chair created") 10 | return KitchenChair() 11 | } 12 | 13 | public func createTable() -> Table { 14 | print("Kitchen table created") 15 | return KitchenTable() 16 | } 17 | 18 | public func createSofa() -> Sofa { 19 | print("Kitchen sofa created") 20 | return KitchenSofa() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/KitchenSofa.swift: -------------------------------------------------------------------------------- 1 | public struct KitchenSofa: Sofa { 2 | public var name: String = "Sofa" 3 | public var type: String = "Sofa for ktichen" 4 | } 5 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/KitchenTable.swift: -------------------------------------------------------------------------------- 1 | public struct KitchenTable: Table { 2 | public var name: String = "Table" 3 | public var type: String = "Table for kitchen" 4 | } 5 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/Sofa.swift: -------------------------------------------------------------------------------- 1 | public protocol Sofa { 2 | // MARK: - Properties 3 | 4 | var name: String { get } 5 | var type: String { get } 6 | } 7 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/Sources/Table.swift: -------------------------------------------------------------------------------- 1 | public protocol Table { 2 | // MARK: - Properties 3 | 4 | var name: String { get } 5 | var type: String { get } 6 | } 7 | -------------------------------------------------------------------------------- /creational/AbstractFactory.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /creational/Factory.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | 2 | var exercises = [Exercise]() 3 | 4 | func executeExercises() { 5 | createExercises() 6 | runCardioExercises() 7 | } 8 | 9 | func createExercises() { 10 | let jumping = FactoryExercise.shared.createExercise(name: .jumping) 11 | let squats = FactoryExercise.shared.createExercise(name: .squats) 12 | exercises.append(jumping) 13 | exercises.append(squats) 14 | } 15 | 16 | func runCardioExercises() { 17 | exercises.map { 18 | $0.start() 19 | $0.stop() 20 | } 21 | } 22 | 23 | executeExercises() 24 | -------------------------------------------------------------------------------- /creational/Factory.playground/Sources/Exercise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Exercise { 4 | // MARK: - Properties 5 | 6 | var name: String { get } 7 | var type: String { get } 8 | 9 | // MARK: - Actions 10 | 11 | func start() 12 | func stop() 13 | } 14 | -------------------------------------------------------------------------------- /creational/Factory.playground/Sources/FactoryExercise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum Exercises { 4 | case jumping, squats 5 | } 6 | 7 | public struct FactoryExercise { 8 | public static let shared = FactoryExercise() 9 | 10 | public func createExercise(name: Exercises) -> Exercise { 11 | switch name { 12 | case .jumping: 13 | return Jumping() 14 | case .squats: 15 | return Squats() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /creational/Factory.playground/Sources/Jumping.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Jumping: Exercise { 4 | public var name: String = "Jumping" 5 | public var type: String = "Exercise for legs" 6 | 7 | public func start() { 8 | print("Start jumping") 9 | } 10 | 11 | public func stop() { 12 | print("Stop jumping") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /creational/Factory.playground/Sources/Squats.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Squats: Exercise { 4 | public var name: String = "Squats" 5 | public var type: String = "Exercise for legs" 6 | 7 | public func start() { 8 | print("Start Squats") 9 | } 10 | 11 | public func stop() { 12 | print("Stop Squats") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /creational/Factory.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /creational/Singleton.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | var object = Singleton.shared 4 | object.value = 1000 5 | 6 | var secondObject = Singleton.shared 7 | secondObject.value = 2000 8 | 9 | object.doSomething() 10 | secondObject.doSomething() 11 | 12 | print(object === secondObject) 13 | -------------------------------------------------------------------------------- /creational/Singleton.playground/Sources/Singleton.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class Singleton { 4 | 5 | public static let shared = Singleton() 6 | 7 | private init() { } 8 | 9 | public var value: Int = 0 10 | 11 | public func doSomething() { 12 | print("Doing something with value \(value)") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /creational/Singleton.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --------------------------------------------------------------------------------