Перед миграцией взвесте все за и против, так-как изменения достаточно колосальные. При этом новые возможности возможно вам и не нужны.
- Удален
DynamicAssembly
, за место него теперь позднее связывание - можно разделить объявление и реализацию интерфейса. - Автоматическая поддержка области видимости у компонентов
- Проверка альтернативных типов во время компиляции - которые задавались через
asType
- Добавлено описание ошибок за место ихнего обычного вывода
- Добавлена поддержка стека вызова -> теперь при правильном использовании можно узнать не только причину, но и место откуда было вызвано, и какие типы были созданы в середине.
- Сильна улучшена автоматика при регистрации. Теперь можно напрямую передавать
init
функцию, и получать готовые значения для внедрения через свойствам. - Добавлен
weak single
- Добавлена возможность синхронизации двух контейнеров, так чтобы они имели одни и теже объекты с областью видимости
perScope
- Улучшено деление на части - если вы чтото не используете, это можно не включать в проект.
- Поддержка автоматического внедрения через свойства класса. Работает только для наследников от
NSObject
- Сильное изменение синтаксиса, на более краткий и понятный
Если эти возможности не являются для вас критичными, и проект не планируется развиваться еще очень долго, то не стоит проводить миграцию.
Начну пожалуй с установки. Теперь весь проект делиться на составные части:
- Core
- Description
- Component
- Module
- Storyboard
- Scan
- RuntimeArgs
По умолчанию устанавливаются следующие части: Core, Description, Component, Module, Storyboard.
По этой причине, если вы использовали возможности сканирования, или передачу аргументов во время исполнения, то стоит включить эти части при установке.
Для упрощения подобной ситуации, существует отдельно выделенная часть: Full
- в неё включаются все остальные части.
Пример использования:
pod 'DITranquillity/Description'
- установить поддержку описания
pod 'DITranquillity/Module'
- установить поддержку модулей
Ядро включается всегда
Начну с самой главной части - переименования.
Название самой функции также претерпело изменения: register() -> register(type:)
- но если вы использовали closure, то это изменение вас не коснется.
initializer
->initial
dependency
->injection
asType
->as
(также см. ниже)initializerDoesNotNeedToBe
->initialNotNecessary
asSelf
->as(.self)
asName
->set(name:)
asDefault
- >set(.default)
initial(byNib:)
->initial(nib:)
initial(byStoryboard:)
->initial(useStoryboard:)
Я окончательно отошел от именования принятого у тайфуна, и назвал вещи своими именами.
DIModule
->DIComponent
DIAssembly
->DIModule
publicModules
,internalModules
->components
(также см. ниже)register(module:)
->register(component:)
register(assembly:)
->register(module:)
Обращаю внимание на пересечение имен, поэтому обязательно переменуйте вначале модуль в компонент, дабы избежать коллизий имен.
- Регистрация типа с runtime аргументами через функцию
register
. см. ниже - Инициалицизая типа с runtime аргументами через функцию
initial
. Теперь существует всего одна возможность это сделать: функцияinitialWithArg
.
Причина: сложность понимания когда используются рантайм аргументы, а когда обычное внедрение. И достаточная редкость использования рантайм аргументов. DILazy
который появился в версии 1.3.1.
Причина: Противоречие концепции библиотеки - нужно использовать "настоящее внедрениее зависимостей"*!
и**!
Причина: Исключения возникающие внутри операций должны прокидываться наверх. Данные операции правоцируют на игнорирование ошибки, и падению в месте где возникла проблема, что не всегда удобно.
- Передача init функции при использовании метода
register(type:)
илиinitial()
.
Пример:builder.register(type: YourClass.init(p1:p2:)
- Внедрение свойств без использования контейнера.
Пример:.injection { $0.value = $1 }
или.injection { obj, value in obj.value = value }
- Обязательная! проверка альтернативного типа во время компиляции:
as(YourProtocol.self).check{$0}
. Так как старое использование не обязывало в функцииregister
указывать тип возращаемого объекта, хоть и было желательным, то существует способ проигнорировать проверку:as(YourProtocol.self).unsafe()
- Функции
register(type:)
,initial
,injection
,initialWithArg
теперь пробрасывают исключения наверх. По этой причине настоятельно рекомендую убрать из них всеtry!
и заменить на обычныйtry
. - Область видимости для компонент при использовании модулей. Очень важное расширение библиотеки, так как теперь библиотека самостоятельно следит чтобы небыло обращений из одного модуля в другой. Это накладывает ограничения, из-за которых старый код может начать кидать исключения. При этом даже в новом коде не стоит переносить контейнер из модуля в модуль, то есть если создается контейнер в модуле Main то этот контейнер не должен покинуть этот модуль. Исключением пожалуй является только storyboard, но его корректной обработкой занимается сама библиотека. Если у вас маленький проект, в котором вы уверены, что никогда не будет пересечения имен, лучше перепишите весь код на обычные компоненты за место использования модулей. Да модули более выразительные, но они несут дополнительную смысловую нагрузку.
- Позднее связывание. Достаточно сложная возможность, являющаяся следствием из нормальной поддержки области видимости для компонент внутри модулей. В силу этого позднее связывание доступно только если используются модули. Подробно о нем можно прочитать в главах: Позднее связывание, Модули
- Указание области видимости у компоненты: Теперь для указания области видимости по аналогии со старыми
publicModules
иinternalModules
, их нужно указывать у каждой компоненты отдельно. По умолчанию все компоненты используютinternal
. Для изменения наpublic
у компоненты нужно написать следующий образом:
class YourComponent: DIComponent {
var scope: DIComponentScope { return .public }
func load(builder: DIContainerBuilder) ...
}
Также см.: Компоненты
- По аналогии с другими DI контейнерами я добавил еще одну области видимости:
.weakSingle
. То есть объект живет, до тех пор пока не будут потеряны все ссылки на него, после чего он будет пересоздан. Подробно о времени жизни можно прочитать в главе: Время жизни - Автоматическое внедрение свойств. Эта возможность работает только для классов отнаследованных от
NSObject
. Причем наследоваться должен как класс хранящий свойства, так и сами свойства. Для указания факта, что вы хотите использовать автоматическое внедрение при регистрации типа нужно использовать функцию:useAutoPropertyInjection
. У этой возможности есть два существенных плюса: Все внедрения проходят сами и появление нового свойства не заставляет его указывать в регистрации; Эти свойства можно объявитьprivate
и они всеравно смогут внедриться. То есть можно избежать проблем с инкапсулящией. Подробно об этой возможности можно прочитать в главе: Регистрация - Сокращенный синтаксис для регистрации storyboard, дабы не писать каждый раз DIStoryboard:
builder.register(type: UIStoryboard.self)
.initial(name: "Main", bundle: nil)
- Значительные правки в работе со storyboard - исправление багов и написание тестов для этой части
- Автоматическое игнорирование включение одного и тогоже кода несколько раз. Если вы вызовете одну и туже функцию, в которой есть код регистрации, несколько раз то все отработает правильно - регистрация пройдет всего один раз.
- Ошибки теперь имеют стек. Помимо этого у них появилось словестное описание.
Для получения более подробной информации о расширении старых и появлении новых возможностях, смотрите обновленную документацию.