Название "Спокойствие" выбрано не случайно - оно закладывает три базовых принципа библиотеки: понятность, простота и безопасность.
Оно говорит - используйте библиотеку и вы будете спокойны за свои зависимости.
Внедрение зависимостей (DI) это паттерн проектирования при котором некто поставляет зависимости в объект.
Является специфичной формой принципа инверсии управления (IoC) и помощником для принципа инверсии зависимостей.
Более подробно об этом можно почитать по ссылке
И советую ознакомиться со словарем который поможет лучше ориентироваться в терминах.
- Регистрация компонент и сервисов
- Внедрение через инициализацию, свойства, методы
- Опциональное внедрение, а также с аргументами, множественное, с указанием тэга/имени
- Отложенное внедрение
- Внедрение циклических зависимостей
- Указание времени жизни
- Поддержка модульности
- Полное и подробное логирование
- Одновременная работа из нескольких потоков
- Swift Concurrency
- Иерархичные контейнеры
Библиотека поддерживает три популярных пакетных менеджера: Cocoapods, Carthage, SwiftPM.
Начиная с версии 5.0.0 Cocoapods не поддерживается.
Вы можете воспользуйтесь "Xcode/File/Swift Packages/Add Package Dependency..." и указать в качестве url:
https://github.com/ivlevAstef/DITranquillity
Или прописать в вашем Package.swift
файле в секции dependencies
:
.package(url: "https://github.com/ivlevAstef/DITranquillity.git", from: "4.6.0")
И не забудьте указать в таргете в аргументе dependencies
зависимость на библиотеку:
.product(name: "DITranquillity")
Добавьте строчку в ваш Podfile
:
pod 'DITranquillity'
Добавьте строчку в ваш Cartfile
:
github "ivlevAstef/DITranquillity"
Библиотека использует декларативный стиль описания зависимостей, и позволяет отделить ваш прикладной код от кода описания зависимостей.
Для быстрого входа давайте рассмотрим пример кода одного упрощенного VIPER экрана:
.................................................
/// Описание зависимостей
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<LoginPresenter>
init(loginPresenterProvider: Provider<LoginPresenter>) {
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<String, Error>) -> Void)
}
class AuthInteractorImpl: AuthInteractor {
func login(name: String, password: String, completion: (Result<String, Error>) -> Void) {
...
}
}
Как видим код описывающий внедрение зависимостей занимает малую часть, а прикладной код остается в неведенье о способе внедрения зависимостей.
Для рассмотрения более сложных кейсов советую посмотреть примеры кода:
Или прочитать статьи:
iOS 13.0+,macOS 10.15+,tvOS 13.0+, watchOS 8.0+, Linux; ARC
- Swift 6.0-6.x: Xcode 16; version >= 5.0.0
- Swift 5.5-5.9: Xcode 13,14,15; 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 файл, или вкладку releases.
- v1.x.x - Начальная версия
- v2.x.x - Стабилизация миграция с первой
- v3.x.x - Эволюция и фичи миграция со второй
- v4.x.x - Оптимизация миграция с третьей
- v5.x.x - Swift Concurrency
Напишите задачу на вкладке GitHub задачи.
Вы можете помочь развитию библиотеки сделав pull requests
Если вам понравилась моя библиотека, то поддержите библиотеку поставив звёздочку.
Я могу ответить на ваши вопросы по почте: [email protected]