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