플라워로드 기술 블로그 : http://blog.flowerroad.ai
Notion Link : https://flyingcorp.notion.site/iOS-Dependency-Inject-with-SwInject-4cd49754024e46939eeed7cbcebeeba6
개요
Dependency Injection?
Clean Architecture에서도 자주 언급되는 용어이고, Software Engineering 에서도 자주 언급되는 단어 입니다. 직역을 하자면 의존성 주입이라고 하는데 사실 직역을 했을때 용어를 보고 어떤 의미인지 이해 하기에는 조금 힘든면이 있습니다.
해당 포스팅은 DI에 대한 내용이 아니기 때문에 간략하게 설명하자면 객체간의 의존관계를 줄여서 수정에 대한 유연성을 높이고, 확장성을 향상시킬수 있는 장점이 있습니다. 여기에 또하나의 장점으로는 unit test를 용이하게 해주는 역할도 있습니다.
조금 더 어렵게 말하면, 사용하려고 하는 하나의 객체(1)를 초기화 할 때에 해당 객체 내에서 사용하는 다른 객체(2,3,4...)들을 객체 내에서 생성하지 않고 외부에서 객체(2,3,4...)를 생성해서 사용하려고 하는 객채(1)에게 넘겨 줌으로서 사용 되는 객체(2,3,4,...)의 코드가 추후에 변경이 되더라도 기존 객체(1)는 다시 빌드 하지 않고도 사용할 수 있다 라고 설명할 수 있네요...뭐..그냥 이렇다고 하는 겁니다...
Swinject
사실 Android에는 google에서 공식적으로 추천을 하는 DI module이 있습니다. 다들 들어보셨을것 같은 dagger2나 Hilt가 그것들이죠.
오늘 살펴볼 Swinject는 Swift에서 사용되는 DI module인데 사실 dagger2에 비하면 아주 사용법도 쉽고 기능도 없습니다(?!).
Swift에서는 사실 custom 으로 DI를 simple하게 만든다고 해도 시간이 많이 들어가지 않고 개발자 마다 혹은 개발 단체 마다 필요한 내용만 구현해서 사용할 수 있기 때문에 Swinject와 같은 module이 많이 사용 되지 않는것 같습니다. 필자도 사실 DI를 관리하는 class/module을 simple하게 만들어서 사용을 하다가 갑작스러운 변덕으로 swinject를 사용하게 되었습니다.
github 상에 기본적인 사용예제가 있고, 이를 거의 그대로 번역해서 올려둔 블로그도 쉽게 찾을 수 있기 때문에 여기서는 저의 주관(?)적인 사용법만 간단하게 살펴 보고, Scope에 대해서 정리하겠습니다.
Basic Usage
기본적인 사용법은 아래 Ref의 github의 readmi를 보시면 예제와함께 잘 나와 있어서 여기서는 크게 언급 하지는 않고 간략하게 제가만든 예시를 첨부해서 간략하게 보겠습니다.
final class DIContainer {
static let shared = DIContainer()
var container: Container = Container()
private init(){}
func initContainer() -> () {
self.container.register(AuthService.self){ _ in
AmplifyAuthService()
}
self.container.register(TestViewServiceProvider.self){ resolver in
TestViewServiceProviderImpl(authService: resolver.resolve(AuthService.self)!)
}
self.container.register(TestViewReactor.self){ resolver in
TestViewReactor(serviceProvider: resolver.resolve(TestViewServiceProvider.self)!)
}
}
}
네. 맞습니다. DI를 관리하는 class이고 static shared를 이용해서 공용으로 사용하실 수 있고, 초기화는 AppDelegate 혹은 SceneDelegate에서 앱 launch 직후 초기화를 하면 이후 자유롭게 사용할 수 있습니다.
기본적으로 Container라는 instance를 관리,저장하는 instance를 만들어서 DI를 관리할 수 있습니다.
초기화 하는 시점에 register method를 이용해서 이후 사용하려고 하는 class instance를 등록해 둘 수 있고,
간단하게 register를 이용해서 사용하려고 하는 instance를 등록 resove를 이용해서 등록되어 있는 instance를 신규 생성/할당 혹은 기존 생성되어 있는 instance를 재사용 할 수 있습니다.
위의 초기화 과정을 거치고 난 후 타 class내에서 등록된 instance를 사용할 때에는 아래와 같은 방법으로 사용할 수 있습니다.
//
var testServiceProvider: TestViewServiceProvider? = DIContainer.shared.container.resolve(TestViewServiceProvider.self)
Scope
DI에서 중요한 것은 등록되어 있는 혹은 만들어진 instance 를 어떻게 유지, 재사용, 삭제 하느냐가 주요한 내용입니다. 예를 들어, repository layer의 경우 앱이 실행 될때 하나만 생성되는, 즉 앱의 전체 life cycle과 동일한 주기를 가지는 singleton instance라고 할때에 하나의 instance만 생성해서 여러곳에서 사용될 수 있도록 scope를 설정해 주어야 합니다.
Graph - default
가장 기본적인 scope이고 register할때에 scope에 대한 설정을 하지 않은 경우에도 기본적으로 설정되는 scope입니다.
resolve를 이용해서 instance를 요청 할때에 항상 새로운 instance를 만들어서 전달합니다. 단, instance등록하는 factory closure내에서 호출 되는 경우 하나의 instance를 공유하게 됩니다.
self.container.register(TestViewServiceProvider.self){ resolver in
TestViewServiceProviderImpl(authService: resolver.resolve(AuthService.self)!)
MainViewServiceProviderImpl(authService: resolver.resolve(AuthService.self)!)
}
Transient
이름에서 유추 하실 수 있듯이 instance를 공하지 않고 resolve호출 시 항상 새로운 instance를 생성해서 전달합니다.
self.container.register(AuthService.self){ _ in
AmplifyAuthService()
}.inObjectScope(.transient)
Container
instance를 현재 containe내에서 항상 공유합니다. 이때 child containers가 있다면 child containers들도 해당 instance를 공유하게 됩니다. Design pattern의 singleton을 구현할때에 사용할 수 있습니다.
self.container.register(AuthService.self){ _ in
AmplifyAuthService()
}.inObjectScope(.container)
Weak
Container와 비슷한 속성으로서 instance를 현재 container와 child containers들에게서 공유하게 됩니다. 다만 해당 instance에 strong reference가 있을때에만 유지되고 strong references가 종료되어 사라지는 시점에 해당 instance의 공유도 종료되고, 이후 resolve시 새로운 instance를 생성해서 전달 합니다.
Custom
프로젝트를 진행하다보면 위의 경우가 아닌 때에 상황에 맞는 scope를 설정해야 하는 경우가 있습니다. 이때 원하는 scope를 설정해서 사용할 수 있습니다.
scope의 정의는 extension을 이용해서 아래와 같이 정의 할 수 있고, .custom으로 사용할 수 있습니다.
extension ObjectScope {
static let custom = ObjectScope(storageFactory: PermanentStorage.init)
}
Ref
'개발 > ios' 카테고리의 다른 글
[iOS] RxSwift + Reactorkit 을 사용해서 프로젝트를 구성해보자 - Project Template (0) | 2022.02.27 |
---|---|
[iOS] UI Test 를 자동화 해보자. - Basic 사용법 (0) | 2022.02.20 |