Подготовка за нелинеен работен процес

Въведение

„Преди това възхвалявах предимствата на модулната архитектура и как контролерът на потока помага при последователната навигация между изолирани функционални модули.

Предоставеното внедряване беше илюстративно — вкус за това как да се справим с проблема с монолитните архитектури — добре за малки приложения с един поток и ограничен брой модули, но не и решение за изграждане на по-сложни реални iOS приложения в екипи.

В тази статия представям по-мащабируемо решение. Нещо, което ще се справи с най-сложните и променливи навигационни структури и ще отговаря на динамичен работен процес, който не се бори с нелинейния характер на разработката на приложения.

Как работим

Производството на приложения е несъвършено от самото начало – предположенията често са неправилни, технологичните избори редовно са неподходящи и всеобхватните бизнес изисквания се променят непрекъснато. Способността за бързо преминаване от идея, към прототип, към итерация, към непрекъснато доставян продукт е от решаващо значение. Стремим се да преместим нашата визия от ниска към висока прецизност с главоломна скорост, в среда, която е хаотична.

Ние работим в екипи с различни размери и области на експертиза. В идеалния случай искаме да работим заедно. Всеки, разделяйки нашите усилия и допринасяйки за бързото производство на цялото. В действителност зависимостите на даден проект често не са готови, когато е необходимо, или могат да се променят, когато стане налична нова информация — потребителски тестови анализи, ръководства за стил, одобрение от изпълнителен директор, правни изисквания, бизнес цели, технологии и т.н.

Едно нещо, което може да се направи, за да се облекчи болката от движението напред в нестабилна среда - започнете с основа, която е предназначена за промяна. Когато се създават нови дизайни, спецификации, потоци, функции и технологии, те могат незабавно да бъдат прототипирани, изградени, интегрирани, тествани и пуснати.

Проектиране за промяна

Ако законодателят на ЕС реши, че провереното лицево сканиране вече е законово изискване за използване на мобилно приложение, е необходимо то да се включи бързо.

Работният процес включва екипа по дизайн и UX, който създава подходящ потребителски интерфейс и актуализиран поток на приложения. Инженерният екип работи върху изграждането или интегрирането на рамки на трети страни и — когато зависимостите станат достъпни — незабавно свързва работата си с новите компоненти на потребителския интерфейс, рамката на функциите и потока на влизане.

Прототипът е изолиран — зад контролер на функции — и не оказва влияние върху останалата част от кодовата база. Когато функциониращият прототип е завършен, той се разпространява в версия за тестване и анализ. Веднъж потвърден, той може да бъде премахнат зад контролера на функциите и интегриран в основната версия.

За да се постигне такъв работен процес, архитектурата трябва да бъде:

С възможност за мащабиране

  • Увеличете прецизността, от интерактивен скелет до пълнофункционално приложение
  • Свободно добавяйте, изтривайте и модифицирайте нови потоци, функции и компоненти

Композируем

  • Композирайте потоци от модални, подредени и разделени навигационни градивни блокове
  • Транспонирайте диаграми на потребителски поток в навигационни изживявания
  • Активиране на вложени потоци

Слабо свързани

  • Компонентите, функциите и потоците могат да бъдат прототипирани, изграждани, модифицирани и тествани изолирано с малко зависимости

Интерактивен, когато е необходимо

  • Преходите между функции могат по избор да използват вграден UIKit, персонализирани или интерактивни анимации

Съвместим сограниченията нарамката на Apple

  • Максимум 6 динамични рамки
  • Не се препоръчва използването на чадърни рамки
  • Целево време за предварителен студен старт от 400 ms

Не преоткриване на колелото

  • Използвайте повторно възможно най-много UIKit компоненти
  • Включете се в жизнения цикъл на потребителския интерфейс от кутията

Наслояване на вашите рамки

В iOS единицата за модулност е рамката. Те са динамичниили статични.

Динамичните рамкиобединяват динамични библиотеки dylibsи техните ресурси. Тези динамични библиотеки се зареждат - според нуждите - по време на изпълнение. Поради естеството на начина, по който работи това зареждане, Apple препоръчва максимум 6 несистемни dylibsдоставки във вашето приложение.

Статичните рамки обединяват статични библиотеки и техните ресурси. Тези статични библиотеки се прикачват по време на изпълнение. Това оказва влияние върху началните времена и отпечатъка на паметта, но като цяло не е толкова голямо, колкото наличието на повече от 6 dylibs.

Предложената от мен архитектура се състои от:

  • приложение, което зависи от;
  • единична динамична рамка, която зависи от много;
  • статични рамки, които са логически групирани в нива на отговорност.

Тези слоеве не се прилагат, но помагат при организирането на връзки на зависимост между рамките.

Рамките нагоре по веригата нямат познания за рамки надолу по веригата, зависещи от тях — с изключение на общите рамки на слоя, които може да имат зависимости в рамките на слоя.

Общ слой

Статични рамки, отговорни за общи услуги. Включително бази данни, мрежи, UI компоненти и данни.

Слоевете могат да бъдат допълнително подразделени на споделени и общи слоеве за по-нататъшно разграждане на зависимостите на споделени и общи рамки.

Слой с функции

Статични рамки, отговорни за агностичните характеристики на модела на дизайна. По желание могат да се използват MVC, MVVM, VIPER и др.

Рамките могат да се свързват с рамки нагоре по веригата в общия слой.

Слой с потоци

Статични рамки, отговорни за потоците от функции. Определя метода за навигация между функциите.

Рамките могат да се свързват с рамки нагоре по веригата в слоевете общи и функции.

Слой на потока на приложението

Статична рамка, отговорна за потоците на приложенията. Определя метод за навигация между потоци — поток от потоци.

Рамката може да се свързва с рамки нагоре по веригата в общите слоеве, характеристики и потоци

Ядро на приложението

Динамична рамка, която свързва всички рамки нагоре по веригата.

Приложение

Приложението вгражда и свързва към основната динамична рамка на приложението.

Модулна структура на практика

„Примерното приложение“ илюстрира предложената архитектура със следната модулна структура:

Композиране на вашите модули

Предполагам, че се ориентирате в Xcode, запознати сте с изграждането на работни пространства и добавянето на проекти за приложения с един изглед и рамки CocoaTouch към тях. Има много отлични уроци и статии по въпроса, моля вижте ги, ако се съмнявате. И не забравяйте, че този пример само илюстрира изпълнение. Има много вариации на представените тук теми, смесващи различни методи за управление на зависимости и структури на директория, работно пространство и проект.

В нашето примерно хранилище на приложението основната директория, ModularFlowArchitecture,съдържа:

  • ModularFlowArchitectureдиректория, съдържаща Xcode проекта на приложението за iOS;
  • ModularFlowArchitecture.xcworkspace, в който са свързани зависимостите на приложението и рамката;
  • Modules директория, съдържаща динамичните и статичните рамкови проекти и работни пространства.

Отворете ModularFlowArchitecture.xcworkspace или работното пространство на която и да е рамка в директорията Modules и ще наблюдавате общ модел в техните навигатори на проекти —проектът, разположен в горната част, и Dependenciesгрупа отдолу, съдържаща свързани зависимости на проекта.

Продуктите от тези проекти — а именно статичните и динамичните рамкинамерени в Productsгрупата на всеки проект — се свързват със зависимия проект чрез плъзгане на рамката в неговия раздел Свързани рамки и библиотеки, под раздела Община проекта.

След това Xcodeавтоматично ще добави зависимостта към групата Frameworks на зависимия проект.

Това дава на компилатора общо местоположение - директорията на вградените продукти на целта на изградения проект - за намиране на зависимостта при изграждане. Страничният ефект от тази настройка: всяко работно пространство е функционално и може да се тества изолирано.

Големият брой рамки означава, че управлението на зависимостите е задължително. Актуализирането на зависимостите на даден слой и зависимите от него продукти надолу по веригата би било непосилно да се направи ръчно. Обикновено използвам Git подмодулиили поддърветазаради минималното въздействие, което имат върху проекти и работни пространства. Тази тема обаче е далеч извън обхвата на тази статия и пример.

Настройте вашите статични рамки

Тази архитектура разчита на използването на статични рамки за повечето функционални зависимости нагоре по веригата. Правенето на рамка статична се постига чрез избиране на раздела Настройки за изграждане на целевата рамка и избиране на опцията Static Libraryна Свързване насекция Mach-O Typeселектор за настройка.

Сега добавете флага -all_load към настройката Other Linker Flagsв единичната динамична рамка на приложениетоAppCoreв моя пример — Раздел Настройки за изграждане. Това гарантира, че всички статични библиотечни символи са включени в динамичната рамка.

Резултатът от използването на всички тези статични рамки, свързани в една динамична рамка, е по-бързото време за стартиране на приложението. За да измерите времето за стартиране на приложението, добавете променливата на средата DYLD_PRINT_STATISTICS към схемата на приложението. Кратко ръководство за това как да направите това можете да намерите тук.

Трябва да се отбележи, че обединяването на статични библиотеки в една библиотека не намалява времето за студен старт. Така че не търсете там оптимизации на времето за зареждане.

Контрол на потока с FLXFlow

Сега, когато архитектурата е модулна, е време наистина да я използваме и да получим нещо като екран.

Контролирането на кой екран се появява следващият и как се появява е отговорност на контролера на потока. Когато множество потоци се нуждаят от координация, е необходим стек на контролера на потока — с родителски контролер на потока, който последователно прехвърля преходите между дъщерните потоци.

Има съществуващи рамки, като RxFlow, които до голяма степен правят това. Аз лично предпочитам решения с по-малко зависимости от трети страни и които са по-малко свързани, по-добре се поддават на модулност.

Поради тази причина включих малка рамка за контролер на потока, наречена FLXFlow, в примерното приложение. Неговите компоненти са:

Контролери на потока

  • FlowController, StackedFlowController, TabbedFlowController класове
  • Има UIViewController подкласове, които притежават контролер за навигация
  • В зависимост от типа, навигационният контролерразрешава модална, подредена или табулирана навигация с функции
  • Това са навигационни градивни елементи, които могат да бъдат композирани в стекове, за да позволят сложни навигационни структури
  • FlowAction обработката на събития се разпространява надолу по стека на контролера на потока, докато не бъде обработена, позволявайки преходи през дълбоко вложени навигационни структури

Поточно действие

  • FlowAction протокол
  • Протокол за маркиране, обикновено съобразен с enum за лесно съвпадение на шаблони
  • Декларира действия, задействани в поток, т.е. case presentModal(from: content:)

Делегат на действие на потока

  • FlowActionDelegate протокол
  • Делегиран протокол, чийто process(action:) метод обработва FlowActions и определя как да се променят навигационни контролери

Интерактори на потока

  • FlowInteractor, TapFlowInteractor, InteractiveFlowInteractor класове
  • Взаимодейства с потребителски или системни събития, като жестове или промени в състоянието на приложението
  • Тригер FlowActions

Появява се динамичен работен процес

С помощта на FLXFlow, във връзка със слоестата модулна основа, характеристиките на потока, последователността, преходите и зависимостите се дефинират в изолирана рамка. Не знае нищо за приложението, от което е част, и дали води до друг поток. Той познава само рамките, които е наследил.

Това води до появата на динамичен работен поток — цели потоци с функции могат да бъдат прототипирани, изградени и тествани, независимо от вашето основно приложение и от други потоци.

Смяната на компоненти, функции, последователности, преходи и интерактори става относително безболезнена. Дори конструирането на тестово приложение за валидиране на предположения се извършва чрез просто свързване на зависимости в нова цел и стартирането му.

Всичко може да се композира, използва повторно и може да се тества през цялото време.

Примерното приложение

„Примерното приложение“ е сравнително просто и има за цел да демонстрира представените ключови концепции. Тази диаграма на потребителския поток дава общ преглед.

При стартиране той проверява дали потребителят се е включил, като го насочва към потока за включване или потока за начало.

Потокът за включванесе намира на върха на подреденанавигационна структура. Зарежда се с първата функция за включване. След това могат да докоснат, за да преминат към втората функция за включване. Тази функция позволява на потребителя или да прекара пръст отляво и да премине интерактивно обратно към първата функция за включване или да докосне, за да премине към началния поток, приключвайки потока на включване и маркирайки включването като завършено.

Началният потоксе намира на върха на табовенавигационна структура. Зарежда се с началната първа функция. Потребителят може да докосва елементите на лентата с раздели, за да превключва между избрани функции — начало първа функция& начало втора функция. Докосването на друго място задейства представянето на потока на модула.

Модалният потоксъдържа само модалната функция. Докосването навсякъде ще започне преход за отхвърляне, премахвайки го от стека.

Контролерът на потока и стекът от функции

Транспонирането на потребителския поток в нашите модулни компоненти води до следния стек UIWindow, контролер на поток и функция.

Внедряване

Нека да преминем през някои от важните части на кодовата база. Свързаното хранилище съдържа допълнителни примери, включително подредени, табулирании модален поток контролери и вградени, персонализирани и интерактивни анимирани преходи, така че моля вижте.

Делегат на приложението

Приложен слой — приложение ModularFlowArchitecture

Инициализирането на приложението изисква едно импортиране на рамката AppFlow и инициализирането на AppFlowController. Тъй като също е UIViewController, той еrootViewController на UIWindow.

Проектът на приложението съдържа малко повече от делегата на приложението и свързване и вграждане на AppCore динамичната рамка.

Контролер на потока на приложението

Слой на потока на приложението — рамка на AppFlow

Нека дефинираме основното AppFlowController. Той координира потоците между своите дъщерни потоци включване, домашнои модални потоци .

Това е подклас StackedFlowController и следователно контролира UINavigationController като свой navigationController.

Разширението му съответства на FlowActionDelegate за обработка на FlowActions. така че е негов собственflowActionDelegate — изискване за обработка на FlowActions.

След това добавяме контролер на дъщерен потокHomeFlowControllerилиOnboardingFlowControllerв зависимост от това дали потребителят се е включил или не. Това прави контролера на потока част от веригата за обработка на FlowAction.

Накрая той добавя контролера на потока детекъм rootNavigationController чрез извикване на show().

Контролер на потока на приложението — разширение за делегат на поток

Слой на потока на приложението — рамка на AppFlow

Разширението замества необходимия process(action:) -> Bool метод. Действието enum съответства на case, което извиква метод на манипулатор.

Тези действия са дефинирани в съответните им рамки на FlowOnboardingFlow и т.н. Той има достъп до всички дъщерни FlowActions, ето защо може да координира между потоците.

Методите на манипулатора извършват мутации в стека navigationController на контролера на потокаили presentedViewController на UIViewController на първоначалната функция.

Той също така присвоява transitioningDelegate на UIViewController на целевата функция, за да контролира анимирани преходи.

Действие на потока на включване

Слой потоци — рамка OnboardingFlow

Декларира какви действия има даден поток. Често изпраща първоначалната (задействаща) функция като параметър. Тази функция притежава интерактор на потока и се използва за контролиране на анимации за преход чрез съответствието на интерактора с UINavigationControllerDelegate и т.н.

Вграден контролер на потока

Слой потоци — рамка OnboardingFlow

Координира потоците между дъщерния поток за включване.

При инициализация присвоява своите navigationController и flowActionDelegate.

Той инициализира и показва първата функция. Присвояване на tapFlowInteractor на тази функция, което инжектира метода, който задейства FlowAction събитие при докосване.

Файлът включва и разширението FlowActionDelegate. Подобно на разширението FlowActionDelegate на AppFlowController, то обработва FlowActions. За разлика от AppFlowController, той може да обработва само OnboardingFlowAction, тъй като не познава действията в други потоци.

Включване на интерактивен поток интерактор

Слой потоци — рамка OnboardingFlow

Този интерактор капсулира UIScreenEdgePanGestureRecognizer. Той управлява интерактивни преходи, като се съобразява с UIPercentDrivenInteractiveTransition. Когато преходът завърши, той задейства FlowAction, който се обработва от стека на контролера на потока.

Включване на първата функция

Слой с функции — рамка OnboardingFirstFeature

Функциите са просто UIViewControllers, които могат да притежават интерактори на потока, които отговарят на FlowInteractorProtocol.

Заключение

И така, ето го – ясно организирана модулна архитектура, проектирана да бъде мащабируема, композируема, свободно свързана и интерактивна, с допълнителното предимство да ви дава по-бързо време за изграждане и стартиране.

Промените във вашите проекти и потребителски потоци могат да бъдат безболезнено включени в компоненти, функции и потоци. Разполагането на нови модули заедно със старите за A/B тестване също е лесно.

Отделете малко време да си поиграете с „примерното приложение“ и вижте дали може да е от полза за вашия работен процес.

Изтегляне

Примерното приложение, за което се отнася тази статия, може да бъде намерено на адрес https://github.com/markjarecki/ModularFlowArchitecture

Примерният код в тази статия е лишен от много коментари, интервали между редове и декларации за публичен контрол на достъпа — за четливост. По-точно изпълнение може да се намери в свързания изходен код.

Този код се предоставя такъв, какъвто е.