├── .github
└── workflows
│ ├── Format.yml
│ └── Test.yml
├── .gitignore
├── .swiftformat
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── LICENSE
├── Makefile
├── Package.swift
├── README.md
├── Scripts
├── format.sh
└── install_swift-format.sh
├── Sources
├── DeclarativeConfiguration
│ └── Exports.swift
├── FunctionalBuilder
│ ├── Builder.swift
│ └── BuilderProvider.swift
├── FunctionalClosures
│ ├── DataSource.swift
│ ├── Deprecations
│ │ └── Assign.swift
│ └── Handler.swift
├── FunctionalConfigurator
│ ├── ConfigIntializable.swift
│ ├── Configurator.swift
│ ├── CustomConfigurable.swift
│ └── Modification.swift
├── FunctionalKeyPath
│ └── FunctionalKeyPath.swift
└── FunctionalModification
│ ├── Deprecations
│ └── Modification.swift
│ └── Reduce.swift
└── Tests
└── DeclarativeConfigurationTests
├── BuilderTests.swift
├── ConfiguratorTests.swift
├── FunctionalClosuresTests.swift
└── ModificationTests.swift
/.github/workflows/Format.yml:
--------------------------------------------------------------------------------
1 | name: format
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | format:
10 | if: |
11 | !contains(github.event.head_commit.message, '[ci skip]') &&
12 | !contains(github.event.head_commit.message, '[ci skip format]')
13 | name: swift-format
14 | runs-on: macos-12
15 | steps:
16 | - uses: actions/checkout@v3
17 | - name: Install
18 | run: make install_formatter
19 | - name: Format
20 | run: make format
21 | - uses: stefanzweifel/git-auto-commit-action@v4
22 | with:
23 | commit_message: 'style(ci): run swiftformat'
24 | branch: 'main'
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 |
28 |
--------------------------------------------------------------------------------
/.github/workflows/Test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | test_macos:
11 | if: |
12 | !contains(github.event.head_commit.message, '[ci skip]') &&
13 | !contains(github.event.head_commit.message, '[ci skip test]') &&
14 | !contains(github.event.head_commit.message, '[ci skip test_macos]')
15 | runs-on: macOS-12
16 | timeout-minutes: 30
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Select Xcode 14.1.0
20 | run: sudo xcode-select -s /Applications/Xcode_14.1.0.app
21 | - name: Run tests
22 | run: make test
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | Scripts/.installed
7 | Scripts/.bin
8 | DerivedData
9 |
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | --acronyms ID,URL,URI,UUID,ULID,SPM,XML,YAML,JSON,IDFA
2 | --allman false
3 | --assetliterals visual-width
4 | --binarygrouping none
5 | --categorymark "MARK: %c"
6 | --classthreshold 0
7 | --closingparen balanced
8 | --closurevoid remove
9 | --commas always
10 | --conflictmarkers reject
11 | --decimalgrouping none
12 | --elseposition same-line
13 | --emptybraces no-space
14 | --enumnamespaces always
15 | --enumthreshold 0
16 | --exponentcase lowercase
17 | --exponentgrouping disabled
18 | --extensionacl on-declarations
19 | --extensionlength 0
20 | --extensionmark "MARK: - %t + %c"
21 | --fractiongrouping disabled
22 | --fragment false
23 | --funcattributes preserve
24 | --generictypes
25 | --groupedextension "MARK: %c"
26 | --guardelse auto
27 | --header ignore
28 | --hexgrouping none
29 | --hexliteralcase uppercase
30 | --ifdef indent
31 | --importgrouping alpha
32 | --indent 2
33 | --indentcase false
34 | --indentstrings false
35 | --lifecycle
36 | --lineaftermarks true
37 | --linebreaks lf
38 | --markcategories true
39 | --markextensions always
40 | --marktypes always
41 | --maxwidth none
42 | --modifierorder
43 | --nevertrailing
44 | --nospaceoperators
45 | --nowrapoperators
46 | --octalgrouping none
47 | --operatorfunc spaced
48 | --organizetypes actor,class,enum,struct
49 | --patternlet hoist
50 | --ranges spaced
51 | --redundanttype infer-locals-only
52 | --self init-only
53 | --selfrequired
54 | --semicolons never
55 | --shortoptionals always
56 | --smarttabs enabled
57 | --someAny true
58 | --stripunusedargs always
59 | --structthreshold 0
60 | --tabwidth unspecified
61 | --trailingclosures
62 | --trimwhitespace always
63 | --typeattributes preserve
64 | --typeblanklines remove
65 | --typemark "MARK: - %t"
66 | --varattributes preserve
67 | --voidtype void
68 | --wraparguments before-first
69 | --wrapcollections before-first
70 | --wrapconditions preserve
71 | --wrapparameters before-first
72 | --wrapreturntype preserve
73 | --wrapternary default
74 | --wraptypealiases preserve
75 | --xcodeindentation disabled
76 | --yodaswap always
77 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Maxim Krouk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | install_formatter:
2 | @chmod +x ./scripts/install_swift-format.sh
3 | @./scripts/install_swift-format.sh
4 |
5 | update_formatter:
6 | @rm ./scripts/.bin/swift-format
7 | @make install_formatter
8 |
9 | format:
10 | @chmod +x ./scripts/format.sh
11 | @./scripts/format.sh
12 |
13 | test:
14 | @swift test --enable-test-discovery
15 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.6
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "swift-declarative-configuration",
7 | platforms: [
8 | .iOS(.v11),
9 | .macOS(.v10_13),
10 | .tvOS(.v11),
11 | .macCatalyst(.v13),
12 | .watchOS(.v4),
13 | ],
14 | products: [
15 | .library(
16 | name: "DeclarativeConfiguration",
17 | targets: ["DeclarativeConfiguration"]
18 | ),
19 | .library(
20 | name: "FunctionalBuilder",
21 | targets: ["FunctionalBuilder"]
22 | ),
23 | .library(
24 | name: "FunctionalConfigurator",
25 | targets: ["FunctionalConfigurator"]
26 | ),
27 | .library(
28 | name: "FunctionalClosures",
29 | targets: ["FunctionalClosures"]
30 | ),
31 | .library(
32 | name: "FunctionalKeyPath",
33 | targets: ["FunctionalKeyPath"]
34 | ),
35 | .library(
36 | name: "FunctionalModification",
37 | targets: ["FunctionalModification"]
38 | ),
39 | ],
40 | targets: [
41 | .target(
42 | name: "DeclarativeConfiguration",
43 | dependencies: [
44 | .target(name: "FunctionalBuilder"),
45 | .target(name: "FunctionalConfigurator"),
46 | .target(name: "FunctionalClosures"),
47 | .target(name: "FunctionalKeyPath"),
48 | .target(name: "FunctionalModification"),
49 | ]
50 | ),
51 | .target(
52 | name: "FunctionalBuilder",
53 | dependencies: [
54 | .target(name: "FunctionalConfigurator"),
55 | .target(name: "FunctionalKeyPath"),
56 | .target(name: "FunctionalModification"),
57 | ]
58 | ),
59 | .target(
60 | name: "FunctionalConfigurator",
61 | dependencies: [
62 | .target(name: "FunctionalKeyPath"),
63 | .target(name: "FunctionalModification"),
64 | ]
65 | ),
66 | .target(name: "FunctionalClosures"),
67 | .target(
68 | name: "FunctionalKeyPath",
69 | dependencies: [
70 | .target(name: "FunctionalModification"),
71 | ]
72 | ),
73 | .target(name: "FunctionalModification"),
74 | .testTarget(
75 | name: "DeclarativeConfigurationTests",
76 | dependencies: [
77 | .target(name: "DeclarativeConfiguration"),
78 | ]
79 | ),
80 | ]
81 | )
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swift Declarative Configuration
2 |
3 | [](https://github.com/CaptureContext/swift-declarative-configuration/actions/workflows/Test.yml) [](https://swift.org/download/)  [](https://twitter.com/capture_context)
4 |
5 | Swift Declarative Configuration (SDC, for short) is a tiny library, that enables you to configure your objects in a declarative, consistent and understandable way, with ergonomics in mind. It can be used to configure any objects on any platform, including server-side-swift.
6 |
7 | ## Products
8 |
9 | - **[FunctionalModification](./Sources/FunctionalModification)**
10 |
11 | Provides modification functions for copying and modifying immutable stuff. It is useful for self-configuring objects like builder, when modifying methods should return modified `self`.
12 |
13 | - **[FunctionalKeyPath](./Sources/FunctionalKeyPath)**
14 |
15 | Functional KeyPath wrapper.
16 |
17 | - **[FunctionalConfigurator](./Sources/FunctionalConfigurator)**
18 |
19 | Functional configurator for anything, enables you to specify modification of an object and to apply the modification later.
20 |
21 | Also contains self-implementing protocols (`ConfigInitializable`, `CustomConfigurable`) to enable you add custom configuration support for your types (`NSObject` already conforms to it for you).
22 |
23 | - **[FunctionalBuilder](./Sources/FunctionalBuilder)**
24 |
25 | Functional builder for anything, enables you to modify object instances in a declarative way. Also contains `BuilderProvider` protocol with a computed `builder` property and implements that protocol on `NSObject` type.
26 |
27 | - **[FunctionalClosures](./Sources/FunctionalClosures)**
28 |
29 | Functional closures allow you to setup functional handlers & datasources, the API may seem a bit strange at the first look, so feel free to ask or discuss anything [here](https://github.com/MakeupStudio/swift-declarative-configuration/issues/1).
30 |
31 | - **[DeclarativeConfiguration](./Sources/DeclarativeConfiguration)**
32 |
33 | Wraps and exports all the products.
34 |
35 | ## Basic Usage
36 |
37 | > **See tests for more**
38 |
39 | ### No SDC
40 |
41 | ```swift
42 | class ImageViewController: UIViewController {
43 | let imageView: UIImageView = {
44 | let imageView = UIImageView()
45 | imageView.contentMode = .scaleAspectFit
46 | imageView.backgroundColor = .black
47 | imageView.layer.masksToBounds = true
48 | imageView.layer.cornerRadius = 10
49 | return imageView
50 | }()
51 |
52 | override func loadView() {
53 | self.view = imageView
54 | }
55 | }
56 | ```
57 |
58 | ### FunctionalConfigurator
59 |
60 | > **Note:** This way is **recommended**, but remember, that custom types **MUST** implement initializer with no parameters even if the superclass already has it or you will get a crash otherwise.
61 |
62 | ```swift
63 | import FunctionalConfigurator
64 |
65 | class ImageViewController: UIViewController {
66 | let imageView = UIImageView { $0
67 | .contentMode(.scaleAspectFit)
68 | .backgroundColor(.black)
69 | .layer.scope { $0
70 | .masksToBounds(true)
71 | .cornerRadius(10)
72 | }
73 | }
74 |
75 | override func loadView() {
76 | self.view = imageView
77 | }
78 | }
79 | ```
80 |
81 | ### FunctionalBuilder
82 |
83 | > **Note:** This way is recommended too, and it is more **safe**, because it modifies existing objects.
84 |
85 | ```swift
86 | import FunctionalBuilder
87 |
88 | class ImageViewController: UIViewController {
89 | let imageView = UIImageView().builder
90 | .contentMode(.scaleAspectFit)
91 | .backgroundColor(.black)
92 | .layer.masksToBounds(true)
93 | .layer.cornerRadius(10)
94 | .build()
95 |
96 | override func loadView() {
97 | self.view = imageView
98 | }
99 | }
100 | ```
101 |
102 | ### FunctionalClosures
103 |
104 | ### No SDC
105 |
106 | **Declaration**
107 |
108 | ```swift
109 | public class TapGestureRecognizer: UITapGestureRecognizer {
110 | var onTapGesture: ((TapGestureRecognizer) -> Void)?
111 |
112 | init() {
113 | super.init(target: nil, action: nil)
114 | commonInit()
115 | }
116 |
117 | override public init(target: Any?, action: Selector?) {
118 | super.init(target: target, action: action)
119 | commonInit()
120 | }
121 |
122 | private func commonInit() {
123 | self.addTarget(self, action: #selector(handleTap))
124 | }
125 |
126 | @objc private func handleTap(_ recognizer: TapGestureRecognizer) {
127 | onTapGesture?(recognizer)
128 | }
129 | }
130 | ```
131 |
132 | **Usage**
133 |
134 | ```swift
135 | let tapRecognizer = TapGestureRecognizer()
136 |
137 | // handler setup
138 | tapRecognizer.onTapGesture = { recognizer in
139 | // ...
140 | }
141 |
142 | // call from the outside
143 | tapRecognizer.onTapGesture?(tapRecognizer)
144 | ```
145 |
146 | ### With SDC
147 |
148 | **Declaration**
149 |
150 | ```swift
151 | public class TapGestureRecognizer: UITapGestureRecognizer {
152 | @Handler
153 | var onTapGesture
154 |
155 | init() {
156 | super.init(target: nil, action: nil)
157 | commonInit()
158 | }
159 |
160 | override public init(target: Any?, action: Selector?) {
161 | super.init(target: target, action: action)
162 | commonInit()
163 | }
164 |
165 | private func commonInit() {
166 | self.addTarget(self, action: #selector(handleTap))
167 | }
168 |
169 | @objc private func handleTap(_ recognizer: TapGestureRecognizer) {
170 | _onTapGesture(recognizer)
171 | }
172 | }
173 | ```
174 |
175 | **Usage**
176 |
177 | ```swift
178 | let tapRecognizer = TapGestureRecognizer()
179 |
180 | // handler setup now called as function
181 | tapRecognizer.onTapGesture { recognizer in
182 | // ...
183 | }
184 |
185 | // call from the outside now uses propertyWrapper projectedValue API, which is not as straitforward
186 | // and it is nice, because:
187 | // - handlers usually should not be called from the outside
188 | // - you do not lose the ability to call it, but an API tells you that it's kinda private
189 | tapRecognizer.$onTapGesture?(tapRecognizer)
190 | ```
191 |
192 | Also you can create such an instance with `Configurator`:
193 |
194 | ```swift
195 | let tapRecognizer = TapGestureRecognizer { $0
196 | .$onTapGesture { recognizer in
197 | // ...
198 | }
199 | }
200 | ```
201 |
202 | ### More
203 |
204 | #### Builder
205 |
206 | Customize any object by passing initial value to a builder
207 |
208 | ```swift
209 | let object = Builder(Object())
210 | .property.subproperty(value)
211 | .build() // Returns modified object
212 | ```
213 |
214 | For classes you can avoid returning a value by calling `apply` method, instead of `build`
215 |
216 | ```swift
217 | let _class = _Class()
218 | Builder(_class)
219 | .property.subproperty(value)
220 | .apply() // Returns Void
221 | ```
222 |
223 | In both Builders and Configurators you can use scoping
224 |
225 | ```swift
226 | let object = Object { $0
227 | .property.subproperty(value)
228 | }
229 | ```
230 |
231 |
232 |
233 | Conform your own types to `BuilderProvider` protocol to access builder property.
234 |
235 | ```swift
236 | import CoreLocation
237 | import DeclarativeConfiguration
238 |
239 | extension CLLocationCoordinate2D: BuilderProvider {}
240 | // Now you can access `location.builder.latitude(0).build()`
241 | ```
242 |
243 | #### Configurator
244 |
245 | > **Note:** Your NSObject classes **must** implement `init()` to use Configurators. It's a little trade-off for the convenience it brings to your codebase, see [tests](./Tests/DeclarativeConfigurationTests/ConfiguratorTests.swift) for an example.
246 |
247 | #### DataSource
248 |
249 | `OptionalDataSource` and `DataSource` types are very similar to the `Handler`, but if `Handler` is kinda `OptionalDataSource`, the second one may have different types of an output. Usage is similar, different types are provided just for better semantics.
250 |
251 | ## Installation
252 |
253 | ### Basic
254 |
255 | You can add DeclarativeConfiguration to an Xcode project by adding it as a package dependency.
256 |
257 | 1. From the **File** menu, select **Swift Packages › Add Package Dependency…**
258 | 2. Enter [`"https://github.com/makeupstudio/swift-declarative-configuration"`](https://github.com/makeupstudio/swift-declarative-configuration) into the package repository URL text field
259 | 3. Choose products you need to link them to your project.
260 |
261 | ### Recommended
262 |
263 | If you use SwiftPM for your project structure, add DeclarativeConfiguration to your package file.
264 |
265 | ```swift
266 | .package(
267 | url: "git@github.com:capturecontext/swift-declarative-configuration.git",
268 | .upToNextMinor(from: "0.3.0")
269 | )
270 | ```
271 | or via HTTPS
272 |
273 | ```swift
274 | .package(
275 | url: "https://github.com:capturecontext/swift-declarative-configuration.git",
276 | .exact("0.3.0")
277 | )
278 | ```
279 |
280 | Do not forget about target dependencies:
281 |
282 | ```swift
283 | .product(
284 | name: "DeclarativeConfiguration",
285 | package: "swift-declarative-configuration"
286 | )
287 | ```
288 |
289 | ## License
290 |
291 | This library is released under the MIT license. See [LICENSE](./LICENSE) for details.
292 |
--------------------------------------------------------------------------------
/Scripts/format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | _get_parent_dir_abs_path() {
4 | echo "$(cd "$(dirname "$1")" && pwd)"
5 | }
6 |
7 | # ––––––––––––––––––––––––– Constants ––––––––––––––––––––––––––
8 |
9 | SCRIPT_DIR=$(_get_parent_dir_abs_path $0)
10 | TOOLS_DIR="${SCRIPT_DIR}/.bin/"
11 | TOOL_NAME="swiftformat"
12 | TOOL="${TOOLS_DIR}/${TOOL_NAME}"
13 |
14 | # ––––––––––––––––––––––––––– Script –––––––––––––––––––––––––––
15 |
16 | cd ${SCRIPT_DIR}
17 | cd ..
18 |
19 | ${TOOL} . \
20 | --config .swiftformat
21 |
--------------------------------------------------------------------------------
/Scripts/install_swift-format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | _get_parent_dir_abs_path() {
4 | echo "$(cd "$(dirname "$1")" && pwd)"
5 | }
6 |
7 | # ––––––––––––––––––––––––––– Config –––––––––––––––––––––––––––
8 |
9 | TOOL_NAME="swiftformat"
10 | TOOL_OWNER="nicklockwood"
11 | TOOL_VERSION="0.50.5"
12 |
13 | # ––––––––––––––––––––––––– Constants ––––––––––––––––––––––––––
14 |
15 | SCRIPT_DIR=$(_get_parent_dir_abs_path $0)
16 | TOOLS_INSTALL_PATH="${SCRIPT_DIR}/.bin"
17 | TOOL_INSTALL_PATH="${TOOLS_INSTALL_PATH}/${TOOL_NAME}"
18 | TOOL_DOWNLOAD_DIR="${TOOLS_INSTALL_PATH}/_${TOOL_NAME}"
19 |
20 | TOOL=${TOOL_INSTALL_PATH}
21 | TOOL_REPO="https://github.com/${TOOL_OWNER}/${TOOL_NAME}"
22 | ARCHIVE_NAME="${TOOL_NAME}.artifactbundle"
23 | ARCHIVE_URL="${TOOL_REPO}/releases/download/${TOOL_VERSION}/${ARCHIVE_NAME}.zip"
24 |
25 | # ––––––––––––––––––––––––––– Steps ––––––––––––––––––––––––––––
26 |
27 | tool_fetch() {
28 | curl -L ${ARCHIVE_URL} -o "${TOOL_DOWNLOAD_DIR}/${ARCHIVE_NAME}.zip"
29 | }
30 |
31 | tool_extract() {
32 | unzip "${TOOL_DOWNLOAD_DIR}/${ARCHIVE_NAME}.zip" -d ${TOOL_DOWNLOAD_DIR}
33 | }
34 |
35 | tool_install() {
36 | install "${TOOL_DOWNLOAD_DIR}/${ARCHIVE_NAME}/${TOOL_NAME}-${TOOL_VERSION}-macos/bin/${TOOL_NAME}" "${TOOLS_INSTALL_PATH}"
37 | }
38 |
39 | # ––––––––––––––––––––––––––– Script –––––––––––––––––––––––––––
40 |
41 | set_bold=$(tput bold)
42 | set_normal=$(tput sgr0)
43 |
44 | log() {
45 | printf "\n$1 ${set_bold}$2${set_normal}\n"
46 | }
47 |
48 | clean_up() {
49 | rm -rf "${TOOL_DOWNLOAD_DIR}"
50 | }
51 |
52 | set -e
53 | trap clean_up err exit SIGTERM SIGINT
54 |
55 | if [ -f "${TOOL_INSTALL_PATH}" ]; then
56 | log "⚠️" " ${TOOL_NAME} already installed"
57 | exit 0
58 | fi
59 |
60 | if [ ! -d "${TOOL_DOWNLOAD_DIR}" ]; then
61 | mkdir -p "${TOOL_DOWNLOAD_DIR}"
62 | fi
63 |
64 | cd "${TEMP_INSTALL_PATH}"
65 |
66 | log "⬇️" " Fetching ${TOOL_NAME}...\n"
67 |
68 | tool_fetch
69 |
70 | log "📦" " Extracting ${TOOL_NAME}...\n"
71 |
72 | tool_extract
73 |
74 | log "♻️" " Installing ${TOOL_NAME}..."
75 |
76 | tool_install
77 |
78 | log "💧" "Performing cleanup..."
79 | clean_up
80 |
81 | if [ -f "${TOOL_INSTALL_PATH}" ]; then
82 | log "✅" "${TOOL_NAME} successfully installed"
83 | exit 0
84 | fi
85 |
86 | log "🚫" "${TOOL_NAME} failed to install"
87 |
--------------------------------------------------------------------------------
/Sources/DeclarativeConfiguration/Exports.swift:
--------------------------------------------------------------------------------
1 | @_exported import FunctionalBuilder
2 | @_exported import FunctionalClosures
3 | @_exported import FunctionalConfigurator
4 | @_exported import FunctionalKeyPath
5 | @_exported import FunctionalModification
6 |
--------------------------------------------------------------------------------
/Sources/FunctionalBuilder/Builder.swift:
--------------------------------------------------------------------------------
1 | import FunctionalConfigurator
2 | import FunctionalKeyPath
3 | import FunctionalModification
4 |
5 | @dynamicMemberLookup
6 | public struct Builder {
7 | @usableFromInline
8 | internal var _initialValue: () -> Base
9 |
10 | @usableFromInline
11 | internal var _configurator: Configurator
12 |
13 | @inlinable
14 | public var base: Base { _initialValue() }
15 |
16 | @inlinable
17 | public func build() -> Base { _configurator.configured(base) }
18 |
19 | @inlinable
20 | public func apply() where Base: AnyObject { _ = build() }
21 |
22 | // /// Applies modification to a new builder, created with a built object.
23 | // @inlinable
24 | // public func reinforce(
25 | // _ transform: @escaping (inout Base) -> Void
26 | // ) -> Builder {
27 | // Builder(build()).set(transform)
28 | // }
29 |
30 | /// Applies modification to a new builder, created with a built object, also passes leading parameters to transform function.
31 | @inlinable
32 | public func reinforce(
33 | _ args: repeat each Arg,
34 | transform: @escaping (inout Base, repeat each Arg) -> Void
35 | ) -> Builder {
36 | Builder(build()).set { base in transform(&base, repeat each args) }
37 | }
38 |
39 | // /// Applies modification to a new builder, created with a built object, also passes leading parameters to transform function.
40 | // @inlinable
41 | // public func reinforce(
42 | // _ t0: T0,
43 | // t1: T1,
44 | // _ transform: @escaping (inout Base, T0, T1) -> Void
45 | // ) -> Builder {
46 | // reinforce { base in transform(&base, t0, t1) }
47 | // }
48 | //
49 | // /// Applies modification to a new builder, created with a built object, also passes leading parameters to transform function.
50 | // @inlinable
51 | // public func reinforce(
52 | // _ t0: T0,
53 | // _ t1: T1,
54 | // _ t2: T2,
55 | // _ transform: @escaping (inout Base, T0, T1, T2) -> Void
56 | // ) -> Builder {
57 | // reinforce { base in transform(&base, t0, t1, t2) }
58 | // }
59 |
60 | @inlinable
61 | public func combined(with builder: Builder) -> Builder {
62 | Builder(
63 | _initialValue,
64 | _configurator.combined(with: builder._configurator)
65 | )
66 | }
67 |
68 | @inlinable
69 | public func combined(with configurator: Configurator) -> Builder {
70 | Builder(
71 | _initialValue,
72 | _configurator.combined(with: configurator)
73 | )
74 | }
75 |
76 | /// Creates a new instance of builder with initial value
77 | @inlinable
78 | public init(_ initialValue: @escaping @autoclosure () -> Base) {
79 | self.init(
80 | initialValue,
81 | Configurator()
82 | )
83 | }
84 |
85 | @usableFromInline
86 | internal init(
87 | _ initialValue: @escaping () -> Base,
88 | _ configurator: Configurator
89 | ) {
90 | self._initialValue = initialValue
91 | self._configurator = configurator
92 | }
93 |
94 | /// Appends transformation to current configuration
95 | @inlinable
96 | public func set(
97 | _ transform: @escaping (inout Base) -> Void
98 | ) -> Builder {
99 | Builder(
100 | _initialValue,
101 | _configurator.set(transform)
102 | )
103 | }
104 |
105 | @inlinable
106 | public subscript(
107 | dynamicMember keyPath: WritableKeyPath
108 | ) -> CallableBlock {
109 | CallableBlock(
110 | builder: self,
111 | keyPath: FunctionalKeyPath(keyPath)
112 | )
113 | }
114 |
115 | @inlinable
116 | public subscript(
117 | dynamicMember keyPath: KeyPath
118 | ) -> NonCallableBlock {
119 | NonCallableBlock(
120 | builder: self,
121 | keyPath: .getonly(keyPath)
122 | )
123 | }
124 |
125 | @inlinable
126 | public subscript(
127 | dynamicMember keyPath: WritableKeyPath
128 | ) -> CallableBlock where Base == Wrapped? {
129 | CallableBlock(
130 | builder: self,
131 | keyPath: FunctionalKeyPath(keyPath).optional()
132 | )
133 | }
134 |
135 | @inlinable
136 | public subscript(
137 | dynamicMember keyPath: KeyPath
138 | ) -> NonCallableBlock where Base == Wrapped? {
139 | NonCallableBlock(
140 | builder: self,
141 | keyPath: FunctionalKeyPath.getonly(keyPath).optional()
142 | )
143 | }
144 | }
145 |
146 | extension Builder {
147 | @dynamicMemberLookup
148 | public struct CallableBlock {
149 | @usableFromInline
150 | internal var _block: NonCallableBlock
151 |
152 | @usableFromInline
153 | internal init(
154 | builder: Builder,
155 | keyPath: FunctionalKeyPath
156 | ) {
157 | self._block = .init(
158 | builder: builder,
159 | keyPath: keyPath
160 | )
161 | }
162 |
163 | @inlinable
164 | public func callAsFunction(
165 | if condition: Bool,
166 | then thenValue: @escaping @autoclosure () -> Value
167 | ) -> Builder {
168 | Builder(
169 | _block.builder._initialValue,
170 | _block.builder._configurator.appendingConfiguration { base in
171 | if condition {
172 | return _block.keyPath.embed(thenValue(), in: base)
173 | } else {
174 | return base
175 | }
176 | }
177 | )
178 | }
179 |
180 | @inlinable
181 | public func scope(_ builder: @escaping (Builder) -> Builder) -> Builder {
182 | Builder(
183 | _block.builder._initialValue,
184 | _block.builder._configurator.appendingConfiguration { base in
185 | _block.keyPath.embed(
186 | builder(.init(_block.keyPath.extract(from: base))).build(),
187 | in: base
188 | )
189 | }
190 | )
191 | }
192 |
193 | @inlinable
194 | public func ifLetScope(
195 | _ builder: @escaping (Builder) -> Builder
196 | ) -> Builder where Value == Wrapped? {
197 | Builder(
198 | _block.builder._initialValue,
199 | _block.builder._configurator.appendingConfiguration { base in
200 | guard let value = _block.keyPath.extract(from: base) else { return base }
201 | return _block.keyPath.embed(
202 | builder(.init(value)).build(),
203 | in: base
204 | )
205 | }
206 | )
207 | }
208 |
209 | @inlinable
210 | public func callAsFunction(
211 | if condition: Bool,
212 | then thenValue: @escaping @autoclosure () -> Value,
213 | else elseValue: (() -> Value)? = nil
214 | ) -> Builder {
215 | Builder(
216 | _block.builder._initialValue,
217 | _block.builder._configurator.appendingConfiguration { base in
218 | if condition {
219 | return _block.keyPath.embed(thenValue(), in: base)
220 | } else if let value = elseValue?() {
221 | return _block.keyPath.embed(value, in: base)
222 | } else {
223 | return base
224 | }
225 | }
226 | )
227 | }
228 |
229 | @inlinable
230 | public func callAsFunction(_ value: @escaping @autoclosure () -> Value) -> Builder {
231 | Builder(
232 | _block.builder._initialValue,
233 | _block.builder._configurator.appendingConfiguration { base in
234 | _block.keyPath.embed(value(), in: base)
235 | }
236 | )
237 | }
238 |
239 | @inlinable
240 | public func set(_ transform: @escaping (inout Value) -> Void) -> Builder {
241 | Builder(
242 | _block.builder._initialValue,
243 | _block.builder._configurator.appendingConfiguration { base in
244 | _block.keyPath.embed(
245 | reduce(_block.keyPath.extract(from: base), with: transform),
246 | in: base
247 | )
248 | }
249 | )
250 | }
251 |
252 | @inlinable
253 | public subscript(
254 | dynamicMember keyPath: WritableKeyPath
255 | ) -> CallableBlock {
256 | CallableBlock(
257 | builder: _block.builder,
258 | keyPath: _block.keyPath.appending(path: .init(keyPath))
259 | )
260 | }
261 |
262 | @inlinable
263 | public subscript(
264 | dynamicMember keyPath: KeyPath
265 | ) -> NonCallableBlock {
266 | _block[dynamicMember: keyPath]
267 | }
268 |
269 | @inlinable
270 | public subscript(
271 | dynamicMember keyPath: WritableKeyPath
272 | ) -> CallableBlock where Value == Wrapped? {
273 | CallableBlock(
274 | builder: _block.builder,
275 | keyPath: _block.keyPath.appending(
276 | path: FunctionalKeyPath(keyPath).optional()
277 | )
278 | )
279 | }
280 |
281 | @inlinable
282 | public subscript(
283 | dynamicMember keyPath: KeyPath
284 | ) -> NonCallableBlock where Value == Wrapped? {
285 | NonCallableBlock(
286 | builder: _block.builder,
287 | keyPath: _block.keyPath.appending(
288 | path: FunctionalKeyPath.getonly(keyPath).optional()
289 | )
290 | )
291 | }
292 | }
293 |
294 | @dynamicMemberLookup
295 | public struct NonCallableBlock {
296 | @usableFromInline
297 | internal var builder: Builder
298 |
299 | @usableFromInline
300 | internal var keyPath: FunctionalKeyPath
301 |
302 | @usableFromInline
303 | internal init(
304 | builder: Builder,
305 | keyPath: FunctionalKeyPath
306 | ) {
307 | self.builder = builder
308 | self.keyPath = keyPath
309 | }
310 |
311 | @inlinable
312 | public func scope(
313 | _ builder: @escaping (Builder) -> Builder
314 | ) -> Builder where Value: AnyObject {
315 | Builder(
316 | self.builder._initialValue,
317 | self.builder._configurator.appendingConfiguration { base in
318 | keyPath.embed(
319 | builder(.init(keyPath.extract(from: base))).build(),
320 | in: base
321 | )
322 | }
323 | )
324 | }
325 |
326 | @inlinable
327 | public func ifLetScope(
328 | _ builder: @escaping (Builder) -> Builder
329 | ) -> Builder where Wrapped: AnyObject, Value == Wrapped? {
330 | Builder(
331 | self.builder._initialValue,
332 | self.builder._configurator.appendingConfiguration { base in
333 | guard let value = keyPath.extract(from: base) else { return base }
334 | return keyPath.embed(
335 | builder(.init(value)).build(),
336 | in: base
337 | )
338 | }
339 | )
340 | }
341 |
342 | @inlinable
343 | public subscript(
344 | dynamicMember keyPath: ReferenceWritableKeyPath
345 | ) -> CallableBlock {
346 | CallableBlock(
347 | builder: self.builder,
348 | keyPath: self.keyPath.appending(path: .init(keyPath))
349 | )
350 | }
351 |
352 | @inlinable
353 | public subscript(
354 | dynamicMember keyPath: KeyPath
355 | ) -> NonCallableBlock {
356 | NonCallableBlock(
357 | builder: self.builder,
358 | keyPath: self.keyPath.appending(path: .getonly(keyPath))
359 | )
360 | }
361 |
362 | @inlinable
363 | public subscript(
364 | dynamicMember keyPath: ReferenceWritableKeyPath
365 | ) -> CallableBlock where Value == Wrapped? {
366 | CallableBlock(
367 | builder: self.builder,
368 | keyPath: self.keyPath.appending(
369 | path: FunctionalKeyPath(keyPath).optional()
370 | )
371 | )
372 | }
373 |
374 | @inlinable
375 | public subscript(
376 | dynamicMember keyPath: KeyPath
377 | ) -> NonCallableBlock where Value == Wrapped? {
378 | NonCallableBlock(
379 | builder: self.builder,
380 | keyPath: self.keyPath.appending(
381 | path: FunctionalKeyPath.getonly(keyPath).optional()
382 | )
383 | )
384 | }
385 | }
386 | }
387 |
--------------------------------------------------------------------------------
/Sources/FunctionalBuilder/BuilderProvider.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public protocol BuilderProvider {}
4 | extension BuilderProvider {
5 | @inlinable
6 | public var builder: Builder { .init(self) }
7 | }
8 |
9 | extension NSObject: BuilderProvider {}
10 |
--------------------------------------------------------------------------------
/Sources/FunctionalClosures/DataSource.swift:
--------------------------------------------------------------------------------
1 | /// A wrapper for clusure-based interaction between objects
2 | ///
3 | /// Provides a public API to set internal closure-based datasource with a functional API
4 | @propertyWrapper
5 | public class DataSource {
6 | public struct Container {
7 | @usableFromInline
8 | internal var action: (Input) -> Output
9 |
10 | public init(action: @escaping (Input) -> Output) {
11 | self.action = action
12 | }
13 |
14 | @inlinable
15 | public mutating func callAsFunction(perform action: @escaping (Input) -> Output) {
16 | self.action = action
17 | }
18 | }
19 |
20 | public init(wrappedValue: Container) {
21 | self.wrappedValue = wrappedValue
22 | }
23 |
24 | public var wrappedValue: Container
25 |
26 | @inlinable
27 | public var projectedValue: (Input) -> Output {
28 | get { wrappedValue.action }
29 | set { wrappedValue.action = newValue }
30 | }
31 |
32 | @inlinable
33 | public func callAsFunction(_ input: Input) -> Output? {
34 | projectedValue(input)
35 | }
36 |
37 | @inlinable
38 | public func callAsFunction() -> Output where Input == Void {
39 | projectedValue(())
40 | }
41 | }
42 |
43 | /// A wrapper for clusure-based interaction between objects
44 | ///
45 | /// Provides a public API to set internal closure-based datasource with a functional API
46 | @propertyWrapper
47 | public class OptionalDataSource {
48 | public struct Container {
49 | @usableFromInline
50 | internal var action: ((Input) -> Output)?
51 |
52 | internal init() {}
53 |
54 | public init(action: ((Input) -> Output)?) {
55 | self.action = action
56 | }
57 |
58 | @inlinable
59 | public mutating func callAsFunction(perform action: ((Input) -> Output)?) {
60 | self.action = action
61 | }
62 | }
63 |
64 | public init() {}
65 |
66 | public init(wrappedValue: Container) {
67 | self.wrappedValue = wrappedValue
68 | }
69 |
70 | public var wrappedValue: Container = .init()
71 |
72 | @inlinable
73 | public var projectedValue: ((Input) -> Output)? {
74 | get { wrappedValue.action }
75 | set { wrappedValue.action = newValue }
76 | }
77 |
78 | @inlinable
79 | public func callAsFunction(_ input: Input) -> Output? {
80 | projectedValue?(input)
81 | }
82 |
83 | @inlinable
84 | public func callAsFunction() -> Output? where Input == Void {
85 | projectedValue?(())
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Sources/FunctionalClosures/Deprecations/Assign.swift:
--------------------------------------------------------------------------------
1 | @available(*, deprecated, message: """
2 | Will be removed in future versions of swift-declarative-configuration
3 | """)
4 | @inlinable
5 | public func assign(
6 | to root: Root,
7 | _ keyPath: ReferenceWritableKeyPath
8 | ) -> (T0) -> Void {
9 | { [weak root] result in root?[keyPath: keyPath] = result }
10 | }
11 |
12 | @available(*, deprecated, message: """
13 | Will be removed in future versions of swift-declarative-configuration
14 | """)
15 | @inlinable
16 | public func assignFirst(
17 | to root: Root,
18 | _ keyPath: ReferenceWritableKeyPath
19 | ) -> (T0, T1) -> Void {
20 | { [weak root] result, _ in root?[keyPath: keyPath] = result }
21 | }
22 |
23 | @available(*, deprecated, message: """
24 | Will be removed in future versions of swift-declarative-configuration
25 | """)
26 | @inlinable
27 | public func assignFirst(
28 | to root: Root,
29 | _ keyPath: ReferenceWritableKeyPath
30 | ) -> (T0, T1, T2) -> Void {
31 | { [weak root] result, _, _ in root?[keyPath: keyPath] = result }
32 | }
33 |
34 | @available(*, deprecated, message: """
35 | Will be removed in future versions of swift-declarative-configuration
36 | """)
37 | @inlinable
38 | public func assignFirst(
39 | to root: Root,
40 | _ keyPath: ReferenceWritableKeyPath
41 | ) -> (T0, T1, T2, T3) -> Void {
42 | { [weak root] result, _, _, _ in root?[keyPath: keyPath] = result }
43 | }
44 |
45 | @available(*, deprecated, message: """
46 | Will be removed in future versions of swift-declarative-configuration
47 | """)
48 | @inlinable
49 | public func assignFirst(
50 | to root: Root,
51 | _ keyPath: ReferenceWritableKeyPath
52 | ) -> (T0, T1, T2, T3, T4) -> Void {
53 | { [weak root] result, _, _, _, _ in root?[keyPath: keyPath] = result }
54 | }
55 |
56 | @available(*, deprecated, message: """
57 | Will be removed in future versions of swift-declarative-configuration
58 | """)
59 | @inlinable
60 | public func assignSecond(
61 | to root: Root,
62 | _ keyPath: ReferenceWritableKeyPath
63 | ) -> (T0, T1) -> Void {
64 | { [weak root] _, result in root?[keyPath: keyPath] = result }
65 | }
66 |
67 | @available(*, deprecated, message: """
68 | Will be removed in future versions of swift-declarative-configuration
69 | """)
70 | @inlinable
71 | public func assignSecond(
72 | to root: Root,
73 | _ keyPath: ReferenceWritableKeyPath
74 | ) -> (T0, T1, T2) -> Void {
75 | { [weak root] _, result, _ in root?[keyPath: keyPath] = result }
76 | }
77 |
78 | @available(*, deprecated, message: """
79 | Will be removed in future versions of swift-declarative-configuration
80 | """)
81 | @inlinable
82 | public func assignSecond(
83 | to root: Root,
84 | _ keyPath: ReferenceWritableKeyPath
85 | ) -> (T0, T1, T2, T3) -> Void {
86 | { [weak root] _, result, _, _ in root?[keyPath: keyPath] = result }
87 | }
88 |
89 | @available(*, deprecated, message: """
90 | Will be removed in future versions of swift-declarative-configuration
91 | """)
92 | @inlinable
93 | public func assignSecond(
94 | to root: Root,
95 | _ keyPath: ReferenceWritableKeyPath
96 | ) -> (T0, T1, T2, T3, T4) -> Void {
97 | { [weak root] _, result, _, _, _ in root?[keyPath: keyPath] = result }
98 | }
99 |
100 | @available(*, deprecated, message: """
101 | Will be removed in future versions of swift-declarative-configuration
102 | """)
103 | @inlinable
104 | public func assignThird(
105 | to root: Root,
106 | _ keyPath: ReferenceWritableKeyPath
107 | ) -> (T0, T1, T2) -> Void {
108 | { [weak root] _, _, result in root?[keyPath: keyPath] = result }
109 | }
110 |
111 | @available(*, deprecated, message: """
112 | Will be removed in future versions of swift-declarative-configuration
113 | """)
114 | @inlinable
115 | public func assignThird(
116 | to root: Root,
117 | _ keyPath: ReferenceWritableKeyPath
118 | ) -> (T0, T1, T2, T3) -> Void {
119 | { [weak root] _, _, result, _ in root?[keyPath: keyPath] = result }
120 | }
121 |
122 | @available(*, deprecated, message: """
123 | Will be removed in future versions of swift-declarative-configuration
124 | """)
125 | @inlinable
126 | public func assignThird(
127 | to root: Root,
128 | _ keyPath: ReferenceWritableKeyPath
129 | ) -> (T0, T1, T2, T3, T4) -> Void {
130 | { [weak root] _, _, result, _, _ in root?[keyPath: keyPath] = result }
131 | }
132 |
133 | @available(*, deprecated, message: """
134 | Will be removed in future versions of swift-declarative-configuration
135 | """)
136 | @inlinable
137 | public func assignFourth(
138 | to root: Root,
139 | _ keyPath: ReferenceWritableKeyPath
140 | ) -> (T0, T1, T2, T3) -> Void {
141 | { [weak root] _, _, _, result in root?[keyPath: keyPath] = result }
142 | }
143 |
144 | @available(*, deprecated, message: """
145 | Will be removed in future versions of swift-declarative-configuration
146 | """)
147 | @inlinable
148 | public func assignFourth(
149 | to root: Root,
150 | _ keyPath: ReferenceWritableKeyPath
151 | ) -> (T0, T1, T2, T3, T4) -> Void {
152 | { [weak root] _, _, _, result, _ in root?[keyPath: keyPath] = result }
153 | }
154 |
155 | @available(*, deprecated, message: """
156 | Will be removed in future versions of swift-declarative-configuration
157 | """)
158 | @inlinable
159 | public func assignFifth(
160 | to root: Root,
161 | _ keyPath: ReferenceWritableKeyPath
162 | ) -> (T0, T1, T2, T3, T4) -> Void {
163 | { [weak root] _, _, _, _, result in root?[keyPath: keyPath] = result }
164 | }
165 |
166 | @available(*, deprecated, message: """
167 | Will be removed in future versions of swift-declarative-configuration
168 | """)
169 | @inlinable
170 | public func assign(
171 | to root: Root,
172 | _ keyPath: ReferenceWritableKeyPath
173 | ) -> (T0) -> Void {
174 | { [weak root] result in root?[keyPath: keyPath] = result }
175 | }
176 |
177 | @available(*, deprecated, message: """
178 | Will be removed in future versions of swift-declarative-configuration
179 | """)
180 | @inlinable
181 | public func assignFirst(
182 | to root: Root,
183 | _ keyPath: ReferenceWritableKeyPath
184 | ) -> (T0, T1) -> Void {
185 | { [weak root] result, _ in root?[keyPath: keyPath] = result }
186 | }
187 |
188 | @available(*, deprecated, message: """
189 | Will be removed in future versions of swift-declarative-configuration
190 | """)
191 | @inlinable
192 | public func assignFirst(
193 | to root: Root,
194 | _ keyPath: ReferenceWritableKeyPath
195 | ) -> (T0, T1, T2) -> Void {
196 | { [weak root] result, _, _ in root?[keyPath: keyPath] = result }
197 | }
198 |
199 | @available(*, deprecated, message: """
200 | Will be removed in future versions of swift-declarative-configuration
201 | """)
202 | @inlinable
203 | public func assignFirst(
204 | to root: Root,
205 | _ keyPath: ReferenceWritableKeyPath
206 | ) -> (T0, T1, T2, T3) -> Void {
207 | { [weak root] result, _, _, _ in root?[keyPath: keyPath] = result }
208 | }
209 |
210 | @available(*, deprecated, message: """
211 | Will be removed in future versions of swift-declarative-configuration
212 | """)
213 | @inlinable
214 | public func assignFirst(
215 | to root: Root,
216 | _ keyPath: ReferenceWritableKeyPath
217 | ) -> (T0, T1, T2, T3, T4) -> Void {
218 | { [weak root] result, _, _, _, _ in root?[keyPath: keyPath] = result }
219 | }
220 |
221 | @available(*, deprecated, message: """
222 | Will be removed in future versions of swift-declarative-configuration
223 | """)
224 | @inlinable
225 | public func assignSecond(
226 | to root: Root,
227 | _ keyPath: ReferenceWritableKeyPath
228 | ) -> (T0, T1) -> Void {
229 | { [weak root] _, result in root?[keyPath: keyPath] = result }
230 | }
231 |
232 | @available(*, deprecated, message: """
233 | Will be removed in future versions of swift-declarative-configuration
234 | """)
235 | @inlinable
236 | public func assignSecond(
237 | to root: Root,
238 | _ keyPath: ReferenceWritableKeyPath
239 | ) -> (T0, T1, T2) -> Void {
240 | { [weak root] _, result, _ in root?[keyPath: keyPath] = result }
241 | }
242 |
243 | @available(*, deprecated, message: """
244 | Will be removed in future versions of swift-declarative-configuration
245 | """)
246 | @inlinable
247 | public func assignSecond(
248 | to root: Root,
249 | _ keyPath: ReferenceWritableKeyPath
250 | ) -> (T0, T1, T2, T3) -> Void {
251 | { [weak root] _, result, _, _ in root?[keyPath: keyPath] = result }
252 | }
253 |
254 | @available(*, deprecated, message: """
255 | Will be removed in future versions of swift-declarative-configuration
256 | """)
257 | @inlinable
258 | public func assignSecond(
259 | to root: Root,
260 | _ keyPath: ReferenceWritableKeyPath
261 | ) -> (T0, T1, T2, T3, T4) -> Void {
262 | { [weak root] _, result, _, _, _ in root?[keyPath: keyPath] = result }
263 | }
264 |
265 | @available(*, deprecated, message: """
266 | Will be removed in future versions of swift-declarative-configuration
267 | """)
268 | @inlinable
269 | public func assignThird(
270 | to root: Root,
271 | _ keyPath: ReferenceWritableKeyPath
272 | ) -> (T0, T1, T2) -> Void {
273 | { [weak root] _, _, result in root?[keyPath: keyPath] = result }
274 | }
275 |
276 | @available(*, deprecated, message: """
277 | Will be removed in future versions of swift-declarative-configuration
278 | """)
279 | @inlinable
280 | public func assignThird(
281 | to root: Root,
282 | _ keyPath: ReferenceWritableKeyPath
283 | ) -> (T0, T1, T2, T3) -> Void {
284 | { [weak root] _, _, result, _ in root?[keyPath: keyPath] = result }
285 | }
286 |
287 | @available(*, deprecated, message: """
288 | Will be removed in future versions of swift-declarative-configuration
289 | """)
290 | @inlinable
291 | public func assignThird(
292 | to root: Root,
293 | _ keyPath: ReferenceWritableKeyPath
294 | ) -> (T0, T1, T2, T3, T4) -> Void {
295 | { [weak root] _, _, result, _, _ in root?[keyPath: keyPath] = result }
296 | }
297 |
298 | @available(*, deprecated, message: """
299 | Will be removed in future versions of swift-declarative-configuration
300 | """)
301 | @inlinable
302 | public func assignFourth(
303 | to root: Root,
304 | _ keyPath: ReferenceWritableKeyPath
305 | ) -> (T0, T1, T2, T3) -> Void {
306 | { [weak root] _, _, _, result in root?[keyPath: keyPath] = result }
307 | }
308 |
309 | @available(*, deprecated, message: """
310 | Will be removed in future versions of swift-declarative-configuration
311 | """)
312 | @inlinable
313 | public func assignFourth(
314 | to root: Root,
315 | _ keyPath: ReferenceWritableKeyPath
316 | ) -> (T0, T1, T2, T3, T4) -> Void {
317 | { [weak root] _, _, _, result, _ in root?[keyPath: keyPath] = result }
318 | }
319 |
320 | @available(*, deprecated, message: """
321 | Will be removed in future versions of swift-declarative-configuration, \
322 | probably will move to `capturecontext/swift-prelude` when
323 | """)
324 | @inlinable
325 | public func assignFifth(
326 | to root: Root,
327 | _ keyPath: ReferenceWritableKeyPath
328 | ) -> (T0, T1, T2, T3, T4) -> Void {
329 | { [weak root] _, _, _, _, result in root?[keyPath: keyPath] = result }
330 | }
331 |
--------------------------------------------------------------------------------
/Sources/FunctionalClosures/Handler.swift:
--------------------------------------------------------------------------------
1 | public enum HandlerContainerBehaviour {
2 | case resetting
3 | case preceding
4 | case appending
5 | }
6 |
7 | public typealias Handler = Handler1
8 |
9 | /// A wrapper for clusure-based interaction between objects
10 | ///
11 | /// Provides a public API to set internal closure-based hanlder or delegate with a functional API
12 | @propertyWrapper
13 | public class Handler1 {
14 | public struct Container {
15 | public typealias Behaviour = HandlerContainerBehaviour
16 |
17 | @usableFromInline
18 | internal var action: ((Input) -> Void)?
19 |
20 | internal init() {}
21 |
22 | public init(action: ((Input) -> Void)?) {
23 | self.action = action
24 | }
25 |
26 | @inlinable
27 | public mutating func callAsFunction(perform action: ((Input) -> Void)?) {
28 | self.action = action
29 | }
30 |
31 | @available(
32 | *,
33 | deprecated,
34 | message:
35 | """
36 | This API will be removed, \
37 | consider using redeclaration with `(Input) -> Output` signature function. \
38 | Feel free to discuss the API here \
39 | https://github.com/CaptureContext/swift-declarative-configuration/issues/1
40 | """
41 | )
42 | @inlinable
43 | public mutating func callAsFunction(_ behaviour: Behaviour, perform action: ((Input) -> Void)?)
44 | {
45 | switch behaviour {
46 | case .resetting:
47 | self.action = action
48 | case .preceding:
49 | let oldAction = self.action
50 | self.action = { input in
51 | action?(input)
52 | oldAction?(input)
53 | }
54 | case .appending:
55 | let oldAction = self.action
56 | self.action = { input in
57 | action?(input)
58 | oldAction?(input)
59 | }
60 | }
61 | }
62 | }
63 |
64 | public init() {}
65 |
66 | public init(wrappedValue: Container) {
67 | self.wrappedValue = wrappedValue
68 | }
69 |
70 | public var wrappedValue: Container = .init()
71 |
72 | public var projectedValue: ((Input) -> Void)? {
73 | get { wrappedValue.action }
74 | set { wrappedValue.action = newValue }
75 | }
76 |
77 | public func callAsFunction(_ input: Input) {
78 | projectedValue?(input)
79 | }
80 |
81 | public func callAsFunction() where Input == Void {
82 | projectedValue?(())
83 | }
84 | }
85 |
86 | // MARK: Typed handlers
87 |
88 | /// A wrapper for clusure-based interaction between objects
89 | ///
90 | /// Provides a public API to set internal closure-based hanlder or delegate with a functional API
91 | @propertyWrapper
92 | public class Handler2 {
93 | public struct Container {
94 | public typealias Behaviour = HandlerContainerBehaviour
95 |
96 | internal var action: ((T0, T1) -> Void)?
97 |
98 | internal init() {}
99 |
100 | public init(action: ((T0, T1) -> Void)?) {
101 | self.action = action
102 | }
103 |
104 | public mutating func callAsFunction(perform action: ((T0, T1) -> Void)?) {
105 | self.action = action
106 | }
107 |
108 | @available(
109 | *,
110 | deprecated,
111 | message:
112 | """
113 | This API will be removed, \
114 | consider using redeclaration with `(Input) -> Output` signature function. \
115 | Feel free to discuss the API here \
116 | https://github.com/MakeupStudio/swift-declarative-configuration/issues/1
117 | """
118 | )
119 | public mutating func callAsFunction(_ behaviour: Behaviour, perform action: ((T0, T1) -> Void)?)
120 | {
121 | switch behaviour {
122 | case .resetting:
123 | self.action = action
124 | case .preceding:
125 | let oldAction = self.action
126 | self.action = { t0, t1 in
127 | action?(t0, t1)
128 | oldAction?(t0, t1)
129 | }
130 | case .appending:
131 | let oldAction = self.action
132 | self.action = { t0, t1 in
133 | action?(t0, t1)
134 | oldAction?(t0, t1)
135 | }
136 | }
137 | }
138 | }
139 |
140 | public init() {}
141 |
142 | public init(wrappedValue: Container) {
143 | self.wrappedValue = wrappedValue
144 | }
145 |
146 | public var wrappedValue: Container = .init()
147 |
148 | public var projectedValue: ((T0, T1) -> Void)? {
149 | get { wrappedValue.action }
150 | set { wrappedValue.action = newValue }
151 | }
152 |
153 | public func callAsFunction(_ t0: T0, _ t1: T1) {
154 | projectedValue?(t0, t1)
155 | }
156 | }
157 |
158 | /// A wrapper for clusure-based interaction between objects
159 | ///
160 | /// Provides a public API to set internal closure-based hanlder or delegate with a functional API
161 | @propertyWrapper
162 | public class Handler3 {
163 | public struct Container {
164 | public typealias Behaviour = HandlerContainerBehaviour
165 |
166 | internal var action: ((T0, T1, T2) -> Void)?
167 |
168 | internal init() {}
169 |
170 | public init(action: ((T0, T1, T2) -> Void)?) {
171 | self.action = action
172 | }
173 |
174 | public mutating func callAsFunction(perform action: ((T0, T1, T2) -> Void)?) {
175 | self.action = action
176 | }
177 |
178 | @available(
179 | *,
180 | deprecated,
181 | message:
182 | """
183 | This API will be removed, \
184 | consider using redeclaration with `(Input) -> Output` signature function. \
185 | Feel free to discuss the API here \
186 | https://github.com/MakeupStudio/swift-declarative-configuration/issues/1
187 | """
188 | )
189 | public mutating func callAsFunction(
190 | _ behaviour: Behaviour,
191 | perform action: ((T0, T1, T2) -> Void)?
192 | ) {
193 | switch behaviour {
194 | case .resetting:
195 | self.action = action
196 | case .preceding:
197 | let oldAction = self.action
198 | self.action = { t0, t1, t2 in
199 | action?(t0, t1, t2)
200 | oldAction?(t0, t1, t2)
201 | }
202 | case .appending:
203 | let oldAction = self.action
204 | self.action = { t0, t1, t2 in
205 | action?(t0, t1, t2)
206 | oldAction?(t0, t1, t2)
207 | }
208 | }
209 | }
210 | }
211 |
212 | public init() {}
213 |
214 | public init(wrappedValue: Container) {
215 | self.wrappedValue = wrappedValue
216 | }
217 |
218 | public var wrappedValue: Container = .init()
219 |
220 | public var projectedValue: ((T0, T1, T2) -> Void)? {
221 | get { wrappedValue.action }
222 | set { wrappedValue.action = newValue }
223 | }
224 |
225 | public func callAsFunction(_ t0: T0, _ t1: T1, _ t2: T2) {
226 | projectedValue?(t0, t1, t2)
227 | }
228 | }
229 |
230 | /// A wrapper for clusure-based interaction between objects
231 | ///
232 | /// Provides a public API to set internal closure-based hanlder or delegate with a functional API
233 | @propertyWrapper
234 | public class Handler4 {
235 | public struct Container {
236 | public typealias Behaviour = HandlerContainerBehaviour
237 |
238 | internal var action: ((T0, T1, T2, T3) -> Void)?
239 |
240 | internal init() {}
241 |
242 | public init(action: ((T0, T1, T2, T3) -> Void)?) {
243 | self.action = action
244 | }
245 |
246 | public mutating func callAsFunction(perform action: ((T0, T1, T2, T3) -> Void)?) {
247 | self.action = action
248 | }
249 |
250 | @available(
251 | *,
252 | deprecated,
253 | message:
254 | """
255 | This API will be removed, \
256 | consider using redeclaration with `(Input) -> Output` signature function. \
257 | Feel free to discuss the API here \
258 | https://github.com/MakeupStudio/swift-declarative-configuration/issues/1
259 | """
260 | )
261 | public mutating func callAsFunction(
262 | _ behaviour: Behaviour,
263 | perform action: ((T0, T1, T2, T3) -> Void)?
264 | ) {
265 | switch behaviour {
266 | case .resetting:
267 | self.action = action
268 | case .preceding:
269 | let oldAction = self.action
270 | self.action = { t0, t1, t2, t3 in
271 | action?(t0, t1, t2, t3)
272 | oldAction?(t0, t1, t2, t3)
273 | }
274 | case .appending:
275 | let oldAction = self.action
276 | self.action = { t0, t1, t2, t3 in
277 | action?(t0, t1, t2, t3)
278 | oldAction?(t0, t1, t2, t3)
279 | }
280 | }
281 | }
282 | }
283 |
284 | public init() {}
285 |
286 | public init(wrappedValue: Container) {
287 | self.wrappedValue = wrappedValue
288 | }
289 |
290 | public var wrappedValue: Container = .init()
291 |
292 | public var projectedValue: ((T0, T1, T2, T3) -> Void)? {
293 | get { wrappedValue.action }
294 | set { wrappedValue.action = newValue }
295 | }
296 |
297 | public func callAsFunction(_ t0: T0, _ t1: T1, _ t2: T2, _ t3: T3) {
298 | projectedValue?(t0, t1, t2, t3)
299 | }
300 | }
301 |
302 | /// A wrapper for clusure-based interaction between objects
303 | ///
304 | /// Provides a public API to set internal closure-based hanlder or delegate with a functional API
305 | @propertyWrapper
306 | public class Handler5 {
307 | public struct Container {
308 | public typealias Behaviour = HandlerContainerBehaviour
309 |
310 | internal var action: ((T0, T1, T2, T3, T4) -> Void)?
311 |
312 | internal init() {}
313 |
314 | public init(action: ((T0, T1, T2, T3, T4) -> Void)?) {
315 | self.action = action
316 | }
317 |
318 | public mutating func callAsFunction(perform action: ((T0, T1, T2, T3, T4) -> Void)?) {
319 | self.action = action
320 | }
321 |
322 | @available(
323 | *,
324 | deprecated,
325 | message:
326 | """
327 | This API will be removed, \
328 | consider using redeclaration with `(Input) -> Output` signature function. \
329 | Feel free to discuss the API here \
330 | https://github.com/MakeupStudio/swift-declarative-configuration/issues/1
331 | """
332 | )
333 | public mutating func callAsFunction(
334 | _ behaviour: Behaviour,
335 | perform action: ((T0, T1, T2, T3, T4) -> Void)?
336 | ) {
337 | switch behaviour {
338 | case .resetting:
339 | self.action = action
340 | case .preceding:
341 | let oldAction = self.action
342 | self.action = { t0, t1, t2, t3, t4 in
343 | action?(t0, t1, t2, t3, t4)
344 | oldAction?(t0, t1, t2, t3, t4)
345 | }
346 | case .appending:
347 | let oldAction = self.action
348 | self.action = { t0, t1, t2, t3, t4 in
349 | action?(t0, t1, t2, t3, t4)
350 | oldAction?(t0, t1, t2, t3, t4)
351 | }
352 | }
353 | }
354 | }
355 |
356 | public init() {}
357 |
358 | public init(wrappedValue: Container) {
359 | self.wrappedValue = wrappedValue
360 | }
361 |
362 | public var wrappedValue: Container = .init()
363 |
364 | public var projectedValue: ((T0, T1, T2, T3, T4) -> Void)? {
365 | get { wrappedValue.action }
366 | set { wrappedValue.action = newValue }
367 | }
368 |
369 | public func callAsFunction(_ t0: T0, _ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4) {
370 | projectedValue?(t0, t1, t2, t3, t4)
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/Sources/FunctionalConfigurator/ConfigIntializable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Allows to intialize a new object without parameters or with configuration
4 | public protocol ConfigInitializable {
5 | init()
6 | }
7 |
8 | extension ConfigInitializable {
9 | public typealias Config = Configurator
10 |
11 | /// Instantiates a new object with specified configuration
12 | ///
13 | /// Note: Type must implement custom intializer with no parameters, even if it inherits from NSObject
14 | @inlinable
15 | public init(config configuration: (Config) -> Config) {
16 | self.init(config: configuration(Config()))
17 | }
18 |
19 | /// Instantiates a new object with specified configuration
20 | ///
21 | /// Note: Type must implement custom intializer with no parameters, even if it inherits from NSObject
22 | public init(config configurator: Config) {
23 | self = configurator.configured(.init())
24 | }
25 | }
26 |
27 | public protocol ConfigInitializableNSObject: NSObjectProtocol {}
28 | extension ConfigInitializableNSObject where Self: NSObject {
29 | public typealias Config = Configurator
30 |
31 | /// Instantiates a new object with specified configuration
32 | ///
33 | /// Note: Type must implement custom intializer with no parameters, even if it inherits from NSObject
34 | @inlinable
35 | public init(config configuration: (Config) -> Config) {
36 | self.init(config: configuration(Config()))
37 | }
38 |
39 | /// Instantiates a new object with specified configuration
40 | ///
41 | /// Note: Type must implement custom intializer with no parameters, even if it inherits from NSObject
42 | @inlinable
43 | public init(config configurator: Config) {
44 | self.init()
45 | configurator.configure(self)
46 | }
47 | }
48 |
49 | extension NSObject: ConfigInitializableNSObject {}
50 |
--------------------------------------------------------------------------------
/Sources/FunctionalConfigurator/Configurator.swift:
--------------------------------------------------------------------------------
1 | import FunctionalKeyPath
2 | import FunctionalModification
3 |
4 | @dynamicMemberLookup
5 | public struct Configurator {
6 | @usableFromInline
7 | internal var _configure: (Base) -> Base
8 |
9 | /// Creates a new instance of configurator
10 | ///
11 | /// Newly created configurator has no modification set up.
12 | /// So it's `configure` function does not modify input
13 | public init() { self._configure = { $0 } }
14 |
15 | /// Creates a configurator with a configuration function
16 | ///
17 | /// Initial value passed to configuration function is an empty configurator
18 | @inlinable
19 | public init(config configuration: (Configurator) -> Configurator) {
20 | self = configuration(.init())
21 | }
22 |
23 | /// Modifies an object with specified configuration
24 | @inlinable
25 | public func configure(_ base: inout Base) {
26 | _ = _configure(base)
27 | }
28 |
29 | /// Modifies a reference-type object with specified configuration
30 | @inlinable
31 | public func configure(_ base: Base) where Base: AnyObject {
32 | _ = _configure(base)
33 | }
34 |
35 | /// Modifies returns modified object
36 | ///
37 | /// Note: for reference types it is usually the same object
38 | @inlinable
39 | public func configured(_ base: Base) -> Base {
40 | _configure(base)
41 | }
42 |
43 | /// Appends modification of stored object to stored configuration
44 | @inlinable
45 | public func set(_ transform: @escaping (inout Base) -> Void) -> Configurator {
46 | appendingConfiguration { base in
47 | reduce(_configure(base), with: transform)
48 | }
49 | }
50 |
51 | @inlinable
52 | public func combined(with configurator: Configurator) -> Configurator {
53 | appendingConfiguration(configurator._configure)
54 | }
55 |
56 | /// Appends modification of a new configurator to stored configuration
57 | @available(*, deprecated, message: "Use `combined(with:) instead`")
58 | @inlinable
59 | public func appending(_ configurator: Configurator) -> Configurator {
60 | appendingConfiguration(configurator._configure)
61 | }
62 |
63 | /// Appends configuration to stored configuration
64 | @inlinable
65 | public func appendingConfiguration(_ configuration: @escaping (Base) -> Base) -> Configurator {
66 | reduce(self) { _self in
67 | _self._configure = { configuration(_configure($0)) }
68 | }
69 | }
70 |
71 | @inlinable
72 | public subscript(
73 | dynamicMember keyPath: WritableKeyPath
74 | ) -> CallableBlock {
75 | CallableBlock(
76 | configurator: self,
77 | keyPath: .init(keyPath)
78 | )
79 | }
80 |
81 | @inlinable
82 | public subscript(
83 | dynamicMember keyPath: KeyPath
84 | ) -> NonCallableBlock {
85 | NonCallableBlock(
86 | configurator: self,
87 | keyPath: .getonly(keyPath)
88 | )
89 | }
90 |
91 | @inlinable
92 | public subscript(
93 | dynamicMember keyPath: WritableKeyPath
94 | ) -> CallableBlock where Base == Wrapped? {
95 | CallableBlock(
96 | configurator: self,
97 | keyPath: FunctionalKeyPath(keyPath).optional()
98 | )
99 | }
100 |
101 | @inlinable
102 | public subscript(
103 | dynamicMember keyPath: KeyPath
104 | ) -> NonCallableBlock where Base == Wrapped? {
105 | NonCallableBlock(
106 | configurator: self,
107 | keyPath: FunctionalKeyPath.getonly(keyPath).optional()
108 | )
109 | }
110 |
111 | @inlinable
112 | public static subscript(
113 | dynamicMember keyPath: WritableKeyPath
114 | ) -> CallableBlock {
115 | Configurator()[dynamicMember: keyPath]
116 | }
117 |
118 | @inlinable
119 | public static subscript(
120 | dynamicMember keyPath: KeyPath
121 | ) -> NonCallableBlock {
122 | Configurator()[dynamicMember: keyPath]
123 | }
124 |
125 | @inlinable
126 | public static subscript(
127 | dynamicMember keyPath: WritableKeyPath
128 | ) -> CallableBlock where Base == Wrapped? {
129 | Configurator()[dynamicMember: keyPath]
130 | }
131 |
132 | @inlinable
133 | public static subscript(
134 | dynamicMember keyPath: KeyPath
135 | ) -> NonCallableBlock where Base == Wrapped? {
136 | Configurator()[dynamicMember: keyPath]
137 | }
138 |
139 | @inlinable
140 | public static func set(_ transform: @escaping (inout Base) -> Void) -> Configurator {
141 | Configurator().set(transform)
142 | }
143 | }
144 |
145 | extension Configurator {
146 | @dynamicMemberLookup
147 | public struct CallableBlock {
148 | @usableFromInline
149 | internal var _block: NonCallableBlock
150 |
151 | @usableFromInline
152 | internal init(
153 | configurator: Configurator,
154 | keyPath: FunctionalKeyPath
155 | ) {
156 | self._block = .init(
157 | configurator: configurator,
158 | keyPath: keyPath
159 | )
160 | }
161 |
162 | @inlinable
163 | public func callAsFunction(_ value: Value) -> Configurator {
164 | _block.configurator.appendingConfiguration {
165 | _block.keyPath.embed(value, in: $0)
166 | }
167 | }
168 |
169 | @inlinable
170 | public func set(_ transform: @escaping (inout Value) -> Void) -> Configurator {
171 | _block.configurator.appendingConfiguration { base in
172 | _block.keyPath.embed(
173 | reduce(
174 | _block.keyPath.extract(from: base),
175 | with: transform
176 | ),
177 | in: base
178 | )
179 | }
180 | }
181 |
182 | @inlinable
183 | public func scope(
184 | _ configuration: @escaping (Configurator) -> Configurator
185 | ) -> Configurator {
186 | _block.configurator.appendingConfiguration { base in
187 | _block.keyPath.embed(
188 | reduce(
189 | _block.keyPath.extract(from: base),
190 | with: configuration
191 | ),
192 | in: base
193 | )
194 | }
195 | }
196 |
197 | @inlinable
198 | public func ifLetScope(
199 | _ configuration: @escaping (Configurator) -> Configurator
200 | ) -> Configurator where Value == Wrapped? {
201 | _block.configurator.appendingConfiguration { base in
202 | guard let value = _block.keyPath.extract(from: base)
203 | else { return base }
204 |
205 | return _block.keyPath.embed(
206 | reduce(value, with: configuration),
207 | in: base
208 | )
209 | }
210 | }
211 |
212 | @inlinable
213 | public subscript(
214 | dynamicMember keyPath: WritableKeyPath
215 | ) -> CallableBlock {
216 | CallableBlock(
217 | configurator: _block.configurator,
218 | keyPath: _block.keyPath
219 | .appending(path: FunctionalKeyPath(keyPath))
220 | )
221 | }
222 |
223 | @inlinable
224 | public subscript(
225 | dynamicMember keyPath: KeyPath
226 | ) -> NonCallableBlock {
227 | _block[dynamicMember: keyPath]
228 | }
229 |
230 | @inlinable
231 | public subscript(
232 | dynamicMember keyPath: WritableKeyPath
233 | ) -> CallableBlock where Value == Wrapped? {
234 | CallableBlock(
235 | configurator: _block.configurator,
236 | keyPath: _block.keyPath.appending(
237 | path: FunctionalKeyPath(keyPath).optional()
238 | )
239 | )
240 | }
241 |
242 | @inlinable
243 | public subscript(
244 | dynamicMember keyPath: KeyPath
245 | ) -> NonCallableBlock where Value == Wrapped? {
246 | _block[dynamicMember: keyPath]
247 | }
248 | }
249 |
250 | @dynamicMemberLookup
251 | public struct NonCallableBlock {
252 | @usableFromInline
253 | internal var configurator: Configurator
254 |
255 | @usableFromInline
256 | internal var keyPath: FunctionalKeyPath
257 |
258 | @usableFromInline
259 | internal init(
260 | configurator: Configurator,
261 | keyPath: FunctionalKeyPath
262 | ) {
263 | self.configurator = configurator
264 | self.keyPath = keyPath
265 | }
266 |
267 | @inlinable
268 | public func scope(
269 | _ configuration: @escaping (Configurator) -> Configurator
270 | ) -> Configurator where Value: AnyObject {
271 | configurator.appendingConfiguration { base in
272 | keyPath.embed(
273 | reduce(
274 | keyPath.extract(from: base),
275 | with: configuration
276 | ),
277 | in: base
278 | )
279 | }
280 | }
281 |
282 | @inlinable
283 | public func ifLetScope(
284 | _ configuration: @escaping (Configurator) -> Configurator
285 | ) -> Configurator where Wrapped: AnyObject, Value == Wrapped? {
286 | configurator.appendingConfiguration { base in
287 | guard let value = keyPath.extract(from: base)
288 | else { return base }
289 |
290 | return keyPath.embed(
291 | reduce(value, with: configuration),
292 | in: base
293 | )
294 | }
295 | }
296 |
297 | @inlinable
298 | public subscript(
299 | dynamicMember keyPath: ReferenceWritableKeyPath
300 | ) -> CallableBlock {
301 | .init(
302 | configurator: self.configurator,
303 | keyPath: self.keyPath.appending(path: FunctionalKeyPath(keyPath))
304 | )
305 | }
306 |
307 | @inlinable
308 | public subscript(
309 | dynamicMember keyPath: KeyPath
310 | ) -> NonCallableBlock {
311 | .init(
312 | configurator: self.configurator,
313 | keyPath: self.keyPath.appending(path: .getonly(keyPath))
314 | )
315 | }
316 |
317 | @inlinable
318 | public subscript(
319 | dynamicMember keyPath: ReferenceWritableKeyPath
320 | ) -> CallableBlock where Value == Wrapped? {
321 | CallableBlock(
322 | configurator: self.configurator,
323 | keyPath: self.keyPath.appending(path: FunctionalKeyPath(keyPath))
324 | )
325 | }
326 |
327 | @inlinable
328 | public subscript(
329 | dynamicMember keyPath: KeyPath
330 | ) -> NonCallableBlock where Value == Wrapped? {
331 | NonCallableBlock(
332 | configurator: self.configurator,
333 | keyPath: self.keyPath.appending(path: .getonly(keyPath))
334 | )
335 | }
336 | }
337 | }
338 |
--------------------------------------------------------------------------------
/Sources/FunctionalConfigurator/CustomConfigurable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public protocol CustomConfigurable {}
4 |
5 | extension CustomConfigurable {
6 | public typealias Config = Configurator
7 |
8 | @inlinable
9 | public func configured(using configuration: (Config) -> Config) -> Self {
10 | configured(using: configuration(Config()))
11 | }
12 |
13 | @inlinable
14 | public func configured(using configurator: Config) -> Self {
15 | configurator.configured(self)
16 | }
17 | }
18 |
19 | extension CustomConfigurable where Self: AnyObject {
20 | @inlinable
21 | public func configure(using configuration: (Config) -> Config) {
22 | configure(using: configuration(Config()))
23 | }
24 |
25 | @inlinable
26 | public func configure(using configurator: Config) {
27 | configurator.configure(self)
28 | }
29 | }
30 |
31 | extension NSObject: CustomConfigurable {}
32 |
--------------------------------------------------------------------------------
/Sources/FunctionalConfigurator/Modification.swift:
--------------------------------------------------------------------------------
1 | /// Modifies an object.
2 | ///
3 | /// Returns a new instance for value types
4 | /// Returns modified reference for reference types
5 | @available(
6 | *,
7 | deprecated,
8 | message:
9 | """
10 | This function will be made internal in `1.0.0` release, implement `CustomConfigurable` protocol for your object and use instance method instead
11 | """
12 | )
13 | @inlinable
14 | public func modification