diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml
index 475ec22a2..173b642c1 100644
--- a/.github/workflows/Build.yml
+++ b/.github/workflows/Build.yml
@@ -7,6 +7,9 @@ on:
- preview/version-8
pull_request:
+env:
+ DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer
+
jobs:
build_iOS:
@@ -72,7 +75,7 @@ jobs:
run: make build_example_tvOS
build_SPM:
- runs-on: macOS-12
+ runs-on: macOS-13
steps:
- uses: actions/checkout@v1
- name: Build
diff --git a/.github/workflows/Lint.yml b/.github/workflows/Lint.yml
index e72fbef22..2d6d03310 100644
--- a/.github/workflows/Lint.yml
+++ b/.github/workflows/Lint.yml
@@ -2,6 +2,9 @@ name: Lint
on: [pull_request]
+env:
+ DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer
+
jobs:
danger:
runs-on: ubuntu-latest
@@ -16,7 +19,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pod:
- runs-on: macOS-12
+ runs-on: macOS-13
steps:
- uses: actions/checkout@v1
- name: Generate projects
diff --git a/Brewfile.lock.json b/Brewfile.lock.json
index 0dde257b9..79fed6238 100644
--- a/Brewfile.lock.json
+++ b/Brewfile.lock.json
@@ -2,7 +2,7 @@
"entries": {
"tap": {
"homebrew/cask": {
- "revision": "266747f8b172dd47e6c80ce2590889a8a7c96501"
+ "revision": "c896fe86403b4515fddc15421cce660a306dc3f0"
},
"yonaskolb/xcodegen": {
"revision": "372f20fe5a3823dc398f52cb6cc1af5003d452f7",
@@ -19,7 +19,7 @@
},
"brew": {
"xcodegen": {
- "version": "2.25.0",
+ "version": "2.35.0",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
@@ -58,7 +58,7 @@
}
},
"sourcery": {
- "version": "1.8.1",
+ "version": "2.0.2",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
@@ -107,11 +107,11 @@
"macOS": "11.5.1"
},
"ventura": {
- "HOMEBREW_VERSION": "4.0.19",
- "HOMEBREW_PREFIX": "/opt/homebrew",
+ "HOMEBREW_VERSION": "4.0.23-10-g6abf680",
+ "HOMEBREW_PREFIX": "/usr/local",
"Homebrew/homebrew-core": "api",
"CLT": "",
- "Xcode": "14.3",
+ "Xcode": "15.0",
"macOS": "13.4"
}
}
diff --git a/Components/Macro/.gitignore b/Components/Macro/.gitignore
new file mode 100644
index 000000000..0023a5340
--- /dev/null
+++ b/Components/Macro/.gitignore
@@ -0,0 +1,8 @@
+.DS_Store
+/.build
+/Packages
+xcuserdata/
+DerivedData/
+.swiftpm/configuration/registries.json
+.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
+.netrc
diff --git a/Components/Macro/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Components/Macro/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/Components/Macro/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/Components/Macro/Package.resolved b/Components/Macro/Package.resolved
new file mode 100644
index 000000000..de20f808f
--- /dev/null
+++ b/Components/Macro/Package.resolved
@@ -0,0 +1,14 @@
+{
+ "pins" : [
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-syntax.git",
+ "state" : {
+ "revision" : "67c5007099d9ffdd292f421f81f4efe5ee42963e",
+ "version" : "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-07-10-a"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/Components/Macro/Package.swift b/Components/Macro/Package.swift
new file mode 100644
index 000000000..f980e6d15
--- /dev/null
+++ b/Components/Macro/Package.swift
@@ -0,0 +1,57 @@
+// swift-tools-version: 5.9
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+import CompilerPluginSupport
+
+let package = Package(
+ name: "Macro",
+ platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)],
+ products: [
+ // Products define the executables and libraries a package produces, making them visible to other packages.
+ .library(
+ name: "Macro",
+ type: .dynamic,
+ targets: ["Macro"]
+ ),
+ .executable(
+ name: "MacroClient",
+ targets: ["MacroClient"]
+ )
+ ],
+ dependencies: [
+ // Depend on the latest Swift 5.9 prerelease of SwiftSyntax
+ .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b")
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package, defining a module or a test suite.
+ // Targets can depend on other targets in this package and products from dependencies.
+ // Macro implementation that performs the source transformation of a macro.
+ .macro(
+ name: "Macros",
+ dependencies: [
+ .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
+ .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
+ ]
+ ),
+
+ // Library that exposes a macro as part of its API, which is used in client programs.
+ .target(name: "Macro",
+ dependencies: [
+ "Macros"
+ ]
+ ),
+
+ // A client of the library, which is able to use the macro in its own code.
+ .executableTarget(name: "MacroClient", dependencies: ["Macro"]),
+
+ // A test target used to develop the macro implementation.
+ .testTarget(
+ name: "MacroTests",
+ dependencies: [
+ "Macro",
+ .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax")
+ ]
+ )
+ ]
+)
diff --git a/Components/Macro/Sources/Macro/MacroDefinition.swift b/Components/Macro/Sources/Macro/MacroDefinition.swift
new file mode 100644
index 000000000..237247ce9
--- /dev/null
+++ b/Components/Macro/Sources/Macro/MacroDefinition.swift
@@ -0,0 +1,10 @@
+// The Swift Programming Language
+// https://docs.swift.org/swift-book
+
+/// A macro which generate `mutating func` for all variables inside struct.
+@attached(member, names: named(set))
+public macro Mutable() = #externalMacro(module: "Macros", type: "MutableMacro")
+
+@attached(conformance)
+@attached(member, names: named(create), named(Property))
+public macro Buildable() = #externalMacro(module: "Macros", type: "BuildableMacro")
diff --git a/Components/Macro/Sources/MacroClient/main.swift b/Components/Macro/Sources/MacroClient/main.swift
new file mode 100644
index 000000000..893e084ce
--- /dev/null
+++ b/Components/Macro/Sources/MacroClient/main.swift
@@ -0,0 +1,37 @@
+import Macro
+
+// MARK: - Precondition
+
+@Mutable
+struct SomeStruct {
+ let id: Int = Int.random(in: Int.min...Int.max)
+ var someFlag: Bool = false
+ var someString: String = ""
+}
+
+// MARK: - Helpers
+
+extension SomeStruct {
+
+ func debugPrintAll() {
+ debugPrint(someFlag)
+ debugPrint(someString)
+ }
+
+}
+
+// MARK: - Debugging
+
+var someStruct = SomeStruct()
+
+someStruct.debugPrintAll()
+
+someStruct.set(someFlag: true)
+someStruct.set(someString: "echo")
+
+someStruct.debugPrintAll()
+
+someStruct.set(someFlag: false)
+someStruct.set(someString: "horray")
+
+someStruct.debugPrintAll()
diff --git a/Components/Macro/Sources/Macros/BuildableMacro.swift b/Components/Macro/Sources/Macros/BuildableMacro.swift
new file mode 100644
index 000000000..0353150b3
--- /dev/null
+++ b/Components/Macro/Sources/Macros/BuildableMacro.swift
@@ -0,0 +1,110 @@
+//
+// BuildableMacro.swift
+//
+//
+// Created by Никита Коробейников on 25.07.2023.
+//
+
+import SwiftSyntax
+import SwiftSyntaxBuilder
+import SwiftSyntaxMacros
+
+/// A macro which generate `func build` for all variables inside struct.
+/// - Note: builder is based on resultBuilder from Swift 5.4
+public struct BuildableMacro: MemberMacro, ConformanceMacro {
+
+ public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingConformancesOf declaration: some SwiftSyntax.DeclGroupSyntax, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [(SwiftSyntax.TypeSyntax, SwiftSyntax.GenericWhereClauseSyntax?)] {
+ return [(TypeSyntax(stringLiteral: "EditorWrapper"), nil)]
+ }
+
+ public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingMembersOf declaration: some SwiftSyntax.DeclGroupSyntax, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] {
+ guard let baseStruct = declaration.as(StructDeclSyntax.self) else {
+ throw MacroError.onlyApplicableToStruct
+ }
+
+ let variables = baseStruct.variables
+
+ let editors = try prepareEditorDeclarations(for: variables)
+
+ let propertyStruct = preparePropertyStruct(for: baseStruct, with: editors)
+
+ let createFunc = try prepareCreateFunction(for: baseStruct)
+
+ var result: [DeclSyntaxProtocol] = []
+
+ result.append(createFunc)
+ result.append(propertyStruct)
+
+ return result.compactMap { $0.as(DeclSyntax.self) }
+ }
+
+}
+
+// MARK: - Private
+
+private extension BuildableMacro {
+
+ static func prepareCreateFunction(for baseStruct: StructDeclSyntax) throws -> FunctionDeclSyntax {
+ guard let baseStuctType = TypeSyntax(baseStruct) else {
+ throw MacroError.failedToExtractTypeOfBaseStruct
+ }
+
+ return .init(modifiers: .init(itemsBuilder: {
+ DeclModifierSyntax(name: .keyword(.public))
+ DeclModifierSyntax(name: .keyword(.static))
+ }),
+ identifier: .identifier("create"),
+ signature: .init(input: .init(parameterListBuilder: { }),
+ output: .init(returnType: baseStuctType))
+ )
+ }
+
+ static func preparePropertyStruct(for baseStruct: StructDeclSyntax, with editors: [FunctionDeclSyntax]) -> StructDeclSyntax {
+ StructDeclSyntax(modifiers: .init(itemsBuilder: {
+ DeclModifierSyntax(name: .keyword(.public))
+ }),
+ identifier: .identifier("Property"),
+ memberBlock: .init(membersBuilder: {
+ .init(itemsBuilder: {
+ TypealiasDeclSyntax(
+ modifiers: .init(itemsBuilder: {
+ DeclModifierSyntax(name: .keyword(.public))
+ }),
+ identifier:
+ .identifier("Model"),
+ initializer: .init(baseStruct)!)
+
+// VariableDeclSyntax(modifiers: .init(itemsBuilder: {
+// DeclModifierSyntax(name: .keyword(.private))
+// }), name: .init(stringLiteral: "closure"), .keyword(.let))
+ })
+ }))
+ }
+
+ static func prepareEditorDeclarations(for variables: [VariableDeclSyntax]) throws -> [FunctionDeclSyntax] {
+ try variables.compactMap { variableDecl -> FunctionDeclSyntax? in
+
+ guard let variable = try variableDecl.parseNameAndType() else {
+ return nil
+ }
+
+ return FunctionDeclSyntax(leadingTrivia: .newlines(2),
+ modifiers: .init(itemsBuilder: {
+ DeclModifierSyntax(name: .keyword(.static))
+ }),
+ identifier: .identifier(variable.name.text),
+ signature: .init(input: .init(parameterListBuilder: {
+ FunctionParameterSyntax(leadingTrivia: .space,
+ firstName: .identifier("value"),
+ type: variable.type)
+ })),
+ body: CodeBlockSyntax(statementsBuilder: {
+ StmtSyntax(stringLiteral: "var model = model")
+ StmtSyntax(stringLiteral: "model.set(\(variable.name.text):value)")
+ StmtSyntax(stringLiteral: "return model")
+ })
+ )
+ }
+ }
+
+}
diff --git a/Components/Macro/Sources/Macros/Decl+Extensions.swift b/Components/Macro/Sources/Macros/Decl+Extensions.swift
new file mode 100644
index 000000000..518b81071
--- /dev/null
+++ b/Components/Macro/Sources/Macros/Decl+Extensions.swift
@@ -0,0 +1,51 @@
+//
+// Decl+Extensions.swift
+//
+//
+// Created by Никита Коробейников on 25.07.2023.
+//
+
+import SwiftSyntax
+import SwiftSyntaxBuilder
+import SwiftSyntaxMacros
+
+// MARK: - Struct
+
+extension StructDeclSyntax {
+
+ var variables: [VariableDeclSyntax] {
+ return memberBlock.members
+ .compactMap { $0.decl.as(VariableDeclSyntax.self) }
+ .filter { $0.bindingKeyword.text == "var" }
+ }
+
+
+}
+
+// MARK: - Variable
+
+extension VariableDeclSyntax {
+
+ func parseNameAndType() throws -> (name: TokenSyntax, type: TypeSyntax)? {
+ guard let variableBinding = bindings.first,
+ let variableType = variableBinding.typeAnnotation?.type.trimmed else {
+ let variableName = bindings.first?.pattern.trimmedDescription
+ throw MacroError.typeAnnotationRequiredFor(variableName: variableName ?? "unknown")
+ }
+
+ let variableName = TokenSyntax(stringLiteral: variableBinding.pattern.description)
+
+ return (name: variableName, type: variableType)
+ }
+
+}
+
+// MARK: - CodeBlockExpression
+
+extension CodeBlockItemSyntax {
+
+ static func stringItem(_ string: String) -> CodeBlockItemSyntax.Item {
+ CodeBlockItemSyntax.Item.expr(.init(stringLiteral: string))
+ }
+
+}
diff --git a/Components/Macro/Sources/Macros/MacroError.swift b/Components/Macro/Sources/Macros/MacroError.swift
new file mode 100644
index 000000000..65fc94ef4
--- /dev/null
+++ b/Components/Macro/Sources/Macros/MacroError.swift
@@ -0,0 +1,14 @@
+//
+// MacroError.swift
+//
+//
+// Created by Никита Коробейников on 16.06.2023.
+//
+
+public enum MacroError: Error {
+
+ case onlyApplicableToStruct
+ case typeAnnotationRequiredFor(variableName: String)
+ case failedToExtractTypeOfBaseStruct
+
+}
diff --git a/Components/Macro/Sources/Macros/MacroPlugin.swift b/Components/Macro/Sources/Macros/MacroPlugin.swift
new file mode 100644
index 000000000..ec160a1f2
--- /dev/null
+++ b/Components/Macro/Sources/Macros/MacroPlugin.swift
@@ -0,0 +1,17 @@
+//
+// MacroPlugin.swift
+//
+//
+// Created by Никита Коробейников on 25.07.2023.
+//
+
+import SwiftCompilerPlugin
+import SwiftSyntaxMacros
+
+@main
+struct MacroPlugin: CompilerPlugin {
+ let providingMacros: [Macro.Type] = [
+ MutableMacro.self,
+ BuildableMacro.self
+ ]
+}
diff --git a/Components/Macro/Sources/Macros/MutableMacro.swift b/Components/Macro/Sources/Macros/MutableMacro.swift
new file mode 100644
index 000000000..d746c9745
--- /dev/null
+++ b/Components/Macro/Sources/Macros/MutableMacro.swift
@@ -0,0 +1,50 @@
+import SwiftSyntax
+import SwiftSyntaxBuilder
+import SwiftSyntaxMacros
+
+/// A macro which generate `mutating func` for all variables inside struct.
+public struct MutableMacro: MemberMacro {
+
+ public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingMembersOf declaration: some SwiftSyntax.DeclGroupSyntax, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] {
+ guard let baseStruct = declaration.as(StructDeclSyntax.self) else {
+ throw MacroError.onlyApplicableToStruct
+ }
+
+ let variables = baseStruct.variables
+
+ let functions = try prepareEditorDeclarations(for: variables)
+
+ return functions.compactMap { $0.as(DeclSyntax.self) }
+ }
+
+}
+
+// MARK: - Private
+
+private extension MutableMacro {
+
+ static func prepareEditorDeclarations(for variables: [VariableDeclSyntax]) throws -> [FunctionDeclSyntax] {
+ try variables.compactMap { variableDecl -> FunctionDeclSyntax? in
+
+ guard let variable = try variableDecl.parseNameAndType() else {
+ return nil
+ }
+
+ return FunctionDeclSyntax(leadingTrivia: .newlines(2),
+ modifiers: .init(itemsBuilder: {
+ DeclModifierSyntax(name: .keyword(.mutating))
+ }),
+ identifier: .identifier("set"),
+ signature: .init(input: .init(parameterListBuilder: {
+ FunctionParameterSyntax(firstName: variable.name,
+ type: variable.type)
+
+ })),
+ body: .init(statementsBuilder: {
+ ExprSyntax(stringLiteral: "self.\(variable.name.text)=\(variable.name.text)")
+ })
+ )
+ }
+ }
+
+}
diff --git a/Components/Macro/Tests/BuildableMacroTests.swift b/Components/Macro/Tests/BuildableMacroTests.swift
new file mode 100644
index 000000000..2b08894a5
--- /dev/null
+++ b/Components/Macro/Tests/BuildableMacroTests.swift
@@ -0,0 +1,66 @@
+//
+// BuildableMacrosTests.swift
+//
+//
+// Created by Никита Коробейников on 25.07.2023.
+//
+
+import SwiftSyntaxMacros
+import SwiftSyntaxMacrosTestSupport
+import XCTest
+import Macros
+
+final class BuildableMacroTests: XCTestCase {
+
+ let testMacros: [String: Macro.Type] = [
+ "Buildable": BuildableMacro.self
+ ]
+
+ func testExpansionSucceded_whenAppliedToStruct_withVariablesAndType() {
+ assertMacroExpansion(
+ """
+ @Buildable
+ struct SomeStruct {
+ let id: String = ""
+ var someVar: Bool = false
+ }
+ """,
+ expandedSource:
+ """
+
+ struct SomeStruct: EditorWrapper {
+ let id: String = ""
+ var someVar: Bool = false
+
+ public static func create() -> SomeStruct {
+ .init()
+ }
+
+ public struct Property: Editor {
+ public typealias Model = SomeStruct
+
+ private let closure: (Model) -> Model
+
+ public init(closure: @escaping (Model) -> Model) {
+ self.closure = closure
+ }
+
+ public func edit(_ model: Model) -> Model {
+ return closure(model)
+ }
+
+ public static func someVar(_ value: TextValue) -> Property {
+ .init(closure: { model in
+ var model = model
+ model.set(someVar: value)
+ return model
+ })
+ }
+ }
+ }
+ """,
+ macros: testMacros
+ )
+ }
+
+}
diff --git a/Components/Macro/Tests/MutableMacroTests.swift b/Components/Macro/Tests/MutableMacroTests.swift
new file mode 100644
index 000000000..6c1c5e142
--- /dev/null
+++ b/Components/Macro/Tests/MutableMacroTests.swift
@@ -0,0 +1,89 @@
+import SwiftSyntaxMacros
+import SwiftSyntaxMacrosTestSupport
+import XCTest
+import Macros
+
+final class MutableMacroTests: XCTestCase {
+
+ let testMacros: [String: Macro.Type] = [
+ "Mutable": MutableMacro.self
+ ]
+
+ func testExpansionSucceded_whenAppliedToStruct_withVariablesAndType() {
+ assertMacroExpansion(
+ """
+ @Mutable
+ struct SomeStruct {
+ let id: String = ""
+ var someVar: Bool = false
+ }
+ """,
+ expandedSource:
+ """
+
+ struct SomeStruct {
+ let id: String = ""
+ var someVar: Bool = false
+
+ mutating func set(someVar: Bool) {
+ self.someVar = someVar
+ }
+ }
+ """,
+ macros: testMacros
+ )
+ }
+
+ func testExpansionFailed_whenAppliedTo_nonStruct() {
+ assertMacroExpansion(
+ """
+ @Mutable
+ class SomeStruct {
+ let id: String = ""
+ var someVar: Bool = false
+ }
+ """,
+ expandedSource:
+ """
+
+ class SomeStruct {
+ let id: String = ""
+ var someVar: Bool = false
+ }
+ """,
+ diagnostics: [
+ .init(message: "onlyApplicableToStruct",
+ line: 1,
+ column: 1)
+ ],
+ macros: testMacros
+ )
+ }
+
+ func testExpansionFailed_whenAppliedTo_variableWithoutAnnotation() {
+ assertMacroExpansion(
+ """
+ @Mutable
+ struct SomeStruct {
+ let id: String = ""
+ var someVar = false
+ }
+ """,
+ expandedSource:
+ """
+
+ struct SomeStruct {
+ let id: String = ""
+ var someVar = false
+ }
+ """,
+ diagnostics: [
+ .init(message: "typeAnnotationRequiredFor(variableName: \"someVar\")",
+ line: 1,
+ column: 1)
+ ],
+ macros: testMacros
+ )
+ }
+
+}
diff --git a/Components/Package.resolved b/Components/Package.resolved
new file mode 100644
index 000000000..cef57fbec
--- /dev/null
+++ b/Components/Package.resolved
@@ -0,0 +1,16 @@
+{
+ "object": {
+ "pins": [
+ {
+ "package": "SwiftSyntax",
+ "repositoryURL": "https://github.com/apple/swift-syntax.git",
+ "state": {
+ "branch": null,
+ "revision": "165fc6d22394c1168ff76ab5d951245971ef07e5",
+ "version": "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-06-05-a"
+ }
+ }
+ ]
+ },
+ "version": 1
+}
diff --git a/Components/Package.swift b/Components/Package.swift
index f7aa97aae..9b3ace2fe 100644
--- a/Components/Package.swift
+++ b/Components/Package.swift
@@ -1,11 +1,11 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "ReactiveDataComponents",
- platforms: [.iOS(.v11)],
+ platforms: [.iOS(.v13)],
products: [
.library(
name: "ReactiveDataComponents",
@@ -13,12 +13,13 @@ let package = Package(
)
],
dependencies: [
- .package(name: "ReactiveDataDisplayManager", path: "../")
+ .package(name: "ReactiveDataDisplayManager", path: "../"),
+ .package(name: "Macro", path: "./Macro")
],
targets: [
.target(
name: "ReactiveDataComponents",
- dependencies: ["ReactiveDataDisplayManager"],
+ dependencies: ["ReactiveDataDisplayManager", "Macro"],
path: "Sources"
)
]
diff --git a/Components/Sources/Common/Protocols/Editor.swift b/Components/Sources/Common/Protocols/Editor.swift
index 511a131bb..d7ea9d433 100644
--- a/Components/Sources/Common/Protocols/Editor.swift
+++ b/Components/Sources/Common/Protocols/Editor.swift
@@ -7,12 +7,24 @@
import Foundation
+// MARK: - Protocols
+
public protocol Editor {
associatedtype Model
func edit(_ model: Model) -> Model
}
+public protocol EditorWrapper {
+
+ associatedtype Property: Editor
+
+ static func create() -> Property.Model
+
+}
+
+// MARK: - Builder
+
@resultBuilder
public struct EditorBuilder {
public static func buildExpression(_ expression: T) -> [T] {
@@ -49,3 +61,15 @@ public struct EditorBuilder {
return component ?? []
}
}
+
+// MARK: - Shortcut
+
+public extension EditorWrapper where Property.Model == Self {
+
+ static func build(@EditorBuilder content: (Property.Type) -> [Property]) -> Self {
+ return content(Property.self).reduce(Self.create(), { model, editor in
+ editor.edit(model)
+ })
+ }
+
+}
diff --git a/Components/Sources/Common/Views/LabelView.swift b/Components/Sources/Common/Views/LabelView.swift
index d8947e32f..3b950c11b 100644
--- a/Components/Sources/Common/Views/LabelView.swift
+++ b/Components/Sources/Common/Views/LabelView.swift
@@ -7,6 +7,7 @@
import UIKit
import ReactiveDataDisplayManager
+import Macro
/// Base view to implement label within cell
public class LabelView: UIView {
@@ -23,7 +24,14 @@ extension LabelView: ConfigurableItem {
// MARK: - Model
- public struct Model: AlignmentProvider, TextProvider {
+ @Mutable
+ public struct Model: EditorWrapper, AlignmentProvider, TextProvider {
+
+ // MARK: - EditorWrapper
+
+ public static func create() -> LabelView.Model {
+ .init()
+ }
// MARK: - Editor
@@ -89,36 +97,6 @@ extension LabelView: ConfigurableItem {
private(set) public var alignment: Alignment = .all(.zero)
private(set) public var textAlignment: NSTextAlignment = .left
- // MARK: - Mutation
-
- mutating func set(text: TextValue) {
- self.text = text
- }
-
- mutating func set(style: TextStyle) {
- self.style = style
- }
-
- mutating func set(layout: TextLayout) {
- self.layout = layout
- }
-
- mutating func set(alignment: Alignment) {
- self.alignment = alignment
- }
-
- mutating func set(textAlignment: NSTextAlignment) {
- self.textAlignment = textAlignment
- }
-
- // MARK: - Builder
-
- public static func build(@EditorBuilder content: (Property.Type) -> [Property]) -> Self {
- return content(Property.self).reduce(.init(), { model, editor in
- editor.edit(model)
- })
- }
-
}
// MARK: - Methods
diff --git a/Components/Sources/Common/Views/MessageView.swift b/Components/Sources/Common/Views/MessageView.swift
index 4f66a0002..b9ed88d92 100644
--- a/Components/Sources/Common/Views/MessageView.swift
+++ b/Components/Sources/Common/Views/MessageView.swift
@@ -7,6 +7,7 @@
#if os(iOS)
import UIKit
import ReactiveDataDisplayManager
+import Macro
/// Base view to implement label within cell
public class MessageView: UIView {
@@ -24,7 +25,14 @@ extension MessageView: ConfigurableItem {
// MARK: - Model
- public struct Model: Equatable, AlignmentProvider, TextProvider {
+ @Mutable
+ public struct Model: Equatable, EditorWrapper, AlignmentProvider, TextProvider {
+
+ // MARK: - EditorWrapper
+
+ public static func create() -> MessageView.Model {
+ .init()
+ }
// MARK: - Editor
@@ -52,7 +60,7 @@ extension MessageView: ConfigurableItem {
public static func style(_ value: TextStyle) -> Property {
.init(closure: { model in
var model = model
- model.set(style: value)
+ model.set(textStyle: value)
return model
})
}
@@ -60,7 +68,7 @@ extension MessageView: ConfigurableItem {
public static func layout(_ value: TextLayout) -> Property {
.init(closure: { model in
var model = model
- model.set(layout: value)
+ model.set(textLayout: value)
return model
})
}
@@ -84,7 +92,7 @@ extension MessageView: ConfigurableItem {
public static func insets(_ value: UIEdgeInsets) -> Property {
.init(closure: { model in
var model = model
- model.set(insets: value)
+ model.set(internalEdgeInsets: value)
return model
})
}
@@ -92,7 +100,7 @@ extension MessageView: ConfigurableItem {
public static func background(_ value: BackgroundStyle) -> Property {
.init(closure: { model in
var model = model
- model.set(background: value)
+ model.set(backgroundStyle: value)
return model
})
}
@@ -100,7 +108,7 @@ extension MessageView: ConfigurableItem {
public static func border(_ value: BorderStyle) -> Property {
.init(closure: { model in
var model = model
- model.set(border: value)
+ model.set(borderStyle: value)
return model
})
}
@@ -136,56 +144,6 @@ extension MessageView: ConfigurableItem {
private(set) public var dataDetection: DataDetectionStyle?
private(set) public var selectable: Bool = false
- // MARK: - Mutation
-
- mutating func set(text: TextValue) {
- self.text = text
- }
-
- mutating func set(style: TextStyle) {
- self.textStyle = style
- }
-
- mutating func set(layout: TextLayout) {
- self.textLayout = layout
- }
-
- mutating func set(alignment: Alignment) {
- self.alignment = alignment
- }
-
- mutating func set(textAlignment: NSTextAlignment) {
- self.textAlignment = textAlignment
- }
-
- mutating func set(insets: UIEdgeInsets) {
- self.internalEdgeInsets = insets
- }
-
- mutating func set(background: BackgroundStyle) {
- self.backgroundStyle = background
- }
-
- mutating func set(border: BorderStyle) {
- self.borderStyle = border
- }
-
- mutating func set(dataDetection: DataDetectionStyle) {
- self.dataDetection = dataDetection
- }
-
- mutating func set(selectable: Bool) {
- self.selectable = selectable
- }
-
- // MARK: - Builder
-
- public static func build(@EditorBuilder content: (Property.Type) -> [Property]) -> Self {
- return content(Property.self).reduce(.init(), { model, editor in
- editor.edit(model)
- })
- }
-
}
// MARK: - Methods
diff --git a/Components/project.yml b/Components/project.yml
index cfceac7b3..a7746dff8 100644
--- a/Components/project.yml
+++ b/Components/project.yml
@@ -2,11 +2,12 @@
name: ReactiveDataComponents
options:
# Версия Xcode
- xcodeVersion: 13.4.1
+ xcodeVersion: 15.0.0
# Создание пустых директорий
generateEmptyDirectories: true
# Создание групп
createIntermediateGroups: true
+
targets:
ReactiveDataComponents:
dependencies:
@@ -14,8 +15,8 @@ targets:
type: framework
platform: [iOS, tvOS]
deploymentTarget:
- iOS: 11.0
- tvOS: 11.0
+ iOS: 13.0
+ tvOS: 13.0
scheme:
configVariants: all
testTargets:
@@ -24,7 +25,9 @@ targets:
sources:
- path: Sources
dependencies:
+ - package: Macro
- target: ReactiveDataDisplayManager_${platform}
+
info:
path: Info.plist
properties:
@@ -47,8 +50,8 @@ targets:
type: bundle.unit-test
platform: [iOS, tvOS]
deploymentTarget:
- iOS: 11.0
- tvOS: 11.0
+ iOS: 13.0
+ tvOS: 13.0
scheme:
configVariants: all
sources:
diff --git a/Example/ReactiveChat/Transport/Impl/Protocols+PropertyWrapper.swift b/Example/ReactiveChat/Transport/Impl/Protocols+PropertyWrapper.swift
index 42cf03e65..5af3afb9c 100644
--- a/Example/ReactiveChat/Transport/Impl/Protocols+PropertyWrapper.swift
+++ b/Example/ReactiveChat/Transport/Impl/Protocols+PropertyWrapper.swift
@@ -1,5 +1,5 @@
//
-// Protocols+PropertyWrapper.swift
+// Protocols+EditorWrapper.swift
// ReactiveChat_iOS
//
// Created by Никита Коробейников on 29.05.2023.
diff --git a/Example/project.yml b/Example/project.yml
index 8623d5fe6..3fad64faf 100644
--- a/Example/project.yml
+++ b/Example/project.yml
@@ -8,7 +8,7 @@ include:
name: ReactiveDataDisplayManagerExample
options:
# Версия Xcode
- xcodeVersion: 13.4.1
+ xcodeVersion: 15.0.0
# Создание пустых директорий
generateEmptyDirectories: true
# Создание групп
@@ -16,11 +16,6 @@ options:
settings:
DEVELOPMENT_TEAM: EFAAG9GXN4
-packages:
- Vapor:
- github: vapor/vapor
- from: 4.0.0
-
targets:
ReactiveServer_macOS:
@@ -61,12 +56,12 @@ targets:
ReactiveDataDisplayManagerExample_iOS:
platform: iOS
- deploymentTarget: 11.0
+ deploymentTarget: 13.0
scheme:
configVariants: all
testTargets:
- ReactiveDataDisplayManagerExampleUITests
- gatherCoverageData: true
+ gatherCoverageData: false
sources:
- path: ReactiveDataDisplayManager
dependencies:
diff --git a/Example/targets/template.yml b/Example/targets/template.yml
index a85581351..4dae8f2fd 100644
--- a/Example/targets/template.yml
+++ b/Example/targets/template.yml
@@ -24,11 +24,11 @@ targetTemplates:
type: bundle.ui-testing
platform: iOS
deploymentTarget:
- iOS: 11.0
- tvOS: 10.0
+ iOS: 13.0
+ tvOS: 13.0
scheme:
configVariants: all
- gatherCoverageData: true
+ gatherCoverageData: false
BaseSettings:
settings:
diff --git a/Makefile b/Makefile
index 450c05cd6..9abafad41 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,7 @@ projects:
-bundle exec pod install
## Build Configuration
-destination='platform=iOS Simulator,name=iPhone 14 Pro'
+destination='platform=iOS Simulator,name=iPhone 14'
destination_tv='platform=tvOS Simulator,name=Apple TV'
## Build lib sources for **tvOS** platform
diff --git a/Package.swift b/Package.swift
index ccf7c388e..02b246b38 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.9
import PackageDescription
let package = Package(
diff --git a/Podfile b/Podfile
index f0a50907c..7898cf860 100644
--- a/Podfile
+++ b/Podfile
@@ -22,7 +22,7 @@ abstract_target 'Targets' do
end
target 'ReactiveDataDisplayManagerExample_iOS' do
- platform :ios, '11.0'
+ platform :ios, '13.0'
utils
diffKit
target 'ReactiveDataDisplayManagerExampleUITests'
@@ -37,7 +37,7 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11'
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13'
end
end
end
diff --git a/ReactiveDataDisplayManager.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ReactiveDataDisplayManager.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 2a09a19fc..2387d365e 100644
--- a/ReactiveDataDisplayManager.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/ReactiveDataDisplayManager.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -162,6 +162,15 @@
"version" : "1.0.2"
}
},
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-syntax.git",
+ "state" : {
+ "revision" : "165fc6d22394c1168ff76ab5d951245971ef07e5",
+ "version" : "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-06-05-a"
+ }
+ },
{
"identity" : "vapor",
"kind" : "remoteSourceControl",
diff --git a/SPMExample/ReactiveDataDisplayManagerSPMExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SPMExample/ReactiveDataDisplayManagerSPMExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 000000000..c4f97948a
--- /dev/null
+++ b/SPMExample/ReactiveDataDisplayManagerSPMExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,14 @@
+{
+ "pins" : [
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-syntax.git",
+ "state" : {
+ "revision" : "165fc6d22394c1168ff76ab5d951245971ef07e5",
+ "version" : "509.0.0-swift-DEVELOPMENT-SNAPSHOT-2023-06-05-a"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/SPMExample/ReactiveDataDisplayManagerSPMExample/ExamplePackage/Package.swift b/SPMExample/ReactiveDataDisplayManagerSPMExample/ExamplePackage/Package.swift
index 7d5b26338..b5b230c2c 100644
--- a/SPMExample/ReactiveDataDisplayManagerSPMExample/ExamplePackage/Package.swift
+++ b/SPMExample/ReactiveDataDisplayManagerSPMExample/ExamplePackage/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
diff --git a/project.yml b/project.yml
index 6d6c84864..8db8a50f5 100644
--- a/project.yml
+++ b/project.yml
@@ -4,12 +4,18 @@ include:
- path: Components/project.yml
relativePaths: true
+packages:
+ Macro:
+ path: Components/Macro
+ Vapor:
+ github: vapor/vapor
+ from: 4.0.0
# Название проекта
name: ReactiveDataDisplayManager
options:
# Версия Xcode
- xcodeVersion: 13.4.1
+ xcodeVersion: 15.0.0
# Создание пустых директорий
generateEmptyDirectories: true
# Создание групп
@@ -21,7 +27,7 @@ targets:
type: framework
platform: [iOS, tvOS]
deploymentTarget:
- iOS: 11.0
+ iOS: 13.0
tvOS: 10.0
scheme:
configVariants: all
@@ -52,7 +58,7 @@ targets:
type: bundle.unit-test
platform: [iOS, tvOS]
deploymentTarget:
- iOS: 11.0
+ iOS: 13.0
tvOS: 10.0
scheme:
configVariants: all