Skip to content

Commit

Permalink
feat: WrapperView 매크로 구현 (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
rlarjsdn3 committed Jun 2, 2024
1 parent f059f82 commit 4053288
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 27 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
- [x] [@CodableKey(_:)]()
- [x] [@Deprecated]()
- [x] [@DependencyValue(for:)]()
- [x] [@DependencyValues]()
- [x] [@DependencyOrganizer]()
- [x] [@Wrapper]()
- [x] (@WrapperView)

### Libraries

Expand Down
12 changes: 7 additions & 5 deletions Sources/Macros/Implementation/Complex/WrapperMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,29 @@ extension WrapperMacro: MemberMacro {
throw MacroError.message("매크로 선언으로 전달된 Generic 타입이 유효하지 않습니다.")
}

let scope = declaration.getAccessControl()

return [
"""
public typealias R = \(firstGenericType)
\(scope)typealias R = \(firstGenericType)
""",
"""
public typealias V = \(lastGenericType)
\(scope)typealias V = \(lastGenericType)
""",

"""
public func makeViewController() -> V {
\(scope)func makeViewController() -> V {
return \(lastGenericType)(reactor: makeReactor())
}
""",

"""
public var viewController: V {
\(scope)var viewController: V {
return makeViewController()
}
""",
"""
public var reactor: R {
\(scope)var reactor: R {
return makeReactor()
}
"""
Expand Down
88 changes: 88 additions & 0 deletions Sources/Macros/Implementation/Complex/WrapperViewMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// File.swift
//
//
// Created by 김건우 on 6/3/24.
//

import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct WrapperViewMacro { }

extension WrapperViewMacro: 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 타입이 유효하지 않습니다.")
}

let scope = declaration.getAccessControl()

return [
"""
\(scope)typealias R = \(firstGenericType)
""",
"""
\(scope)typealias V = \(lastGenericType)
""",

"""
\(scope)func makeView() -> V {
return \(lastGenericType)(reactor: makeReactor())
}
""",

"""
\(scope)var view: V {
return makeView()
}
""",
"""
\(scope)var reactor: R {
return makeReactor()
}
"""
]
}

}

extension WrapperViewMacro: 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]
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public struct DependencyOrganizerMacro: MemberAttributeMacro {
}

if !varDecl.attributes.isAttributeApplied("DependencyValue") {
let capitalizedIdentifier = identifier.capitalizeFirstLetter()
let capitalizedIdentifier = identifier.makeFirstLetterLowercase()

return [
"""
Expand Down
1 change: 1 addition & 0 deletions Sources/Macros/Implementation/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct Bibbi_MacroPlugin: CompilerPlugin {
CodableKeyMacro.self,
CodableMacro.self,
WrapperMacro.self,
WrapperViewMacro.self,
URLMacro.self,
DependencyOrganizerMacro.self,
DeprecatedMacro.self
Expand Down
63 changes: 63 additions & 0 deletions Sources/Macros/Interface/ComplexMacorsInterfae.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ public macro CodableKey(name: String) = #externalMacro(
/// func makeReactor() -> R
/// }
/// ```
/// 이 매크로는 첨부된 선언의 접근 제어자에 따라 전개되는 멤버의 접근 제어자가 바뀝니다. 예를 들어, 첨부 Class가 public 접근 제어자를 가진다면 전개되는 멤버도 public 접근 제어자를 가집니다.
///
/// - Warning: 이 매크로는 Class에만 적용할 수 있습니다.
///
Expand All @@ -141,3 +142,65 @@ public protocol BaseWrapper {



// MARK: - Wrapper View Complex


/// 타입이 BaseWrapper 프로토콜을 준수하게 합니다.
///
/// 이 매크로를 적용한 타입에 BaseWrapper 프로토콜을 준수하게 하고 make() 메서드 및 view, reactor 계산 프로퍼티를 추가합니다.
///
/// 첫 번째 Generic 타입은 Reactor 프로토콜을 준수하는 타입이어야 하고, 두 번째 Generic 타입은 ReactorKit.View 프로토콜을 준수하는 타입이어야 합니다.
///
/// 아래는 전개되기 전과 후의 코드를 보여줍니다.
/// ```swift
/// @Wrapper<RankingReactor, RankingView>
/// public class HomeViewControllerWrapper {
/// public func makeReactor() -> R {
/// return HomeReactor()
/// }
///
/// // Begin expansion of "@WrapperView"
/// public typealias R = RankingReactor
/// public typealias V = RankingView
///
/// public func makeView() -> V {
/// return RankingView(reactor: makeReactor())
/// }
///
/// public var view: V {
/// return makeView()
/// }
///
/// public var reactor: R {
/// return makeReactor()
/// }
/// // End expansion of "@WrapperView"
/// }
/// // Begin expansion of "@WrapperView"
/// extension HomeViewControllerWrapper: BaseWrapper { }
/// // End expansion of "@WrapperView"
/// ```
///
/// - 참고: BaseWrapper 프로토콜은 아래와 같이 선언되어 있습니다.
/// ```swift
/// public protocol BaseWrapper {
/// associatedtype R: Reactor
/// associatedtype V: ReactorKit.View
///
/// func makeReactor() -> R
/// }
/// ```
/// 이 매크로는 첨부된 선언의 접근 제어자에 따라 전개되는 멤버의 접근 제어자가 바뀝니다. 예를 들어, 첨부 Class가 public 접근 제어자를 가진다면 전개되는 멤버도 public 접근 제어자를 가집니다.
///
/// - Warning: 이 매크로는 Class에만 적용할 수 있습니다.
///
/// - Author: 김소월
///
@attached(member, names: arbitrary)
@attached(extension, conformances: BaseWrapper)
public macro WrapperView<R, V>() = #externalMacro(
module: "MacrosImplementation",
type: "WrapperViewMacro"
)


Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public macro Deprecated() = #externalMacro(
)


// MARK: - Dependency Values Member Attribute
// MARK: - Dependency Organizer MemberAttribute


/// @DependencyValue 매크로를 프로퍼티에 전개합니다.
Expand Down
88 changes: 71 additions & 17 deletions Sources/Macros/Playground/ComplexMacrosPlayground.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import MacrosInterface
// MARK: - Codable Complex

@Codable
public struct MemberDTO {
struct MemberDTO {
var name: String
@CodableKey(name: "day_of_birth") var dayOfBirth: String
var age: Int
Expand All @@ -25,49 +25,102 @@ public struct MemberDTO {
import UIKit
import ReactorKit

public class SomeReactor: Reactor {
public typealias Action = NoAction
class HomeReactor {
typealias Action = NoAction

public struct State { }
struct State { }

public var initialState: State = State()
var initialState: State = State()
}

public class SomeViewController: UIViewController, ReactorKit.View {
public typealias Reactor = SomeReactor
public var disposeBag = DisposeBag()
extension HomeReactor: Reactor { }

extension HomeReactor: Identifiable {
public var id: UUID { UUID() }
}

extension HomeReactor: Equatable {
static func == (
lhs: HomeReactor,
rhs: HomeReactor
) -> Bool {
return lhs.id == rhs.id
}
}

class HomeViewController: UIViewController, ReactorKit.View {
typealias Reactor = HomeReactor
var disposeBag = DisposeBag()

public convenience init(reactor: Reactor) {
convenience init(reactor: Reactor) {
self.init()
self.reactor = reactor
}

public override func viewDidLoad() {
override func viewDidLoad() {
super.viewDidLoad()
}

public func bind(reactor: SomeReactor) { }
func bind(reactor: HomeReactor) { }
}

@Wrapper<SomeReactor, SomeViewController>
public class SomeViewControlllerWrapper {
@Wrapper<HomeReactor, HomeViewController>
class HomeViewControllerWrapper {

public func makeReactor() -> R {
return SomeReactor()
func makeReactor() -> R {
return HomeReactor()
}

}


func runWrapperMacro() {
let someViewController = SomeViewControlllerWrapper().viewController
print("SomeViewController: \(someViewController)")
let vc = HomeViewControllerWrapper().viewController
print("HomeViewController: \(vc)")
}

#endif




// MARK: - Wrapper View Complex

#if canImport(UIKit)

import UIKit

class HomeView: UIView, ReactorKit.View {
typealias Reactor = HomeReactor
var disposeBag = DisposeBag()

convenience init(reactor: Reactor) {
self.init()
self.reactor = reactor
}

func bind(reactor: HomeReactor) { }
}

@WrapperView<HomeReactor, HomeView>
class HomeViewWrapper {

func makeReactor() -> R {
return HomeReactor()
}

}

func runWrapperViewMacro() {
let v = HomeViewWrapper().view
print("HomeView: \(v)")
}



#endif


func runComplexMacorsPlayground() {

let jsonString = """
Expand All @@ -86,6 +139,7 @@ func runComplexMacorsPlayground() {
#if canImport(UIKit)

runWrapperMacro()
runWrapperViewMacro()

#endif

Expand Down
Loading

0 comments on commit 4053288

Please sign in to comment.