# DITranquillity
Спокойствие это простая, но мощная библиотека на языке swift для [внедрения зависимостей](https://ru.wikipedia.org/wiki/Внедрение_зависимости).
Название "Спокойствие" выбрано не случайно - оно закладывает три базовых принципа библиотеки: понятность, простота и безопасность.
Оно говорит - используйте библиотеку и вы будете спокойны за свои зависимости.
> Сменить язык: [English](README.md), [Russian](README_ru.md)
## Что такое внедрение зависимостей?
[Внедрение зависимостей (DI)](https://ru.wikipedia.org/wiki/Внедрение_зависимости) это паттерн проектирования при котором некто поставляет зависимости в объект.
Является специфичной формой [принципа инверсии управления (IoC)](https://ru.wikipedia.org/wiki/Инверсия_управления) и помощником для [принципа инверсии зависимостей](https://ru.wikipedia.org/wiki/Принцип_инверсии_зависимостей).
Более подробно об этом можно [почитать по ссылке](Documentation/ru/about_dependency_injection.md)
И советую ознакомиться со [словарем](Documentation/ru/glossary.md) который поможет лучше ориентироваться в терминах.
## Возможности
#### Ядро
- [x] [Регистрация компонент и сервисов](Documentation/ru/core/registration_and_service.md)
- [x] [Внедрение через инициализацию, свойства, методы](Documentation/ru/core/injection.md)
- [x] [Опциональное внедрение, а также с аргументами, множественное, с указанием тэга/имени](Documentation/ru/core/modificated_injection.md)
- [x] [Отложенное внедрение](Documentation/ru/core/delayed_injection.md)
- [x] [Внедрение циклических зависимостей](Documentation/ru/core/injection.md#Внедрение-через-свойства)
- [x] [Указание времени жизни](Documentation/ru/core/scope_and_lifetime.md)
- [x] [Поддержка модульности](Documentation/ru/core/modular.md)
- [x] [Полное и подробное логирование](Documentation/ru/core/logs.md)
- [x] Одновременная работа из нескольких потоков
- [x] [Иерархичные контейнеры](Documentation/ru/core/container_hierarchy.md)
#### UI
- [x] [Поддержка сторибоардов](Documentation/ru/ui/storyboard.md)
- [x] [Внедрение в subviews и ячейки](Documentation/ru/ui/view_injection.md)
#### Graph API
- [x] [Получение графа зависимостей](Documentation/ru/graph/get_graph.md)
- [x] [Валидация графа зависимостей](Documentation/ru/graph/graph_validation.md)
- [ ] [Визуализация графа зависимостей](Documentation/ru/graph/visualization_graph.md)
## Установка
Библиотека поддерживает три популярных пакетных менеджера: Cocoapods, Carthage, SwiftPM.
#### CocoaPods
Добавьте строчку в ваш `Podfile`:
```
pod 'DITranquillity'
```
#### SwiftPM
Вы можете воспользуйтесь "Xcode/File/Swift Packages/Add Package Dependency..." и указать в качестве url:
```
https://github.com/ivlevAstef/DITranquillity
```
Или прописать в вашем `Package.swift` файле в секции `dependencies`:
```Swift
.package(url: "https://github.com/ivlevAstef/DITranquillity.git", from: "4.5.2")
```
И не забудьте указать в таргете в аргументе `dependencies` зависимость на библиотеку:
```Swift
.product(name: "DITranquillity")
```
> Важно! - SwiftPM не поддерживает фичи из секции UI.
#### Carthage
Добавьте строчку в ваш `Cartfile`:
```
github "ivlevAstef/DITranquillity"
```
Carthage поддерживает работу со сторибоардами графом и прямое внедрение, без дополнительных действий.
## Использование
Библиотека использует декларативный стиль описания зависимостей, и позволяет отделить ваш прикладной код от кода описания зависимостей.
Для быстрого входа давайте рассмотрим пример кода одного упрощенного VIPER экрана:
```Swift
.................................................
/// Описание зависимостей
let container = DIContainer()
container.register(LoginRouter.init)
container.register(LoginPresenterImpl.init)
.as(LoginPresenter.self)
.lifetime(.objectGraph)
container.register(LoginViewController.init)
.injection(cycle: true, \.presenter)
.as(LoginView.self)
.lifetime(.objectGraph)
container.register(AuthInteractorImpl.init)
.as(AuthInteractor.self)
.................................................
/// Место запуска приложения
let router: LoginRouter = container.resolve()
window.rootViewController = router.rootViewController
router.start()
.................................................
/// Код приложения
import SwiftLazy
class LoginRouter {
let rootViewController = UINavigationController()
private let loginPresenterProvider: Provider
init(loginPresenterProvider: Provider) {
loginPresenterProvider = loginPresenterProvider
}
func start() {
let presenter = loginPresenterProvider.value
presenter.loginSuccessCallback = { [weak self] _ in
...
}
// без force cast можно обойтись, но этот вопрос выходит за рамки разбора DI
rootViewController.push(presenter.view as! UIViewController)
}
}
protocol LoginPresenter: class {
var loginSuccessCallback: ((_ userId: String) -> Void)?
func login(name: String, password: String)
}
protocol LoginView: class {
func showError(text: String)
}
class LoginPresenterImpl: LoginPresenter {
private weak var view: LoginView?
private let authInteractor: AuthInteractor
init(view: LoginView, authInteractor: AuthInteractor) {
self.view = view
self.authInteractor = authInteractor
}
func login(name: String, password: String) {
if name.isEmpty || password.isEmpty {
view?.showError(text: "fill input")
return
}
authInteractor.login(name: name, password: password, completion: { [weak self] result in
switch result {
case .failure(let error):
self?.view?.showError(text: "\(error)")
case .success(let userId):
self?.loginSuccessCallback?(userId)
}
})
}
}
class LoginViewController: UIViewController, LoginView {
var presenter: LoginPresenter!
...
func showError(text: String) {
showAlert(title: "Error", message: text)
}
private func tapOnLoginButton() {
presenter.login(name: nameTextField.text ?? "", password: passwordTextField.text ?? "")
}
}
protocol AuthInteractor: class {
func login(name: String, password: String, completion: (Result) -> Void)
}
class AuthInteractorImpl: AuthInteractor {
func login(name: String, password: String, completion: (Result) -> Void) {
...
}
}
```
Как видим код описывающий внедрение зависимостей занимает малую часть, а прикладной код остается в неведенье о способе внедрения зависимостей.
Для рассмотрения более сложных кейсов советую посмотреть примеры кода:
* [Для хабра](Samples/SampleHabr/)
* [Хаос](Samples/SampleChaos/)
* [Делегаты и обсерверы](Samples/SampleDelegateAndObserver/)
* [SwiftPM большая архитектура](https://github.com/ivlevAstef/FunCorpSteamApp)
Или прочитать статьи:
* [https://habr.com/ru/post/457188/](https://habr.com/ru/post/457188/)
* Старая [https://habr.com/ru/post/311334/](https://habr.com/ru/post/311334/)
## Требования
iOS 11.0+,macOS 10.13+,tvOS 11.0+, watchOS 4.0+, Linux; ARC
* Swift 5.5-5.8: Xcode 13,14; version >= 3.6.3
* Swift 5.0-5.3: Xcode 10.2-12.x; version >= 3.6.3
* Swift 4.1: Xcode 9.3; version >= 3.2.3
* Swift 4.0: Xcode 9.0; version >= 3.0.5
* Swift 3.0-3.2: Xcode 8.0-9.0; 0.9.5 <= version < 3.7.0
* Swift 2.3: Xcode 7.0; version < 0.9.5
## Изменения
Смотри [CHANGELOG](CHANGELOG.md) файл, или вкладку [releases](https://github.com/ivlevAstef/DITranquillity/releases).
## История и планы
- [x] v1.x.x - Начальная версия
- [x] v2.x.x - Стабилизация [миграция с первой](Documentation/ru/migration1to2.md)
- [x] v3.x.x - Эволюция и фичи [миграция со второй](Documentation/ru/migration2to3.md)
- [ ] v4.x.x - API получения графа, оптимизация, Обновление документации и маркетинг [миграция с третьей](Documentation/ru/migration3to4.md)
- [ ] v5.x.x - Перенос валидации графа, и других проверок на этап компиляции
## Обратная связь
### Я нашел баг, или хочу больше возможностей
Напишите задачу на вкладке [GitHub задачи](https://github.com/ivlevAstef/DITranquillity/issues).
### Я нашел проблему в документации, или я знаю как улучшить библиотеку
Вы можете помочь развитию библиотеки сделав [pull requests](https://github.com/ivlevAstef/DITranquillity/pulls)
### Просьба
Если вам понравилась моя библиотека, то поддержите библиотеку поставив звёздочку.
### Остались вопросы?
Я могу ответить на ваши вопросы по почте: ivlev.stef@gmail.com