Skip to content

Commit

Permalink
add new safety idea - root components.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Ivlev committed Aug 31, 2023
1 parent f435228 commit fda9b88
Show file tree
Hide file tree
Showing 23 changed files with 248 additions and 50 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 4.5.0
* Add `root` components. For more information see `registration_and_service.md`

# 4.4.0
* Increase minimal version iOS 11.0, tvOS 11.0, macOS 10.13, watchOS 4.0
* Remove SpinLock
Expand Down
2 changes: 1 addition & 1 deletion DITranquillity.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = 'DITranquillity'
s.version = '4.4.0'
s.version = '4.5.0'
s.summary = 'DITranquillity - Dependency injection for iOS/macOS/tvOS (Swift) '

s.description = <<-DESC
Expand Down
14 changes: 7 additions & 7 deletions DITranquillity.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -1326,7 +1326,7 @@
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 11.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Debug;
Expand Down Expand Up @@ -1368,7 +1368,7 @@
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 11.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Release;
Expand Down Expand Up @@ -1512,7 +1512,7 @@
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -1556,7 +1556,7 @@
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -1689,7 +1689,7 @@
"$(inherited)",
);
INFOPLIST_FILE = Tests/DiTranquillityTest/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -1720,7 +1720,7 @@
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = Tests/DiTranquillityTest/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
1 change: 1 addition & 0 deletions Documentation/ru/core/logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ DISetting.Log.level = .verbose
* `Until get argument. Not found extensions for {Component}` - во время получения аргумента оказалось, что его нет. Такая ситуация возможна когда в указанном компоненте есть `arg`, но во время создания объекта аргумент не был передан с помощью возможностей библиотеки. Подробно про аргументы тут: [Модификаторы внедрения](modificated_injection.md#Аргумент)
* `Until make extensions can't find component by type: {type} in file: {file} on line: {line}` - при добавление расширения, не удалось найти компонент для указанного типа. На текущий момент такое возможно, только если вы хотите указать передачу компонентов. Для решение проблемы убедить что в контейнере зарегистрирован компонент данного типа.
* `Until make extensions can't choose component by type: {type} in file: {file} on line: {line}` - аналог выше стоящему, но только в этом случае говорится что для указанного типа выбор большой, и библиотека не может определиться какой выбрать. Для решения проблемы нужно убрать лишние регистрации.
* `Are you using root components, but a root component was found that was not marked as root: {Component}` - Может возникаеть в случае если вы используете на проекте root компоненты, но при этом был найден такой корневой resolve, который пытается создать не root компонент. Возможно такое возникло из-за не правильного использования, или же просто вы забыли объявить компонент рутовым. Обращаю внимание, что возникновение данной ошибки не гарантирует дальнейшей корректной работы библиотеки, так-как валидация графа была только с помеченными root компонентами.

#### info логи
* `Not found {type}` - не удалось найти компонент для указанного типа. Очень часто после этой ошибки следующая приведет к фатальной, так как тип окажется не опциональным.
Expand Down
30 changes: 30 additions & 0 deletions Documentation/ru/core/registration_and_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,33 @@ let logger: Logger = container.resolve()

## Тест
Аналогично поведения "по умолчанию" сущестует еще один вариант регистрации - тестовая регистрация. Она имеет наибольший приоритет при выборе, и игнорирует модульности и т.п. Из названия можно догадаться что этот способ регистрации нужен для тестов. Он позволяет уже на существуем графе зависимостей легко подменить одну или несколько зависимостей, без необходимости удалять аналогичные.

## Рутовый компонент
Начиная с версии 4.5.0 появилось понятие рутового компонента, которых может быть несколько.
Начнем с синтаксиса. Чтобы сделать любой компонент рутовым, надо его пометить таковым:
```Swift
container.register(MyClass.init)
.root()
```
Да так просто - при регистрации надо просто пометить компонент с помощью функции `.root()`.

!!! Проект на котором есть рутовые объекты, и на котором их нет отличается в плане анализа графа. Вы можете по прежнему использовать библиотеку по старому, без рутовых компонентов, но как только вы решили начать использовать их, то надо быть готовым проанализировать проект и пометить все нужные места.
Сами рутовые компонентами, это дополнительная информация для валидатора, с каких объектов нужно начинать проверять дерево графа. Это позволяет как ускорить поиск проблем (за счет отсечения части лишних проверок), так и сделать его более точным - так как теперь анализатор точно знает, что получение объектов может происходить только с указанных. Более того валидатор также может найти не используемые компоненты, и указать на это.
Как понять, какой компонент помечать как рутовый? это все те компоненты который создаются на прямую из контейнера, а не иерархично внутри по средством внедрения в другие объекты.
Ну или проще говоря если видете функцию `container.resolve()` то объект который она возвращает создается из рутовой компоненты.

!!! Отсюда следует важный момент - использовать рутовые компоненты стоит только в тех случаях, когда вы используете контейнер по изначальной задумке автора - с минимальным количеством получения объектов из контейнера на прямую, в идеале с одномим получением. А все остальные зависимости создаются иерархически, и благодаря Provider/Lazy не сразу, а только в момент нужды.

Так-как singleton объекты также создаются отдельно с помощью `initializeSingletonObjects` то они все являются по умолчанию рутовыми, но проверки будут включены только если есть хотябы один компонент содержит явное указание, что он root.

Для кастомных scope даже если они создаются с помощью `initializeObjectsForScope` по умолчанию нет рутовости, по этим причинам к этим компонентам стоит прописывать `.root()` ну либо для упрощения писать extension на подобии:
```Swift
extension DIComponentBuilder {
func myLifetime() -> Self {
return .lifetime(.custom(myScope))
.root()
}
}
```
Конечно это только в том случае если эти объекты чем-то напоминают singleton объекты, и создаются с помощью `initializeObjectsForScope`

3 changes: 2 additions & 1 deletion Documentation/ru/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

* Аргумент - Тот же самый аргумент на языке Swift, который мы передаем в метод инициализации.
* Экземпляр класса - Swift или Obj-C объект, который был создан с помощью библиотеки, используя контейнер.
* Компонент – Набор контрактов с библиотекой, позволяющие описать способ создания объекта.
* Компонент – Набор контрактов с библиотекой, позволяющие описать способ создания объекта.
* [Рутовый Компонент](core/registration_and_service.md#рутовый-компонент) - Это компонент помеченный специальный образом, указывающий на то, что создание графа зависимостей может начинаться с него. Это еще одна опциональная ступенька к улучшению защищенности/валидации ваших зависимостей.
* [Регистрация](core/registration_and_service.md) - Код, записанный в определенном стиле для описания способа создания объекта. При регистрации создаются компоненты.
* DI контейнер - Контейнер для регистрации компонент и создания экземпляров классов.
* Зависимость - Контракт, между двумя и более объектами, говорящий, что для создания одного объекта требуется создать другие.
Expand Down
Loading

0 comments on commit fda9b88

Please sign in to comment.