Skip to content

Commit 94e9319

Browse files
committed
add post
1 parent b1ef7e4 commit 94e9319

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
---
2+
layout: post
3+
title: "[Swift 5.10] Nested Protocol With RIBs"
4+
tags: [Swift, Protocol, RIBs]
5+
---
6+
{% include JB/setup %}
7+
8+
Swift에서는 Protocol을 제외한 대부분의 타입은 타입 내부에서 다른 타입을 정의할 수 있었습니다.
9+
10+
```swift
11+
struct Parent {
12+
class ChildClass {} //
13+
struct ChildStruct {} //
14+
enum ChildEnum {} //
15+
protocol ChildProtocol {} //
16+
}
17+
```
18+
19+
이로 인해 Protocol 이름은 길어질 수밖에 없었습니다.
20+
21+
```swift
22+
protocol ParentChildProtocol {}
23+
```
24+
25+
이는 Swift 5.10 이전까지 Protocol을 사용하던 방식이었습니다. 그러나 Swift 5.10부터는 중첩 프로토콜을 사용할 수 있게 되었습니다. [SE-0404 Allow Protocols to be Nested in Non-Generic Contexts](https://github.com/apple/swift-evolution/blob/main/proposals/0404-nested-protocols.md) 덕분입니다.
26+
27+
이제 Struct, Class, Enum과 같이 Protocol도 타입 내부에서 정의할 수 있게 되었습니다. 중첩 프로토콜을 사용하면 타입의 구조를 더욱 조직화하고 캡슐화할 수 있습니다.
28+
29+
```swift
30+
// FileName : RootInterface.swift
31+
enum Root {}
32+
33+
extension Root {
34+
protocol Serviceable {
35+
func doSomething()
36+
}
37+
38+
protocol Interactable {
39+
var service: Serviceable { get }
40+
func doSomething()
41+
}
42+
}
43+
44+
// FileName : RootImplement.swift
45+
extension Root {
46+
struct Service: Serviceable {
47+
func doSomething() {
48+
print("Service did something")
49+
}
50+
}
51+
52+
struct Interactor: Interactable {
53+
let service: any Serviceable
54+
init(service: some Serviceable) {
55+
self.service = service
56+
}
57+
58+
func doSomething() {
59+
service.doSomething()
60+
}
61+
}
62+
}
63+
64+
// 중첩 프로토콜과 구현 타입을 사용하는 예시
65+
let service = Root.Service()
66+
let interactor = Root.Interactor(service: service)
67+
interactor.doSomething() // "Service did something" 출력
68+
```
69+
70+
## RIBs
71+
72+
RIBs 아키텍처는 Router, Interactor, Builder, Presenter 등으로 구성됩니다. 각 구성 요소는 Protocol로 추상화합니다.
73+
74+
```swift
75+
/// FileName : LoggedInBuilder.swift
76+
protocol LoggedInDependency: Dependency {}
77+
protocol LoggedInBuildable: Buildable {
78+
func build(withListener listener: LoggedInListener) -> LoggedInRouting
79+
}
80+
81+
/// FileName : LoggedInteractor.swift
82+
protocol LoggedInRouting: ViewableRouting {}
83+
protocol LoggedInPresentable: Presentable {
84+
var listener: LoggedInPresentableListener? { get set }
85+
}
86+
protocol LoggedInListener: AnyObject {}
87+
88+
/// FileName : LoggedRouter.swift
89+
protocol LoggedInInteractable: Interactable {
90+
var router: LoggedInRouting? { get set }
91+
var listener: LoggedInListener? { get set }
92+
}
93+
protocol LoggedInViewControllable: ViewControllable {}
94+
95+
/// FileName : LoggedViewController.swift
96+
protocol LoggedInPresentableListener {}
97+
```
98+
99+
중첩 프로토콜이 도입되기 전에는 위와 같이 Protocol 이름이 길어질 수밖에 없었습니다. 하지만, 중첩 프로토콜을 사용한다면 네임스페이스 역할을 해주는 타입 아래에 코드를 조직화할 수 있게 되었습니다.
100+
101+
다음은 LoggedInRIB에 중첩 프로토콜을 적용한 코드입니다.
102+
103+
```swift
104+
/// FileName: LoggedIn.swift
105+
enum LoggedIn {}
106+
107+
/// FileName: LoggedIn+Builder.swift
108+
extension LoggedIn {
109+
protocol Dependency: RIBs.Dependency {}
110+
111+
protocol Buildable: RIBs.Buildable {
112+
func build(withListener listener: Listener) -> RIBs.Routing
113+
}
114+
}
115+
116+
extension LoggedIn {
117+
final class Component: RIBs.Component<Dependency> {}
118+
119+
final class Builder: RIBs.Builder<Dependency>, Buildable {
120+
override init(dependency: Dependency) {
121+
super.init(dependency: dependency)
122+
}
123+
124+
func build(withListener listener: Listener) -> RIBs.Routing {
125+
let component = Component(dependency: dependency)
126+
let viewController = ViewController()
127+
let interactor = Interactor(presenter: viewController)
128+
interactor.listener = listener
129+
return Router(interactor: interactor, viewController: viewController)
130+
}
131+
}
132+
}
133+
134+
/// FileName: LoggedIn+Interactor.swift
135+
extension LoggedIn {
136+
protocol Routing: RIBs.ViewableRouting {}
137+
protocol Presentable: RIBs.Presentable {
138+
var listener: PresentableListener? { get set }
139+
}
140+
141+
protocol Listener: AnyObject {}
142+
}
143+
144+
extension LoggedIn {
145+
final class Interactor: PresentableInteractor<Presentable>, Interactable, PresentableListener {
146+
weak var router: Routing?
147+
weak var listener: Listener?
148+
// TODO: Add additional dependencies to constructor. Do not perform any logic
149+
// in constructor.
150+
override init(presenter: Presentable) {
151+
super.init(presenter: presenter)
152+
presenter.listener = self
153+
}
154+
155+
override func didBecomeActive() {
156+
super.didBecomeActive()
157+
// TODO: Implement business logic here.
158+
}
159+
160+
override func willResignActive() {
161+
super.willResignActive()
162+
// TODO: Pause any business logic.
163+
}
164+
}
165+
}
166+
167+
/// FileName : LoggedIn+Router.swift
168+
extension LoggedIn {
169+
protocol Interactable: RIBs.Interactable {
170+
var router: Routing? { get set }
171+
var listener: Listener? { get set }
172+
}
173+
174+
protocol ViewControllable: RIBs.ViewControllable {}
175+
}
176+
177+
extension LoggedIn {
178+
final class Router: ViewableRouter<Interactable, ViewControllable>, Routing {
179+
// TODO: Constructor inject child builder protocols to allow building children.
180+
override init(interactor: Interactable, viewController: ViewControllable) {
181+
super.init(interactor: interactor, viewController: viewController)
182+
interactor.router = self
183+
}
184+
}
185+
}
186+
187+
/// FileName : LoggedIn+ViewController.swift
188+
extension LoggedIn {
189+
protocol PresentableListener: AnyObject {}
190+
}
191+
192+
extension LoggedIn {
193+
final class ViewController: UIViewController, Presentable, ViewControllable {
194+
weak var listener: PresentableListener?
195+
}
196+
}
197+
```
198+
199+
## 정리
200+
201+
* Swift 5.10 이전에는 프로토콜은 네임스페이스 기능을 제공하지 못했으나, Swift 5.10 이후부터는 중첩 프로토콜을 지원하여 코드를 더욱 구조화 및 캡슐화할 수 있게 되었습니다.
202+
203+
## 참고자료
204+
205+
* Apple
206+
* [SE-0404: Nested Protocols](https://github.com/apple/swift-evolution/blob/main/proposals/0404-nested-protocols.md)
207+
* [Swift Forum - Pitch: Allow Protocols to be Nested in Non-Generic Contexts](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)
208+
* [[iOS - swift] nested protocol 개념 (#Swift 5.10)](https://ios-development.tistory.com/1623)

0 commit comments

Comments
 (0)