├── .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 |
7 |
8 |
9 |
10 |
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 |
--------------------------------------------------------------------------------