From 6711e6b2f6cdf42450bc0b6c3d235b4ae1003f54 Mon Sep 17 00:00:00 2001 From: Zizi_Kim Date: Fri, 31 May 2024 21:51:35 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20Wrapper=20=EB=A7=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=AC=ED=98=84=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Package.resolved | 27 ++++++ Package.swift | 12 +-- README.md | 22 ++++- .../Implementation/Complex/WrapperMacro.swift | 85 +++++++++++++++++++ Sources/Macros/Implementation/Plugin.swift | 1 + .../Interface/ComplexMacorsInterfae.swift | 83 +++++++++++++++++- .../Interface/ExpressionMacrosInterface.swift | 3 +- .../Playground/ComplexMacrosPlayground.swift | 58 ++++++++++++- Tests/Macros/Complex/CodableMacroTests.swift | 30 +++++++ Tests/Macros/Complex/WrapperMacroTests.swift | 64 ++++++++++++++ 10 files changed, 373 insertions(+), 12 deletions(-) create mode 100644 Sources/Macros/Implementation/Complex/WrapperMacro.swift create mode 100644 Tests/Macros/Complex/WrapperMacroTests.swift diff --git a/Package.resolved b/Package.resolved index fa0a833..8a11bee 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,23 @@ { "pins" : [ + { + "identity" : "reactorkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/ReactorKit.git", + "state" : { + "revision" : "8fa33f09c6f6621a2aa536d739956d53b84dd139", + "version" : "3.2.0" + } + }, + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift.git", + "state" : { + "revision" : "b06a8c8596e4c3e8e7788e08e720e3248563ce6a", + "version" : "6.7.1" + } + }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", @@ -8,6 +26,15 @@ "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", "version" : "509.1.1" } + }, + { + "identity" : "weakmaptable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/WeakMapTable.git", + "state" : { + "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", + "version" : "1.2.1" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index 13a68b9..72a5527 100644 --- a/Package.swift +++ b/Package.swift @@ -7,8 +7,8 @@ import CompilerPluginSupport let package = Package( name: "Bibbi-Package", platforms: [ - .macOS(.v10_15), - .iOS(.v13) + .iOS(.v15), + .macOS(.v10_15) ], products: [ .library( @@ -23,6 +23,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"), + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0") ], targets: [ .macro( @@ -31,7 +32,7 @@ let package = Package( .product(name: "SwiftSyntax", package: "swift-syntax"), .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), - .product(name: "SwiftDiagnostics", package: "swift-syntax") + .product(name: "SwiftDiagnostics", package: "swift-syntax"), ], path: "Sources/Macros/Implementation" ), @@ -47,7 +48,8 @@ let package = Package( .executableTarget( name: "MacrosPlayground", dependencies: [ - "MacrosInterface" + "MacrosInterface", + .product(name: "ReactorKit", package: "ReactorKit") ], path: "Sources/Macros/Playground" ), @@ -55,7 +57,7 @@ let package = Package( .testTarget( name: "Bibbi-MacroTests", dependencies: [ - "MacrosInterface", + "MacrosImplementation", .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), ], path: "Tests/Macros" diff --git a/README.md b/README.md index 2f80114..8d6ad41 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,29 @@
-Bibbi Package는 삐삐(Bibbi) 앱을 개발하는 데 유용한 `매크로`가 포함되어 있습니다. 해당 리포지토리는 구현된 매크로의 기본 컨셉과 매크로를 활용한 적용법을 명시하고 있습니다. 매크로 예제 코드는 [Playground](Sources/Macros/Playground)에서 찾아보실 수 있으며, 자세한 사용법은 각 매크로에 `Option`키를 누르면 확인할 수 있습니다.
+Bibbi Package는 삐삐(Bibbi) 앱을 개발하는 데 유용한 `매크로`가 포함되어 있습니다. 해당 리포지토리는 구현된 매크로의 기본 컨셉과 매크로를 활용한 적용법을 명시하고 있습니다.
+ +예제 코드는 [Playground](Sources/Macros/Playground)에서 찾아보실 수 있으며, 자세한 적용법은 각 매크로에 `Option`키를 누르면 확인할 수 있습니다.
아래는 구현된 매크로의 목록을 보여줍니다. -## Table Of Macros +## List Of Macros -* [#URL]() +### Attached * [@Codable]() * [@CodableKey]() * [@Deprecated]() + + +### Freestanding + +* [#URL]() + + +## ChangeLog + +| 버전 | 내용 | +| :----: | :------------------: | +| v0.1.0 | 매크로 구현(@Codable 등) | + + diff --git a/Sources/Macros/Implementation/Complex/WrapperMacro.swift b/Sources/Macros/Implementation/Complex/WrapperMacro.swift new file mode 100644 index 0000000..debbd8e --- /dev/null +++ b/Sources/Macros/Implementation/Complex/WrapperMacro.swift @@ -0,0 +1,85 @@ +// +// File.swift +// +// +// Created by 김건우 on 5/31/24. +// + +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct WrapperMacro { } + +extension WrapperMacro: MemberMacro { + + public static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + guard + let _ = declaration.as(ClassDeclSyntax.self) + else { + throw MacroError.message("해당 매크로는 Class에만 적용할 수 있습니다.") + } + + guard + let genericArguments = node.attributeName + .as(IdentifierTypeSyntax.self)? + .genericArgumentClause? + .arguments, + let firstGenericType = genericArguments + .first? + .argument, + let lastGenericType = genericArguments + .last? + .argument + else { + throw MacroError.message("매크로 선언으로 전달된 Generic 타입이 유효하지 않습니다.") + } + + return [ + """ + public typealias R = \(firstGenericType) + """, + """ + public typealias V = \(lastGenericType) + """, + + """ + public func makeViewController() -> V { + return \(lastGenericType)(reactor: makeReactor()) + } + """, + + """ + public var viewController: V { + return makeViewController() + } + """, + """ + public var reactor: R { + return makeReactor() + } + """ + ] + } + +} + +extension WrapperMacro: ExtensionMacro { + + public static func expansion( + of node: AttributeSyntax, + attachedTo declaration: some DeclGroupSyntax, + providingExtensionsOf type: some TypeSyntaxProtocol, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) throws -> [ExtensionDeclSyntax] { + let baseWrapperExtension = try ExtensionDeclSyntax("extension \(type.trimmed): BaseWrapper { }") + + return [baseWrapperExtension] + } + +} diff --git a/Sources/Macros/Implementation/Plugin.swift b/Sources/Macros/Implementation/Plugin.swift index ede8306..1bb942e 100644 --- a/Sources/Macros/Implementation/Plugin.swift +++ b/Sources/Macros/Implementation/Plugin.swift @@ -13,6 +13,7 @@ struct Bibbi_MacroPlugin: CompilerPlugin { var providingMacros: [Macro.Type] = [ CodableKeyMacro.self, CodableMacro.self, + WrapperMacro.self, URLMacro.self, DeprecatedMacro.self ] diff --git a/Sources/Macros/Interface/ComplexMacorsInterfae.swift b/Sources/Macros/Interface/ComplexMacorsInterfae.swift index ce705b5..ea381e9 100644 --- a/Sources/Macros/Interface/ComplexMacorsInterfae.swift +++ b/Sources/Macros/Interface/ComplexMacorsInterfae.swift @@ -5,6 +5,8 @@ // Created by 김건우 on 5/25/24. // +import ReactorKit + // MARK: - Codable Complex /// 타입이 Codable 프로토콜을 준수하게 합니다. @@ -22,19 +24,23 @@ /// var age: String /// } /// -/// // Expanding a Macro /// struct MemberDTO { /// var name: String /// var dayOfBirth: String /// var age: String /// +/// // Begin expansion of "@Codable" /// enum CodingKeys: String, CodingKey { /// case name /// case dayOfBirth = "day_of_birth" /// case age /// } +/// // End expansion of "@Codable" /// } +/// +/// // Begin expansion of "@Codable" /// extension MemberDTO: Codable { } +/// // End expansion of "@Codable" /// ``` /// - Warning: 해당 매크로는 Struct에만 적용할 수 있습니다. /// @@ -62,3 +68,78 @@ public macro CodableKey(name: String) = #externalMacro( module: "MacrosImplementation", type: "CodableKeyMacro" ) + + + +// MARK: - Wrapper Complex + + +/// 타입이 BaseWrapper 프로토콜을 준수하게 합니다. +/// +/// 해당 매크로를 적용한 타입에 BaseWrapper 프로토콜을 준수하게 하고 make() 메서드 및 viewController, reactor 계산 프로퍼티를 추가합니다. +/// +/// 첫 번째 Generic 타입은 Reactor 프로토콜을 준수하는 타입이어야 하고, 두 번째 Generic 타입은 ReactorKit.View 프로토콜을 준수하는 타입이어야 합니다. +/// +/// 아래는 확장되기 전과 후의 코드를 보여줍니다. +/// ```swift +/// @Wrapper +/// public class HomeViewControllerWrapper { +/// public func makeReactor() -> R { +/// return HomeReactor() +/// } +/// +/// // Begin expansion of "@Wrapper" +/// public typealias R = HomeReactor +/// public typealias V = ViewController +/// +/// public func makeViewController() -> V { +/// return HomeViewController(reactor: makeReactor()) +/// } +/// +/// public var viewController: V { +/// return makeViewController() +/// } +/// +/// public var reactor: R { +/// return makeReactor() +/// } +/// // End expansion of "@Wrapper" +/// } +/// // Begin expansion of "@Wrapper" +/// extension HomeViewControllerWrapper: BaseWrapper { } +/// // End expansion of "@Wrapper" +/// ``` +/// +/// - 참고: BaseWrapper 프로토콜은 아래와 같이 선언되어 있습니다. +/// ```swift +/// public protocol BaseWrapper { +/// associatedtype R: Reactor +/// associatedtype V: ReactorKit.View +/// +/// func makeReactor() -> R +/// func makeViewController() -> V +/// } +/// ``` +/// +/// - Warning: 해당 매크로는 Class에만 적용할 수 있습니다. +/// +/// - Author: 김소월 +/// +@attached(member, names: arbitrary) +@attached(extension, conformances: BaseWrapper) +public macro Wrapper() = #externalMacro( + module: "MacrosImplementation", + type: "WrapperMacro" +) + + +public protocol BaseWrapper { + associatedtype R: Reactor + associatedtype V: ReactorKit.View + + func makeReactor() -> R + func makeViewController() -> V +} + + + diff --git a/Sources/Macros/Interface/ExpressionMacrosInterface.swift b/Sources/Macros/Interface/ExpressionMacrosInterface.swift index 201d6ff..3008a5c 100644 --- a/Sources/Macros/Interface/ExpressionMacrosInterface.swift +++ b/Sources/Macros/Interface/ExpressionMacrosInterface.swift @@ -18,8 +18,9 @@ import Foundation /// ```swift /// let apple = #URL("https://www.apple.com/kr") /// -/// // Expanding a macro +/// // Begin expansion of "#URL" /// let apple = URL(string: "https://www.apple.com/kr") +/// // End expansion of "#URL" /// ``` /// /// - Note: 코드가 확장되기 전에 확장 단계에서 인자로 주어진 문자열 리터럴이 유효한지 확인하기에 런타임 도중 Error를 방출하지 않습니다. diff --git a/Sources/Macros/Playground/ComplexMacrosPlayground.swift b/Sources/Macros/Playground/ComplexMacrosPlayground.swift index 48b909e..ffeae79 100644 --- a/Sources/Macros/Playground/ComplexMacrosPlayground.swift +++ b/Sources/Macros/Playground/ComplexMacrosPlayground.swift @@ -8,8 +8,6 @@ import Foundation import MacrosInterface - - // MARK: - Codable Complex @Codable @@ -20,6 +18,55 @@ public struct MemberDTO { } +// MARK: - Wrapper Complex + +#if canImport(UIKit) + +import UIKit +import ReactorKit + +public class SomeReactor: Reactor { + public typealias Action = NoAction + + public struct State { } + + public var initialState: State = State() +} + +public class SomeViewController: UIViewController, ReactorKit.View { + public typealias Reactor = SomeReactor + public var disposeBag = DisposeBag() + + public convenience init(reactor: Reactor) { + self.init() + self.reactor = reactor + } + + public override func viewDidLoad() { + super.viewDidLoad() + } + + public func bind(reactor: SomeReactor) { } +} + +@Wrapper +public class SomeViewControlllerWrapper { + + public func makeReactor() -> R { + return SomeReactor() + } + +} + + +func runWrapperMacro() { + let someViewController = SomeViewControlllerWrapper().viewController + print("SomeViewController: \(someViewController)") +} + +#endif + + func runComplexMacorsPlayground() { @@ -35,6 +82,13 @@ func runComplexMacorsPlayground() { print("Decoded Member: \(decodedMember)") } + + #if canImport(UIKit) + + runWrapperMacro() + + #endif + } diff --git a/Tests/Macros/Complex/CodableMacroTests.swift b/Tests/Macros/Complex/CodableMacroTests.swift index 8889b76..f10ac54 100644 --- a/Tests/Macros/Complex/CodableMacroTests.swift +++ b/Tests/Macros/Complex/CodableMacroTests.swift @@ -44,4 +44,34 @@ final class CodableMacroTests: XCTestCase { } + func testCodableKeyMacro() throws { + + assertMacroExpansion( + """ + @Codable + struct Member { + var name: String + @CodableKey(name: "day_of_birth") var dayOfBirth: String + } + """, + expandedSource: + """ + struct Member { + var name: String + var dayOfBirth: String + + private enum CodingKeys: String, CodingKey { + case name + case dayOfBirth = "day_of_birth" + } + } + + extension Member: Codable { + } + """, + macros: testMacros + ) + + } + } diff --git a/Tests/Macros/Complex/WrapperMacroTests.swift b/Tests/Macros/Complex/WrapperMacroTests.swift new file mode 100644 index 0000000..1f9db8f --- /dev/null +++ b/Tests/Macros/Complex/WrapperMacroTests.swift @@ -0,0 +1,64 @@ +// +// File.swift +// +// +// Created by 김건우 on 5/31/24. +// + +import SwiftSyntax +import SwiftSyntaxMacros +import SwiftSyntaxMacrosTestSupport +import MacrosImplementation +import XCTest + +fileprivate let testMacros: [String: Macro.Type] = [ + "Wrapper": WrapperMacro.self +] + +final class WrapperMacroTests: XCTest { + + func testWrapperMacro() throws { + + assertMacroExpansion( + """ + @Wrapper + public class SomeViewControlllerWrapper { + + public func makeReactor() -> R { + return SomeReactor() + } + + } + """, + expandedSource: + """ + public class SomeViewControlllerWrapper { + + public func makeReactor() -> R { + return SomeReactor() + } + + public typealias R = SomeReactor + + public typealias V = SomeViewController + + public func makeViewController() -> V { + return SomeViewController(reactor: makeReactor()) + } + + public var viewController: V { + return makeViewController() + } + + public var reactor: R { + return makeReactor() + } + + } + """, + macros: testMacros + ) + + } + +} From bec9f1129c12baa1698d7c5bb3344926d4ff6575 Mon Sep 17 00:00:00 2001 From: Zizi_Kim Date: Fri, 31 May 2024 22:30:59 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20DependencyValue=20=EB=A7=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=9C=20=EA=B5=AC=ED=98=84=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Package.resolved | 45 ++++++++++++++ Package.swift | 10 +-- .../Accessor/DependencyValueMacro.swift | 38 ++++++++++++ Sources/Macros/Implementation/Plugin.swift | 1 + .../Interface/AccessorMacrosInterface.swift | 16 +++++ .../Playground/AccessorMacrosPlayground.swift | 47 ++++++++++++++ Sources/Macros/Playground/main.swift | 4 ++ .../Accessor/DepdencyValueMacroTests.swift | 46 ++++++++++++++ Tests/Macros/Complex/WrapperMacroTests.swift | 61 +++++++++---------- 9 files changed, 233 insertions(+), 35 deletions(-) create mode 100644 Sources/Macros/Implementation/Accessor/DependencyValueMacro.swift create mode 100644 Sources/Macros/Interface/AccessorMacrosInterface.swift create mode 100644 Sources/Macros/Playground/AccessorMacrosPlayground.swift create mode 100644 Tests/Macros/Accessor/DepdencyValueMacroTests.swift diff --git a/Package.resolved b/Package.resolved index 8a11bee..9cc138f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "combine-schedulers", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/combine-schedulers", + "state" : { + "revision" : "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", + "version" : "1.0.0" + } + }, { "identity" : "reactorkit", "kind" : "remoteSourceControl", @@ -18,6 +27,33 @@ "version" : "6.7.1" } }, + { + "identity" : "swift-clocks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-clocks", + "state" : { + "revision" : "a8421d68068d8f45fbceb418fbf22c5dad4afd33", + "version" : "1.0.2" + } + }, + { + "identity" : "swift-concurrency-extras", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-concurrency-extras", + "state" : { + "revision" : "bb5059bde9022d69ac516803f4f227d8ac967f71", + "version" : "1.1.0" + } + }, + { + "identity" : "swift-dependencies", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-dependencies", + "state" : { + "revision" : "350e1e119babe8525f9bd155b76640a5de270184", + "version" : "1.3.0" + } + }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", @@ -35,6 +71,15 @@ "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", "version" : "1.2.1" } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2", + "version" : "1.1.2" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index 72a5527..e338e8e 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,8 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"), - .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0") + .package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"), + .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.3.0") ], targets: [ .macro( @@ -40,7 +41,9 @@ let package = Package( .target( name: "MacrosInterface", dependencies: [ - "MacrosImplementation" + "MacrosImplementation", + .product(name: "ReactorKit", package: "ReactorKit"), + .product(name: "Dependencies", package: "swift-dependencies") ], path: "Sources/Macros/Interface" ), @@ -48,8 +51,7 @@ let package = Package( .executableTarget( name: "MacrosPlayground", dependencies: [ - "MacrosInterface", - .product(name: "ReactorKit", package: "ReactorKit") + "MacrosInterface" ], path: "Sources/Macros/Playground" ), diff --git a/Sources/Macros/Implementation/Accessor/DependencyValueMacro.swift b/Sources/Macros/Implementation/Accessor/DependencyValueMacro.swift new file mode 100644 index 0000000..580c73a --- /dev/null +++ b/Sources/Macros/Implementation/Accessor/DependencyValueMacro.swift @@ -0,0 +1,38 @@ +// +// File.swift +// +// +// Created by 김건우 on 5/31/24. +// + +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct DependencyValueMacro: AccessorMacro { + + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + guard + case let .argumentList(arguments) = node.arguments, + let expression = arguments.first? + .expression + else { + throw MacroError.message("유효하지 않은 인자입니다.") + } + + return [ + """ + get { self[\(expression)] } + """, + """ + set { self[\(expression)] = newValue } + """ + ] + } + +} + diff --git a/Sources/Macros/Implementation/Plugin.swift b/Sources/Macros/Implementation/Plugin.swift index 1bb942e..e37b459 100644 --- a/Sources/Macros/Implementation/Plugin.swift +++ b/Sources/Macros/Implementation/Plugin.swift @@ -11,6 +11,7 @@ import SwiftSyntaxMacros @main struct Bibbi_MacroPlugin: CompilerPlugin { var providingMacros: [Macro.Type] = [ + DependencyValueMacro.self, CodableKeyMacro.self, CodableMacro.self, WrapperMacro.self, diff --git a/Sources/Macros/Interface/AccessorMacrosInterface.swift b/Sources/Macros/Interface/AccessorMacrosInterface.swift new file mode 100644 index 0000000..4a6c11d --- /dev/null +++ b/Sources/Macros/Interface/AccessorMacrosInterface.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by 김건우 on 5/31/24. +// + +import Dependencies + +// MARK: - DependecyValue Accessor + +@attached(accessor) +public macro DependencyValue(for: any DependencyKey.Type) = #externalMacro( + module: "MacrosImplementation", + type: "DependencyValueMacro" +) diff --git a/Sources/Macros/Playground/AccessorMacrosPlayground.swift b/Sources/Macros/Playground/AccessorMacrosPlayground.swift new file mode 100644 index 0000000..7f35d4a --- /dev/null +++ b/Sources/Macros/Playground/AccessorMacrosPlayground.swift @@ -0,0 +1,47 @@ +// +// File.swift +// +// +// Created by 김건우 on 5/31/24. +// + +import Dependencies +import MacrosInterface + +// MARK: - DependencyValue Accessor + +protocol RepositoryProtocol { + func request() -> String +} +class Repository: RepositoryProtocol { + func request() -> String { + return "Hello, World!" + } +} + +struct RepositoryKey: DependencyKey { + static let liveValue: RepositoryProtocol = Repository() +} +extension DependencyValues { + @DependencyValue(for: RepositoryKey.self) + var repository: RepositoryProtocol +} + +class UseCase { + @Dependency(\.repository) private var repository: RepositoryProtocol + + func execute() -> String { + repository.request() + } +} + + + + + +func runAccessorMacrosPlayground() { + + let usecase = UseCase() + print("DepdencyValue Test: \(usecase.execute())") + +} diff --git a/Sources/Macros/Playground/main.swift b/Sources/Macros/Playground/main.swift index 2083d5f..09ea0e6 100644 --- a/Sources/Macros/Playground/main.swift +++ b/Sources/Macros/Playground/main.swift @@ -7,6 +7,10 @@ import MacrosInterface +// MARK: - Accessor Macro + +runAccessorMacrosPlayground() + // MARK: - Complex Macro diff --git a/Tests/Macros/Accessor/DepdencyValueMacroTests.swift b/Tests/Macros/Accessor/DepdencyValueMacroTests.swift new file mode 100644 index 0000000..fcd9790 --- /dev/null +++ b/Tests/Macros/Accessor/DepdencyValueMacroTests.swift @@ -0,0 +1,46 @@ +// +// File.swift +// +// +// Created by 김건우 on 5/31/24. +// + +import SwiftSyntaxMacros +import SwiftSyntaxMacrosTestSupport +import MacrosImplementation +import XCTest + +fileprivate let testMacros: [String: Macro.Type] = [ + "DependencyValue": DependecyValueMacro.self +] + +final class DepdencyValueMacroTests: XCTestCase { + + func testDependecyValueMacro() throws { + + assertMacroExpansion( + """ + extension DependencyValues { + @DependencyValue(for: UseCaseKey.self) + var usecas: UseCaseProtocol + } + """, + expandedSource: + """ + extension DependencyValues { + var usecas: UseCaseProtocol { + get { + self [UseCaseKey.self] + } + set { + self [UseCaseKey.self] = newValue + } + } + } + """, + macros: testMacros + ) + + } + +} diff --git a/Tests/Macros/Complex/WrapperMacroTests.swift b/Tests/Macros/Complex/WrapperMacroTests.swift index 1f9db8f..8100dfa 100644 --- a/Tests/Macros/Complex/WrapperMacroTests.swift +++ b/Tests/Macros/Complex/WrapperMacroTests.swift @@ -5,7 +5,6 @@ // Created by 김건우 on 5/31/24. // -import SwiftSyntax import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import MacrosImplementation @@ -20,43 +19,43 @@ final class WrapperMacroTests: XCTest { func testWrapperMacro() throws { assertMacroExpansion( - """ - @Wrapper - public class SomeViewControlllerWrapper { - - public func makeReactor() -> R { - return SomeReactor() + """ + @Wrapper + public class SomeViewControlllerWrapper { + + public func makeReactor() -> R { + return SomeReactor() + } + } + """, + expandedSource: + """ + public class SomeViewControlllerWrapper { + + public func makeReactor() -> R { + return SomeReactor() + } - } - """, - expandedSource: - """ - public class SomeViewControlllerWrapper { - - public func makeReactor() -> R { - return SomeReactor() - } - - public typealias R = SomeReactor + public typealias R = SomeReactor - public typealias V = SomeViewController + public typealias V = SomeViewController - public func makeViewController() -> V { - return SomeViewController(reactor: makeReactor()) - } + public func makeViewController() -> V { + return SomeViewController(reactor: makeReactor()) + } - public var viewController: V { - return makeViewController() - } + public var viewController: V { + return makeViewController() + } - public var reactor: R { - return makeReactor() + public var reactor: R { + return makeReactor() + } + } - - } - """, - macros: testMacros + """, + macros: testMacros ) } From c35521c76a2e964b3cd8028a1da8cbff360e1cf8 Mon Sep 17 00:00:00 2001 From: Zizi_Kim Date: Sat, 1 Jun 2024 10:08:06 +0900 Subject: [PATCH 3/5] =?UTF-8?q?docs:=20=EB=AC=B8=EC=84=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Package.swift | 5 ---- README.md | 29 ++++++++----------- .../Interface/AccessorMacrosInterface.swift | 19 ++++++++++++ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Package.swift b/Package.swift index e338e8e..3ced820 100644 --- a/Package.swift +++ b/Package.swift @@ -14,11 +14,6 @@ let package = Package( .library( name: "Macros", targets: ["MacrosInterface"] - ), - - .executable( - name: "MacrosPlayground", - targets: ["MacrosPlayground"] ) ], dependencies: [ diff --git a/README.md b/README.md index 8d6ad41..760c2b2 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,24 @@
-Bibbi Package는 삐삐(Bibbi) 앱을 개발하는 데 유용한 `매크로`가 포함되어 있습니다. 해당 리포지토리는 구현된 매크로의 기본 컨셉과 매크로를 활용한 적용법을 명시하고 있습니다.
+**Bibbi Package**는 삐삐(Bibbi)를 개발하는 데 유용한 `라이브러리`와 `매크로`가 포함되어 있습니다. 이 리포지토리는 구현된 매크로의 기본 지식과 활용 방법을 설명하고 있습니다.
-예제 코드는 [Playground](Sources/Macros/Playground)에서 찾아보실 수 있으며, 자세한 적용법은 각 매크로에 `Option`키를 누르면 확인할 수 있습니다.
+## Featrues -아래는 구현된 매크로의 목록을 보여줍니다. +### Macros -## List Of Macros +- [v] [@Codable]() +- [v] [@CodableKey]() +- [v] [@Deprecated]() +- [v] [@DependencyValue]() +- [v] [@Wrapepr]() -### Attached - -* [@Codable]() -* [@CodableKey]() -* [@Deprecated]() - - -### Freestanding - -* [#URL]() +## Requirements +* iOS 15.0+ / macOS 10.15+ +* Swift 5.0+ ## ChangeLog | 버전 | 내용 | | :----: | :------------------: | -| v0.1.0 | 매크로 구현(@Codable 등) | - - +| - | - | diff --git a/Sources/Macros/Interface/AccessorMacrosInterface.swift b/Sources/Macros/Interface/AccessorMacrosInterface.swift index 4a6c11d..a4dda1a 100644 --- a/Sources/Macros/Interface/AccessorMacrosInterface.swift +++ b/Sources/Macros/Interface/AccessorMacrosInterface.swift @@ -9,6 +9,25 @@ import Dependencies // MARK: - DependecyValue Accessor +/// 의존성 주입을 위한 getter/setter 코드를 전개합니다. +/// +/// 아래는 확장되기 전과 후의 코드를 보여줍니다. +/// ```swift +/// extension DependencyValues { +/// @DependencyValue(for: LinkRepositoryKey.self) +/// var linkRepository: LinkRepositoryProtocol +/// // Begin expansion of "@DependencyValue" +/// { +/// get { self[LinkRepositoryKey.self] } +/// set { self[LinkRepositoryKey.self] = newValue } +/// } +/// // End expansion of "@DependencyValue" +/// } +///``` +/// NOTE: - 의존성 주입에 관한 내용은 [여기](https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies)를 참조하세요. +/// +/// Author: - 김소월 +/// @attached(accessor) public macro DependencyValue(for: any DependencyKey.Type) = #externalMacro( module: "MacrosImplementation", From d64c0375accfc9b436d8ac7e9bd19339b48ee9a9 Mon Sep 17 00:00:00 2001 From: Zizi_Kim Date: Sat, 1 Jun 2024 11:29:12 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20DependencyVales=20=EB=A7=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=9C=20=EA=B5=AC=ED=98=84=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Package.swift | 6 ++ .../Extensions/AttributeListDecl+Ext.swift | 33 ++++++++++ .../Macros/Helper/Extensions/String+Ext.swift | 21 ++++++ .../Helper/Extensions/VariableDecl+Ext.swift | 66 +++++++++++++++++++ .../DependencyValuesMacro.swift | 51 ++++++++++++++ Sources/Macros/Implementation/Plugin.swift | 1 + .../MemberAttributeMacrosInterface.swift | 9 +++ .../Playground/AccessorMacrosPlayground.swift | 57 +++++++++++----- 8 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 Sources/Macros/Helper/Extensions/AttributeListDecl+Ext.swift create mode 100644 Sources/Macros/Helper/Extensions/String+Ext.swift create mode 100644 Sources/Macros/Helper/Extensions/VariableDecl+Ext.swift create mode 100644 Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift diff --git a/Package.swift b/Package.swift index 3ced820..16f4908 100644 --- a/Package.swift +++ b/Package.swift @@ -25,6 +25,7 @@ let package = Package( .macro( name: "MacrosImplementation", dependencies: [ + "MacrosHelper", .product(name: "SwiftSyntax", package: "swift-syntax"), .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), @@ -32,6 +33,11 @@ let package = Package( ], path: "Sources/Macros/Implementation" ), + + .target( + name: "MacrosHelper", + path: "Sources/Macros/Helper" + ), .target( name: "MacrosInterface", diff --git a/Sources/Macros/Helper/Extensions/AttributeListDecl+Ext.swift b/Sources/Macros/Helper/Extensions/AttributeListDecl+Ext.swift new file mode 100644 index 0000000..4dba7e9 --- /dev/null +++ b/Sources/Macros/Helper/Extensions/AttributeListDecl+Ext.swift @@ -0,0 +1,33 @@ +// +// File.swift +// +// +// Created by 김건우 on 6/1/24. +// + +import SwiftSyntax + +public extension AttributeListSyntax { + + /// 특정 속성이 적용되었는지 확인합니다. + /// + /// - Parameters: + /// - attributeName: 속성 이름 + /// - Returns: 속성이 있으면 true, 없으면 false + /// + /// - Author: 김소월 + /// + func isAttributeApplied(_ attributeName: String) -> Bool { + guard + let _ = self.first(where: { attribute in + attribute + .as(AttributeSyntax.self)? + .attributeName + .as(IdentifierTypeSyntax.self)? + .name.text == attributeName + }) + else { return false } + return true + } + +} diff --git a/Sources/Macros/Helper/Extensions/String+Ext.swift b/Sources/Macros/Helper/Extensions/String+Ext.swift new file mode 100644 index 0000000..43451e2 --- /dev/null +++ b/Sources/Macros/Helper/Extensions/String+Ext.swift @@ -0,0 +1,21 @@ +// +// File.swift +// +// +// Created by 김건우 on 6/1/24. +// + +import Foundation + +public extension String { + + /// 문자열의 첫 문자를 대문자로 바꿉니다. + /// + /// - Returns: String + /// - Author: 김소월 + /// + func capitalizeFirstLetter() -> String { + return prefix(1).uppercased() + dropFirst() + } + +} diff --git a/Sources/Macros/Helper/Extensions/VariableDecl+Ext.swift b/Sources/Macros/Helper/Extensions/VariableDecl+Ext.swift new file mode 100644 index 0000000..06f0873 --- /dev/null +++ b/Sources/Macros/Helper/Extensions/VariableDecl+Ext.swift @@ -0,0 +1,66 @@ +// +// File.swift +// +// +// Created by 김건우 on 6/1/24. +// + +import SwiftSyntax + +public extension VariableDeclSyntax { + + /// 프로퍼티에 초기값이 있는지 확인합니다. + /// + /// - Returns: 초기값이 있으면 true, 없으면 false + /// - Author: 김소월 + /// + var hasIntializer: Bool { + // 라인 하나에 바인딩 하나 + if bindings.count != 1 { + return false + } + + let binding = bindings.first + guard + let _ = binding?.initializer?.value + else { return true } + return false + } + + /// 저장 프로퍼티인지 확인합니다. + /// + /// - Returns: 저장 프로퍼티라면 true, 아니라면 false + /// - Author: 김소월 + /// + var isStoredProperty: Bool { + // 라인 하나에 바인딩 하나 + if bindings.count != 1 { + return false + } + + let binding = bindings.first + switch binding?.accessorBlock?.accessors { + case .none: + return true + + case let .accessors(accessors): + for accessor in accessors { + switch accessor.accessorSpecifier.tokenKind { + case .keyword(.willSet), .keyword(.didSet): + // willset, didset 옵저버는 저장 프로퍼티 + break + + default: + return false + } + } + + return true + + case .getter: + // + return false + } + } + +} diff --git a/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift b/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift new file mode 100644 index 0000000..b50c24b --- /dev/null +++ b/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift @@ -0,0 +1,51 @@ +// +// File.swift +// +// +// Created by 김건우 on 6/1/24. +// + +import MacrosHelper +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct DependencyValuesMacro: MemberAttributeMacro { + + public static func expansion( + of node: AttributeSyntax, + attachedTo declaration: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AttributeSyntax] { + guard + let _ = declaration.as(ExtensionDeclSyntax.self) + else { + throw MacroError.message("해당 매크로는 Extension에만 적용할 수 있습니다.") + } + + guard + let varDecl = member.as(VariableDeclSyntax.self), + varDecl.isStoredProperty, + let identifier = varDecl.bindings + .first?.pattern + .as(IdentifierPatternSyntax.self)? + .identifier.text + else { + return [] + } + + if !varDecl.attributes.isAttributeApplied("DependencyValue") { + let capitalizedIdentifier = identifier.capitalizeFirstLetter() + + return [ + """ + @DependencyValue(for: \(raw: capitalizedIdentifier)Key.self) + """ + ] + } + return [] + + } + +} diff --git a/Sources/Macros/Implementation/Plugin.swift b/Sources/Macros/Implementation/Plugin.swift index e37b459..4314028 100644 --- a/Sources/Macros/Implementation/Plugin.swift +++ b/Sources/Macros/Implementation/Plugin.swift @@ -16,6 +16,7 @@ struct Bibbi_MacroPlugin: CompilerPlugin { CodableMacro.self, WrapperMacro.self, URLMacro.self, + DependencyValuesMacro.self, DeprecatedMacro.self ] } diff --git a/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift b/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift index e278074..ddaf419 100644 --- a/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift +++ b/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift @@ -18,3 +18,12 @@ public macro Deprecated() = #externalMacro( module: "MacrosImplementation", type: "DeprecatedMacro" ) + + +// MARK: - Dependency Values Member Attribute + +@attached(memberAttribute) +public macro DependencyVales() = #externalMacro( + module: "MacrosImplementation", + type: "DependencyValuesMacro" +) diff --git a/Sources/Macros/Playground/AccessorMacrosPlayground.swift b/Sources/Macros/Playground/AccessorMacrosPlayground.swift index 7f35d4a..4bbf880 100644 --- a/Sources/Macros/Playground/AccessorMacrosPlayground.swift +++ b/Sources/Macros/Playground/AccessorMacrosPlayground.swift @@ -10,30 +10,50 @@ import MacrosInterface // MARK: - DependencyValue Accessor -protocol RepositoryProtocol { +protocol MeRepositoryProtocol { func request() -> String } -class Repository: RepositoryProtocol { +class MeRepository: MeRepositoryProtocol { func request() -> String { - return "Hello, World!" + return """ + { + \"name\":\"김소월\", + \"day_of_birth\":\"1998-03-21\", + \"age\": 27 + } + """ } } -struct RepositoryKey: DependencyKey { - static let liveValue: RepositoryProtocol = Repository() +protocol MemberRepositoryProtocol { + func request() -> String } -extension DependencyValues { - @DependencyValue(for: RepositoryKey.self) - var repository: RepositoryProtocol +class MemberRepository: MemberRepositoryProtocol { + func request() -> String { + return """ + { + \"members\":\"[김소월, 김제니, 마미미]\", + } + """ + } } -class UseCase { - @Dependency(\.repository) private var repository: RepositoryProtocol - - func execute() -> String { - repository.request() - } +// DependencyKey +struct MeRepositoryKey: DependencyKey { + static let liveValue: MeRepositoryProtocol = MeRepository() } +struct MemberRepositoryKey: DependencyKey { + static let liveValue: MemberRepositoryProtocol = MemberRepository() +} + +// DependencyValues +@DependencyVales +extension DependencyValues { + var meRepository: MeRepositoryProtocol + @DependencyValue(for: MemberRepositoryKey.self) + var memberRepository: MemberRepositoryProtocol +} + @@ -41,7 +61,12 @@ class UseCase { func runAccessorMacrosPlayground() { - let usecase = UseCase() - print("DepdencyValue Test: \(usecase.execute())") + @Dependency(\.meRepository) var meRepository + let me = meRepository.request() + print("Requested Data from MeRepository: \(me)") + + @Dependency(\.memberRepository) var memberRepository + let members = memberRepository.request() + print("Requested Data from MemberRepository: \(members)") } From 4782d5e6a0be14a80bcab6e0f20dbc9afb937835 Mon Sep 17 00:00:00 2001 From: Zizi_Kim Date: Sat, 1 Jun 2024 12:07:13 +0900 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20=EB=AC=B8=EC=84=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++-- .../Implementation/Complex/CodableMacro.swift | 2 +- .../Implementation/Complex/WrapperMacro.swift | 2 +- .../DependencyValuesMacro.swift | 2 +- .../Interface/AccessorMacrosInterface.swift | 8 +++-- .../Interface/ComplexMacorsInterfae.swift | 18 +++++------ .../Interface/ExpressionMacrosInterface.swift | 6 ++-- .../MemberAttributeMacrosInterface.swift | 32 +++++++++++++++++-- 8 files changed, 54 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 760c2b2..d487a57 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,11 @@ ### Macros - [v] [@Codable]() -- [v] [@CodableKey]() +- [v] [@CodableKey(_:)]() - [v] [@Deprecated]() -- [v] [@DependencyValue]() -- [v] [@Wrapepr]() +- [v] [@DependencyValue(for:)]() +- [v] [@DependencyValues]() +- [v] [@Wrapper]() ## Requirements diff --git a/Sources/Macros/Implementation/Complex/CodableMacro.swift b/Sources/Macros/Implementation/Complex/CodableMacro.swift index 51e99b6..b5f3358 100644 --- a/Sources/Macros/Implementation/Complex/CodableMacro.swift +++ b/Sources/Macros/Implementation/Complex/CodableMacro.swift @@ -21,7 +21,7 @@ extension CodableMacro: MemberMacro { guard let structDecl = declaration.as(StructDeclSyntax.self) else { - throw MacroError.message("해당 매크로는 Struct에만 적용할 수 있습니다.") + throw MacroError.message("이 매크로는 Struct에만 적용할 수 있습니다.") } let memberList = structDecl.memberBlock.members diff --git a/Sources/Macros/Implementation/Complex/WrapperMacro.swift b/Sources/Macros/Implementation/Complex/WrapperMacro.swift index debbd8e..e7a5467 100644 --- a/Sources/Macros/Implementation/Complex/WrapperMacro.swift +++ b/Sources/Macros/Implementation/Complex/WrapperMacro.swift @@ -21,7 +21,7 @@ extension WrapperMacro: MemberMacro { guard let _ = declaration.as(ClassDeclSyntax.self) else { - throw MacroError.message("해당 매크로는 Class에만 적용할 수 있습니다.") + throw MacroError.message("이 매크로는 Class에만 적용할 수 있습니다.") } guard diff --git a/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift b/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift index b50c24b..86cd99a 100644 --- a/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift +++ b/Sources/Macros/Implementation/MemberAttribute/DependencyValuesMacro.swift @@ -21,7 +21,7 @@ public struct DependencyValuesMacro: MemberAttributeMacro { guard let _ = declaration.as(ExtensionDeclSyntax.self) else { - throw MacroError.message("해당 매크로는 Extension에만 적용할 수 있습니다.") + throw MacroError.message("이 매크로는 Extension에만 적용할 수 있습니다.") } guard diff --git a/Sources/Macros/Interface/AccessorMacrosInterface.swift b/Sources/Macros/Interface/AccessorMacrosInterface.swift index a4dda1a..a2bd482 100644 --- a/Sources/Macros/Interface/AccessorMacrosInterface.swift +++ b/Sources/Macros/Interface/AccessorMacrosInterface.swift @@ -11,7 +11,7 @@ import Dependencies /// 의존성 주입을 위한 getter/setter 코드를 전개합니다. /// -/// 아래는 확장되기 전과 후의 코드를 보여줍니다. +/// 아래는 전개되기 전과 후의 코드를 보여줍니다. /// ```swift /// extension DependencyValues { /// @DependencyValue(for: LinkRepositoryKey.self) @@ -24,7 +24,11 @@ import Dependencies /// // End expansion of "@DependencyValue" /// } ///``` -/// NOTE: - 의존성 주입에 관한 내용은 [여기](https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies)를 참조하세요. +/// +/// - Parameters: +/// - key: DependencyKey 프르토콜 준수 객체의 메타 타입 +/// +/// - NOTE: 의존성 주입에 관한 내용은 [여기](https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies)를 참조하세요. /// /// Author: - 김소월 /// diff --git a/Sources/Macros/Interface/ComplexMacorsInterfae.swift b/Sources/Macros/Interface/ComplexMacorsInterfae.swift index ea381e9..3359539 100644 --- a/Sources/Macros/Interface/ComplexMacorsInterfae.swift +++ b/Sources/Macros/Interface/ComplexMacorsInterfae.swift @@ -11,11 +11,11 @@ import ReactorKit /// 타입이 Codable 프로토콜을 준수하게 합니다. /// -/// 해당 매크로는 적용한 타입에 Codable 프로토콜을 준수하는 Extension과 CodingKeys 코드를 확장합니다. +/// 이 매크로는 적용한 타입에 Codable 프로토콜을 준수하는 Extension과 CodingKeys 코드를 전개합니다. /// -/// CodingKey를 바꾸고 싶다면 멤버에 @CodableKey(name:) 매크로를 적용해야 합니다. 해당 매크로는 단독으로 사용할 수 없으며, 반드시 @Codable 매크로와 함께 작성해야 합니다. +/// CodingKey를 바꾸고 싶다면 멤버에 @CodableKey(name:) 매크로를 적용해야 합니다. 이 매크로는 단독으로 사용할 수 없으며, 반드시 @Codable 매크로와 함께 작성해야 합니다. /// -/// 아래는 확장되기 전과 후의 코드를 보여줍니다. +/// 아래는 전개되기 전과 후의 코드를 보여줍니다. /// ```swift /// @Codable /// struct MemberDTO { @@ -42,7 +42,7 @@ import ReactorKit /// extension MemberDTO: Codable { } /// // End expansion of "@Codable" /// ``` -/// - Warning: 해당 매크로는 Struct에만 적용할 수 있습니다. +/// - Warning: 이 매크로는 Struct에만 적용할 수 있습니다. /// /// - Author: 김소월 /// @@ -55,7 +55,7 @@ public macro Codable() = #externalMacro( /// CodingKey를 바꿉니다. /// -/// 해당 매크로를 적용한 멤버의 CodingKey를 바꾸어 CodingKeys 코드를 확장합니다. +/// 이 매크로를 적용한 멤버의 CodingKey를 바꾸어 CodingKeys 코드를 전개합니다. /// /// - Parameters: /// - name: CodingKey를 입력합니다. @@ -76,11 +76,11 @@ public macro CodableKey(name: String) = #externalMacro( /// 타입이 BaseWrapper 프로토콜을 준수하게 합니다. /// -/// 해당 매크로를 적용한 타입에 BaseWrapper 프로토콜을 준수하게 하고 make() 메서드 및 viewController, reactor 계산 프로퍼티를 추가합니다. +/// 이 매크로를 적용한 타입에 BaseWrapper 프로토콜을 준수하게 하고 make() 메서드 및 viewController, reactor 계산 프로퍼티를 추가합니다. /// /// 첫 번째 Generic 타입은 Reactor 프로토콜을 준수하는 타입이어야 하고, 두 번째 Generic 타입은 ReactorKit.View 프로토콜을 준수하는 타입이어야 합니다. /// -/// 아래는 확장되기 전과 후의 코드를 보여줍니다. +/// 아래는 전개되기 전과 후의 코드를 보여줍니다. /// ```swift /// @Wrapper /// public class HomeViewControllerWrapper { @@ -117,11 +117,10 @@ public macro CodableKey(name: String) = #externalMacro( /// associatedtype V: ReactorKit.View /// /// func makeReactor() -> R -/// func makeViewController() -> V /// } /// ``` /// -/// - Warning: 해당 매크로는 Class에만 적용할 수 있습니다. +/// - Warning: 이 매크로는 Class에만 적용할 수 있습니다. /// /// - Author: 김소월 /// @@ -138,7 +137,6 @@ public protocol BaseWrapper { associatedtype V: ReactorKit.View func makeReactor() -> R - func makeViewController() -> V } diff --git a/Sources/Macros/Interface/ExpressionMacrosInterface.swift b/Sources/Macros/Interface/ExpressionMacrosInterface.swift index 3008a5c..7899817 100644 --- a/Sources/Macros/Interface/ExpressionMacrosInterface.swift +++ b/Sources/Macros/Interface/ExpressionMacrosInterface.swift @@ -11,10 +11,10 @@ import Foundation /// 유효한 URL을 반환합니다. /// -/// 해당 매크로는 인자로 주어진 문자열 리터럴을 [URL](https://developer.apple.com/documentation/foundation/url) 타입으로 변환한 코드를 확장합니다. +/// 이 매크로는 매개변수로 넘겨진 문자열 리터럴을 [URL](https://developer.apple.com/documentation/foundation/url) 타입으로 변환한 코드를 전개합니다. /// 인자로 주어진 문자열 리터럴이 유효하지 않다면 Error를 방출합니다. /// -/// 아래는 확장되기 전과 후의 코드를 보여줍니다. +/// 아래는 전개되기 전과 후의 코드를 보여줍니다. /// ```swift /// let apple = #URL("https://www.apple.com/kr") /// @@ -23,7 +23,7 @@ import Foundation /// // End expansion of "#URL" /// ``` /// -/// - Note: 코드가 확장되기 전에 확장 단계에서 인자로 주어진 문자열 리터럴이 유효한지 확인하기에 런타임 도중 Error를 방출하지 않습니다. +/// - Note: 매크로가 전개되기 전에 전개 단계에서 매개변수로 넘겨진 문자열 리터럴이 유효한지 확인하기에 런타임 경고가 발생할 여지를 차단합니다. /// - Author: 김소월 /// @freestanding(expression) diff --git a/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift b/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift index ddaf419..7d7f03d 100644 --- a/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift +++ b/Sources/Macros/Interface/MemberAttributeMacrosInterface.swift @@ -7,9 +7,9 @@ // MARK: - Deprecated MemberAttribute -/// 모든 멤버(프로퍼티, 함수 등)에 deprecated 기호를 붙입니다. +/// 모든 멤버(프로퍼티, 함수 등)에 deprecated 기호를 전개합니다. /// -/// 해당 매크로를 적용한 타입에 속한 모든 멤버(프로퍼티, 함수, 타입아일리어스 등)에 deprecated 기호를 붙입니다. +/// 이 매크로를 적용한 타입에 속한 모든 멤버(프로퍼티, 함수, 타입아일리어스 등)에 deprecated 기호를 붙입니다. /// /// - Author: 김소월 /// @@ -22,6 +22,34 @@ public macro Deprecated() = #externalMacro( // MARK: - Dependency Values Member Attribute + +/// @DependencyValue 매크로를 프로퍼티에 전개합니다. +/// +/// 이 매크로를 적용한 확장에 속한 모든 저장 프로퍼티에 @DependencyValue 매크로를 붙입니다. 확장에 속한 프로퍼티가 getter/setter 프로퍼티라면 속성을 붙이지 않습니다. +/// +/// 아래는 전개되기 전과 후의 코드를 보여줍니다. +/// ```swift +/// @DependencyValues +/// extension DependencyValues { +/// // Begin expansion of "@DependencyValues" +/// @DependencyValue(for: MeRepositoryKey.self) +/// // End expansion of "@DependencyValues" +/// var meRepository: MeRepositoryProtocol +/// } +/// ``` +/// +/// 이 매크로는 전개된 매크로를 적용한 프로퍼티에 영향을 받습니다. 매크로는 프로퍼티 이름(identifier)의 첫 글자를 대문자로 바꾸고, 접미사에 `Key` 키워드를 붙여 새로운 매크로의 매개변수에 메타 타입으로 넘겨줍니다. +/// +/// 위 예제를 보면 전개된 `@DependencyValue` 매크로의 매개변수로 `MeRepositoryKey.self`라는 메타 타입을 넘겨 줄 수. 있는 건 `meRepository`라는 프로퍼티 이름으로부터 추론했기 때문입니다. +/// +/// 그렇기 때문에 코드 작성자는 DepdendencyKey 프로토콜 준수 객체의 이름을 `MeRepositoryKey`처럼 일정한 규칙에 따라 작성해야 합니다. +/// +/// 부득이한 경우에는 직접 프로퍼티에 `@DependencyValue` 매크로를 적용해 다른 규칙을 적용할 수 있습니다. 이 매크로가 적용되어도 @DependencyValues 매크로는 여전히 사용가능합니다. 해당 매크로는 @DependencyValue 매크로가 붙은 프로퍼티를 무시합니다. +/// +/// - NOTE: 의존성 주입에 관한 내용은 [여기](https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies)를 참조하세요. +/// +/// - Warning: 이 매크로는 Extension에만 적용할 수 있습니다. +/// @attached(memberAttribute) public macro DependencyVales() = #externalMacro( module: "MacrosImplementation",