Bibi's DevLog ๐ค๐
Environment : ๊ฐ์ฒด์ ์์กด์ฑ ํ๊ฒฝ ๋ง๋ค๊ธฐ ๋ณธ๋ฌธ
Environment๋?
๊ฐ์ฒด๋ฅผ ์์ฑํ ๋, ๊ทธ ๊ฐ์ฒด๊ฐ ํ์๋ก ํ๋ ์์กด์ฑ์ ์ฃผ์
ํด ์ค๋ค.
์ด ๋ ํ์ํ ์์กด์ฑ์ด ๊ฐ์ฒด ์ ์ฒด๊ฐ ์๋๋ผ ๊ทธ ๊ฐ์ฒด์ ๋ฉ์๋ ์ผ๋ถ๋ผ๋ฉด, ๊ฐ์ฒด ์ ์ฒด๊ฐ ์๋๋ผ ๋ฉ์๋ ์ผ๋ถ๋ง์ ์ฃผ์
ํด ์ฃผ๋ ๊ฒ์ด ์ข์ ๊ฒ์ด๋ค.
๊ทธ๋์ 'ํน์ ๊ฐ์ฒด๊ฐ ํ์๋ก ํ๋ ํ๊ฒฝ' ์๋ฏธ๋ก ํด์ํ์ฌ Environment๋ผ๋ ์ด๋ฆ์ ์ดํดํด ๋ณด์๋ค.
์ฌ์ฉ ๋ฐฐ๊ฒฝ
Environment ์ฌ์ฉ ์ ์๋ ์์กด์ฑ ์ฃผ์ ์ ํ์ํ ์์๋ค์ด ๋ชจ๋ ๋ถ๋ฆฌ๋์ด ์๊ฑฐ๋, ๋ค๋ฅธ ๊ฐ์ฒด ๋ด์ ์์๋ก ์กด์ฌํด ๊ฐ์ฒด ์์ฑ ๊ณผ์ ์ด ๋งค์ฐ ๋ณต์กํ๋ค.
์๋ฅผ ๋ค์ด,
- ์ด์ ๋ชฉ๋ก๋ค์ ๋ณด์ฌ์ฃผ๋ IssueViewController์์ ํ์ํ ๋ฉ์๋๊ฐ IssueService์ ๋ฉ์๋ ์ค 1๊ฐ๋ฟ์ธ๋ฐ, ๊ทธ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด IssueModel์๊ฒ IssueService๊ฐ์ฒด ์ ์ฒด๋ฅผ ๋๊ธฐ๋ ์ํฉ ๋ฐ์
- ๊ฐ์ฒด ์์ฑ ๋ฐ ๊ด๋ฆฌ๋ฅผ ๋ด๋นํ๋ Container๊ฐ AppDelegate์๋ง ์กด์ฌํด, Container์ฌ์ฉ์ ์ํด AppDelegate๋ฅผ ์ค๋ธ์ ํธํํ์ฌ ์ ๊ทผํ๋ ์ํฉ ๋ฐ์
...์ ๊ฐ์ ํผ๋์ค๋ฌ์ด ์ํฉ์ด ์๊ฒผ๋ค.
1์ฐจ์ ์ผ๋ก ์๊ฐํ ํด๊ฒฐ๋ฐฉ์์ "IssueService์ ๋๋ฌด ๋ง์ ๋ฉ์๋๊ฐ ์์ด์ ๊ทธ๋ฌ๋, ๊ฐ ๋ทฐ ์ปจํธ๋กค๋ฌ์ ํ์ํ ๋ฉ์๋๋ค๋ก ์๋น์ค ๊ฐ์ฒด๋ฅผ ๋ ๋๋ ์ผ๊ฒ ๋ค" ๊ณ ์๊ฐํ๋ค.
ํ์ง๋ง ์๋น์ค๋ฅผ ๋๋๋ ๊ฒ ๋ง์ผ๋ก๋ ๋ ๋ฒ์งธ ์ํฉ์ด ํด๊ฒฐ๋์ง ์๋๋ค.
๋, "๋ชจ๋ธ์ด๋ผ๋ ๊ฐ์ฒด๊ฐ ํ์๋ก ํ๋ ๊ฒ์ IssueService๊ฐ ์๋๋ผ IssueService์ ์๋ ๋ฉ์๋ ์ผ๋ถ์ด๋ค. ๊ทธ๋ฌ๋ ๊ฐ์ฒด ์ ์ฒด๊ฐ ์๋๋ผ ๊ฐ์ฒด์ ํ์ํ ํ๋๋ง์ ์ ๋ฌํด ์ฃผ๋ฉด ๋์ง ์๊ฒ ๋?" ๋ผ๋ ๋ฆฌ๋ทฐ์ด๋์ ์กฐ์ธ์ด ์์๋ค.
๊ทธ๋์ Environment๋ฅผ ์ฌ์ฉํด ๊ฐ์ ํ๋ ๊ฒ์ ๋์ ํด ๋ณด์๋ค.
์ ์ฉ ๊ณผ์
Environment๋ ํน์ ๊ฐ์ฒด์ ํ์ํ ์์กด์ฑ์ ๊ด๋ฆฌํ๋ค.
๋๋ ๊ธฐ์กด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๋ถ๋ถ์ธ Container์ Model์ Environment๋ฅผ ์ ์ฉํ๋ค.
- ContainerEnvironment : Container์ ํ์ํ ํ๊ฒฝ ๊ด๋ฆฌ
- Container๋ ๊ฐ์ฒด๋ค์ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๊ฐ์ฒด์ด๋ฏ๋ก, ๊ฐ ๊ฐ์ฒด๋ค์ ์์ฑ์๊ฐ ํ์๋ก ํ๋ ์์๋ค์ ๋ชจ๋ ๋ฃ์ด ์ฃผ์ด์ผ ํ๋ค.
- ๋ฐ๋ผ์ UserDefaults, OAuthService, IssueService๋ผ๋ ๊ฐ์ฅ ๋ง์ ํ๋กํผํฐ๋ฅผ ๊ฐ๊ฒ ๋์๋ค.
- Model๋ค์ Environment : ํด๋น Model์ ํ์ํ Service์ ๋ฉ์๋ ์ผ๋ถ ๊ด๋ฆฌ
- ์๋ฅผ ๋ค์ด, ReposModelEnvironment๋ ReposModel์ ํ์ํ ํ๊ฒฝ(IssueService์ ๋ฉ์๋ ์ผ๋ถ)์ ๊ด๋ฆฌํ๋ค.
- ์ฌ์ฉํ๋ IssueService ๋ด์ ํจ์๋ง ํด๋ก์ ๋ก ๋ฐ๊ณ , ์ด Environment๋ฅผ ReposModel์ ์์ฑ์์ ์ฃผ์ ํด ์ค๋ค.
์ ์ฉ ๋ฐฉ๋ฒ
struct ReposModelEnvironment {
let requestRepos: (@escaping (Result<[Repository], IssueError>) -> Void) -> Void
}
- ์ด ๊ฐ์ฒด์ ํ์ํ ํ๊ฒฝ์ ํ์ธํ๋ค.
- ReposModel์ด ๊ธฐ์กด์ ์์ฑ์๋ก ๋ฐ์์ค๋ ์์๋ IssueService์๊ณ , IssueService์์ ์ฌ์ฉํ๋ ๋ฉ์๋๋ requestRepos() ๋ฟ์ด๋ค.
๊ฐ์ฒด์ด๋ฆEnvironment
๊ตฌ์กฐ์ฒด๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ๊ฐ์ฒด๊ฐ ํ์๋ก ํ๋ ํ๊ฒฝ์ ํ๋กํผํฐ๋ก ์ ์ํ๋ค.- ๊ฐ์ฒด๊ฐ ํ์๋ก ํ๋ ๊ฒ์ด ๋ฉ์๋์ธ ๊ฒฝ์ฐ์๋ ํด๋ก์ ๋ก ์ ์ํ๋ค.
- ์๋ฅผ ๋ค์ด ReposModel์์ ์ฌ์ฉํ๋ IssueService์ requestRepos()์ ๊ฒฝ์ฐ, ๊ธฐ์กด service.requestRepos()ํ๋ ๋ฉ์๋์ ์๊ทธ๋์ฒ๋ ์๋์ ๊ฐ๋ค.
c. ReposModel์ ํ๊ฒฝ์ ์ด ๋ฉ์๋๋ฅผ ๋ด์์ผ ํ๋ค. ๋ฐ๋ผ์ ์ด ๋ฉ์๋๋ฅผ ๋ฐ๊ณ ๋ฆฌํดํ์ ์ ์๋func requestRepos(completion: @escaping (Result<[Repository], IssueError>) -> Void) { // ... }
(@escaping ((Result<[Repository], IssueError>)) -> Void) -> Void
ํ์ ์ด ๋๋ค.- ๊ฐ์ฒด๊ฐ ๊ธฐ์กด์ ๋ฐ๋ ์์กด์ฑ ๋์ , Environment๋ฅผ ๋ฃ๋๋ค.
- ์ด์ ReposModel์ ์์ฑํ ๋ service ๋์ Environment๋ฅผ ๋ฃ์ด์ค๋ค.
let environment = ReposModelEnvironment(requestRepos: { [weak self] completion in // ํ์๋ก ํ๋ ๋ฉ์๋๋ฅผ completionHandler๋ก ์ ๋ฌ self?.container.environment.issueService.requestRepos(completion: { result in completion(result) }) }) let model = ReposModel(environment: environment) let viewController = ReposViewController(model: model)
์ ํ ๋น๊ต
Before (Model)
import Foundation
class LoginModel {
private let service: OAuthService
init(service: OAuthService) {
self.service = service
}
func requestCode(completion: @escaping (Result<URL, OAuthError>) -> Void) {
service.requestCode { result in
completion(result)
}
}
}
๋ชจ๋ธ์ด ํ์๋ก ํ๋ service์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด, ์์ฑ์์์ service๋ฅผ ์ง์ ๋ฐ๊ณ ์๋ค.
After (Model)
import Foundation
class LoginModel {
private let environment: LoginModelEnvironment
init(environment: LoginModelEnvironment) {
self.environment = environment
}
func requestCode(completion: @escaping (Result<URL, OAuthError>) -> Void) {
environment.requestCode { result in
completion(result)
}
}
}
struct LoginModelEnvironment {
let requestCode: (@escaping (Result<URL, OAuthError>) -> Void) -> Void
}
๋ชจ๋ธ์ด ํ์๋ก ํ๋ service์ ํน์ ๋ฉ์๋๋ฅผ LoginModelEnvironment๋ผ๋ ๊ตฌ์กฐ์ฒด์ ๋ฐ๋ก ์ ์ํ๋ค.
๋ชจ๋ธ์ ์์ฑ์์์ ์ด Environment๋ฅผ ๋ฐ๋๋ค.
์ด์ ๋ชจ๋ธ์ ์์ฑํ ๋ OAuthService์ ์ฒด๊ฐ ์๋ LoginModelEnvironment๋ฅผ ์์ฑํ๊ณ ,
LoginModelEnvironment์ ๋ชจ๋ธ์ด ํ์๋ก ํ๋ OAuthService์ ๋ฉ์๋๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
Before (Container)
class Container {
private var accessToken: String?
init(token: String?) {
self.accessToken = token
}
func buildViewController(_ screen: Screen) -> UIViewController {
let service = IssueService(token: self.accessToken ?? "")
switch screen {
case .login:
return LoginViewController(service: OAuthService())
case .issue(let selectedRepo):
let model = IssueModel(service: service, repo: selectedRepo)
let viewController = IssueViewController(model: model, repo: selectedRepo)
viewController.title = "Issues"
return viewController
case .repos:
let model = ReposModel(service: service)
let viewController = ReposViewController(model: model)
viewController.title = "Repos"
return UINavigationController(rootViewController: viewController)
case .newIssue(let repo):
let model = NewIssueModel(service: service)
return NewIssueViewController(repo: repo, model: model)
case .optionSelect(let option, let repo):
let model = OptionSelectModel(service: service)
return OptionSelectViewController(model: model, option: option, repo: repo)
}
}
}
์ปจํ ์ด๋๊ฐ ๊ฐ์ฒด๋ค์ ๋ง๋ค ๋ ํ์๋ก ํ๋ ์ฌ๋ฌ ์์๋ค์ด ํ๋กํผํฐ๋ก ์์ด, ๊ทธ๋๊ทธ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ๋ง๋ค์ด ์ฌ์ฉํ๊ณ ์๋ค. ๋, ๋ทฐ ์ปจํธ๋กค๋ฌ๋ง๋ค ํ์๋ก ํ๋ ์๋น์ค๋ฅผ ํต์งธ๋ก ์ ๋ฌํ๊ณ ์๋ค.
After (Container)
class Container {
let environment: ContainerEnvironment
private var registeredObjects: [String: Any] = [:]
private var registeredViewControllerCoordinator: [UIViewController : Coordinator] = [:]
init(environment: ContainerEnvironment) {
self.environment = environment
}
// .......
}
struct ContainerEnvironment {
var githubUserDefaults: GithubUserDefaults
var oAuthService: OAuthService
var issueService: IssueService
static let live: ContainerEnvironment = {
let githubUserDefaults = GithubUserDefaults()
guard let token = githubUserDefaults.getToken() else {
return ContainerEnvironment(githubUserDefaults: githubUserDefaults, oAuthService: OAuthService(), issueService: IssueService())
}
return ContainerEnvironment(githubUserDefaults: githubUserDefaults, oAuthService: OAuthService(), issueService: IssueService(token: token))
}()
}
์ปจํ
์ด๋๊ฐ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ ํ์๋ก ํ๋ ์์๋ค์ Environment๋ก ์ ๊ณตํด ์ฃผ๊ณ ์๊ธฐ ๋๋ฌธ์, ๋์ผํ ๊ฐ์ฒด๋ฅผ ์ฌ๋ฌ ๋ฒ ๋ง๋ค ํ์๊ฐ ์์ด์ก๋ค. ๋ทฐ ์ปจํธ๋กค๋ฌ๊ฐ ํ์๋ก ํ๋ ๋ฉ์๋๋ ContainerEnvironment.issueService.๋ฉ์๋
์ ๊ฐ์ด ์ ๊ทผํด ๋ฐ๋ก ๋๊ฒจ์ค ์ ์๋ค.
๋๋ ์
- ์ฐ์ ์์ฑ์๋ฅผ ํตํด ๋ถํ์ํ๊ฒ ํฐ ๊ฐ์ฒด๊ฐ ์ค๊ณ ๊ฐ์ง ์๋ ์ ์ด ๊ฐ์ฅ ํฐ ์ฅ์ ์ธ ๊ฒ ๊ฐ๋ค.
- “๊ฐ์ฒด๊ฐ ์ ๋ง ํ์ํ ๊ฒ์ด ๋ฌด์์ธ์ง ์๊ฐํด ๋ณด๊ณ , ๊ผญ ํ์ํ ๊ฒ๋ง ๋ฃ์ด ์ฃผ๊ธฐ” ๋ฅผ ๋ช ์ฌํด์ผ๊ฒ ๋ค.
- ํจ์๋ฅผ ๋ณ์์ฒ๋ผ ์ฌ์ฉํ๋ ์ฐ์ต์ ์ง์ ์ ์ผ๋ก ํ ์ ์์ด์ ์ข์๋ค.
'๐ฑ๐ iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[iOS] Advanced coordinators in iOS ํด์ ๋ฐ ์ ๋ฆฌ (0) | 2022.11.25 |
---|---|
[iOS] How to use the coordinator pattern in iOS apps ํด์ ๋ฐ ์ ๋ฆฌ (0) | 2022.11.25 |
[๊ณฐํ๊น๋] RxSwift + MVVM ์ ๋ฆฌ (0) | 2022.06.06 |
UILabel ํ ์คํธ์ ์ผ๋ถ ํฐํธ/์์/ํฌ๊ธฐ ๋ณ๊ฒฝํ๊ธฐ, ์ทจ์์ ๊ธ๊ธฐ - attributedText, NSMutableAttributedString (0) | 2022.05.14 |
์ฝ๋๋ก UICollectionView ๋ง๋ค๊ธฐ (0) | 2022.05.12 |