Мои мысли и ощущения


Последнее время стало модно использовать всеми горячо любимый фреймворк Swinject для внедрения зависимостей , о чем не стесняются писать в резюме. Для чего? Если коротко, то ваш код не должен быть сильно связным, все зависимости каждого компонента должны зависеть лишь от абстракций, а не от конкретной реализации. По крайней мере этого просит буква D из принципов SOLID. А внедрять эти зависимости можно из так называемого контейнера для внедрения зависимостей, напоминающий фабрику. Этот контейнер возвращает определенный инстанс определенного класса, отвечающему заданному протоколу, в том числе и с другими вложенными зависимостями. Получается, что пользователь не знает, каким образом и какой конкретный объект ему был возвращен контейнером, т.к. работает с объектом строго через заданный протокол. Таким образом можно еще и удобно протестировать работу отдельного компонента, сделав контейнер с заданной конфигурацией, в которой будут отдаваться классы-заглушки с законсервированными данными для зависимостей тестируемого компонента. Еще прикольно, что Swinject умеет задавать стратегию создания объекта, например, либо каждый раз новый объект, либо один раз в виде синглтона (+ это еще не все)

Это все прекрасно, но при каждом обновлении Xcode я получал ошибку:

Module compiled with Swift 4.2 cannot be imported by the Swift 5.0 compiler

Это означает, что настала пора снова брать Swinject и зачем-то пересобирать, затем обратно класть обновленный swiftmodule в проект. Это как-то не очень. После обновления до Swift 5.1 я его выбросил нахрен и написал свой Dependency Injection Container, причем ровно с таким же интерфейсом как и у Swinject, даже переписывать ничего не пришлось в уже существовавшем контейнере. Вроде бы дело нехитрое, запомнить стратегию создания объекта для заданного протокола в кложуре и когда тебя попросят - просто зарезолвить. Кроме того, добавилась еще и потокобезопасность! Да-да, в Swinject все разваливалось при резолвинге из разных потоков. Ну и еще я поддержал стратегию создания объектов в виде синглтона или каждый раз нового. Больше мне ничего от огромного фреймворка не нужно было.  Вышел компактный класс, который живет в хелперах проекта, можно забыть про отдельную перекомпиляцию рядом лежащего модуля. Ну и опять же, в очередной раз избавились от зависимости от модуля внедрения зависимостей, ведь когда-нибудь это могло бы сыграть злую шутку при очередном обновлении языка Swift

Подробнее




Наверняка у каждого iOS-разработчика про запас лежит свой кастомный контрол, который умеет получать и отображать картинку из интернетика по URL. Некоторые любители зависимостей cocoapods также используют сторонний SDWebImage. Впрочем, я решил поиспражняться поупражняться и сделать свой велосипед. Ведь всяко может обновиться iOS SDK, пацаны могут выкатить новый Swift, и тут выяснится, что все пропало, а контрибьютер стороннего компонента уже срубил кучу бабла и где-нибудь чиллит на Мальдивах с безлимитным куба-либре в руке, а релиз у вас уже завтра, и что делать - хз.

Кстати, сама идея использовать URL для UIImageView породила вот этот доклад, в котором ребята из ВКонтакте в сам урл запихивают допустим GPS-координаты - а в ответ получают картинку с местоположением в Google Maps, либо накладывать локально фильтры на изображение из галереи, очень гибко переопределив работу URLProtocol

Мой велосипед умеет:

  • Async load of images from the given url
  • Save already loaded images in NSCache
  • Persist already loaded images in app caches directory and restore it back after app relaunch
  • Ability to set placeholder while image is loading
  • Create only one network request when trying to load 1000 images with the same URL at the same time. Other copies are waiting the network request result

А пользоваться еще проще.

Листинг кода можно посмотреть на гитхабе, особенно может быть интересным, как сделано ожидание загрузки у остальных картинок с одинаковым урлом, пока первая не скачается, а остальным нотифицирует, что пора бы обновить картинку локально

Подробнее




Существует довольно-таки распространенная  необходимость сигнализировать о каких-либо событиях от одного к нескольким объектам. В Swift это решается двумя способами из коробки:

  1. KVO, которое тянет за собой обязательство наследоваться от NSObject
  2. NotificationCenter, который до iOS 11 SDK хранил сильную ссылку на объект-наблюдатель, что приводило к утечке памяти, если вовремя не отписаться. Еще он неудобен тем, что необходимо отписываться явным образом от конкретной сигнатуры нотификации, чтобы случайно не отписать от других важных событий текущий объект, если использовать removeObserver(self)

Еще один годный вариант - сделать свой Observable, который будет:

  1. Работать на кложурах, без использования NSObject-based классов или @objc-методов
  2. Самостоятельно больше не трекать умерших наблюдателей и не слать нотификации мертвому объекту

Идея не нова, в С++ уже давно используется boost::signal, примерно похожее и удобное будем делать на Swift

Подробнее




Недавно затащил в проект SwiftLint, чтобы код как можно ближе соответствовал Swift Style Guide, ну чтобы в общем был по всем канонам. Куча ворнингов, и довольно часто встречается вот такой:

Line Length Violation: Line should be 120 characters or less: currently 139 characters (line_length)

Вылазит для куска кода, где в wireframe инстанциируется View-слой для VIPER-модуля. Аналогично и для получения ячейки по идентификатору из UICollectionView / UITableView

Казалось бы, куда уж короче, ведь строчку некуда сокращать. Нашлась достаточно удобная оптимизация, позволяющая сделать код более читаемым, а также избавиться от строковых констант, для которых автокомплит не сработает. Кроме повышения читаемости кода, исключаем риск опечататься в строковом идентификаторе, который задан в Storyboard

Подробнее




В аду приготовлено отдельное место для того, кто придумал сделать отдельную кнопку в верхнем правом баре в Xcode, которая в режиме редактирования Storyboard отображает popup-окно с различными контролами. И затем оно само прячется при потере фокуса. Дико раздражает и заставляет делать на одно действие больше, да и неудобно это
 

Но решение есть в виде команды:

⇧+⌘+⌥+L

После чего окно станет отдельным, и мы сможем вернуть его вправо вниз на привычное место. В случае, если оно будет мешать для просмотра свойств в инспекторе - его можно явно закрыть

Подробнее




Представим, что у нас бывают тяжеловатые задачи, которые на некоторое время заметно блокируют UI приложения. Например, это может быть чтение и десериализация какого-нибудь JSON-файла из Bundle на старте приложения. Для заметного ускорения можно разгрузить главный runloop приложения, а также задействовать для решения задачи другие свободные ядра процессора на смартфоне.

Для этого нам поможет класс-хелпер, чем-то напоминающий Promise, но без возможности зафейлиться, т.е. результат будет возвращаться всегда. Базироваться он будет на DispatchQueue, где в глобальной очереди будет исполнятся сама задача, а результат возвращаться в главную очередь обратно.

Подробнее




Ранее я уже писал, как собирать Qt 4.8 для OS X Sierra и Linux. Осталось уточнить некоторые моменты для Windows. Основное отличие от ранее описанных инструкций таится в том, что мы будем все собирать под Visual Studio 2010 в отличие от GCC. Стоит отметить, что 2010 студия - последняя, которая поддерживается со стороны Qt 4.8.

Подробнее




Простой скрипт, который может пригодиться для периодического дампа всех MySQL баз данных на вашем сервере + заархивировать по отдельности каждую папку с сайтом. Код под катом

Подробнее




В общем, задача такая, сделать почти как в андроиде, чтобы плейсхолдер в UITextField в момент фокуса улетал наверх, а также само поле ввода было подчеркнуто и линия становилась полупрозрачной при потере фокуса.

Swift Custom TextField

 

Реализовано, конечно, не совсем как в гайдлайне материал дизайна, но задача была сформулирована именно таким образом. В реализации используется CoreGraphics Affine Transform для плавной анимации UILabel для плейсхолдера. Также переопределено само свойство placeholder со своими сеттерами и геттерами для текста нового UILabel. Можно задавать текст прямо из storyboard и, внимание, менять цвет текста плейсхолдера, ведь раньше приходилось хачить в рантайме через attributedPlaceholder. Исходный код лежит на гитхабе.

Подробнее




Если вдруг в гите понадобилось безболезненно переменовать текущую ветку как локально так и удаленно, то помогают такие команды:

git branch -m new_branch
git push origin :old_branch
git push --set-upstream origin new_branch

Подробнее