$ grep -r Tag: «swift»

-rw-r--r-- 10K 19 мая 2026 · CE824A9 · ~5 мин

Swift Adwaita: от 1.2.0 до 1.3.1

swift libadwaita open source swift package linux macos

После релиза 1.1.0 у библиотеки swift-adwaita вышло семь релизов подряд. Пока я продолжаю разрабатывать первое реальное приложение на этой обёртке, всплывают вещи, которые в синтетических тестах не видны — и почти каждая правка отсюда. Главная история этого цикла: swift-adwaita теперь собирается и запускается на macOS, а заодно я наступил на красивые грабли со Swift Concurrency внутри GLib main-loop и аккуратно с них слез.

Swift Adwaita

История одного бага: async, который никогда не выполняется

В 1.2.0 я схлопнул все диалоги (FileDialog, ColorDialog, FontDialog) на async throws и убрал колбэк-варианты — казалось, так чище. Через сутки выяснилось, что внутри запущенного GTK-приложения (g_application_run) код вида Task { @MainActor in await dialog.open(...) } просто никогда не выполняется. Дефолтный исполнитель главного актора в Swift — это DispatchQueue.main, а GLib main-loop его не крутит. Процесс выглядит живым, ошибок нет, кнопка нажимается — но файловый диалог не появляется.

1.2.1 экстренно вернул колбэк-варианты для FileDialog, 1.2.2 закрыл эту дыру окончательно: колбэк-перегрузки добавлены для всех async-API (Clipboard, ColorDialog, FontDialog, UriLauncher, Texture.load). Async-варианты остались — они нужны для тестов и не-GTK контекста, — но из обработчиков сигналов GTK теперь по умолчанию рекомендуется колбэк-форма. Долгосрочное решение (свой SerialExecutor поверх GLib) отложено как отдельная задача.

1.2.0: что появилось в API

  • Асинхронная загрузка изображений. Texture.load(from:) декодирует всё, что умеет GdkPixbuf — PNG, JPEG, GIF, WebP, TIFF, BMP — вне главного актора. Это шире, чем умеет нативный gdk_texture_new_from_filename.
  • Воспроизведение анимированных изображений. AnimatedImagePlayer крутит кадры из GdkPixbufAnimation в виджете Picture с методами start / stop / advanceFrame.
  • Application.onOpen и Application.run(arguments:) — обработка активации по файлам для приложений с флагом G_APPLICATION_HANDLES_OPEN.
  • Runtime-проверки типов виджетов. Widget.gtkType, isInstance(of:), и более строгий tryCast, который теперь действительно сужает тип, а не «успешно» приводит любой виджет к чему угодно.
  • Изолированные deinit на GObjectRef, GVariant и других обёртках — освобождение GObject теперь всегда происходит на главном акторе явно, а не на случайном потоке, который дропнул последнюю ссылку.
  • Минимальный тулчейн поднят до Swift 6.2 — isolated deinit в 6.1 экспериментальный, релизный тулчейн отказывался его включать.

1.2.3–1.2.5: удобства и буфер обмена

Три небольших релиза о том, чтобы реже импортировать CAdwaita ради рутинных вещей:

  • RGBA(hex:) — парсинг CSS-цветов: #RGB, #RGBA, #RRGGBB, #RRGGBBAA.
  • IconTheme — обёртка над gtk_icon_theme_get_for_display с addSearchPath(_:) для локальных иконок приложения.
  • ApplicationFlags как OptionSet: Application(id: "...", flags: [.handlesOpen, .nonUnique]) вместо сырых битовых масок.
  • MainContext.drainPending() и pump(for:) — однострочные замены для while g_main_context_pending { g_main_context_iteration }, которые в каждом тестовом наборе писались заново.
  • Перехват вставки. Widget.onPasteClipboard, синхронные пробы Clipboard.containsImage / containsFiles, асинхронные readTexture / readFiles, и Texture.encodedPNGData() — теперь можно перехватить вставку картинки в редактор и пропустить её через свой импорт, а не позволять GTK воткнуть её как текст.
  • Silencing GTK-CRITICAL спама от GtkScrolledWindow и неверно настроенных GtkDropTarget — опциональный фильтр + правильные сигнатуры сигналов ::enter / ::motion.

1.3.0: macOS как платформа для разработки

Главная новость цикла. swift-adwaita теперь собирается и работает на macOS 13+ на Apple Silicon. Linux остаётся главной целевой платформой, но локально разрабатывать и тестировать можно прямо на маке, не поднимая виртуалку.

  • Установка через Homebrew: brew install libadwaita gtksourceview5 pkgconf adwaita-icon-theme. Без adwaita-icon-theme кнопки в HeaderBar и баннеры рендерятся пустыми — Homebrew не подтягивает её транзитивно.
  • Обязательная переменная окружения: XDG_DATA_DIRS=/opt/homebrew/share, иначе libadwaita не находит свои GSettings-схемы и падает при старте.
  • DemoAppLib — все 78 примеров галереи теперь живут в отдельной библиотеке, которую можно слинковать с внешним приложением. Исполняемый DemoApp стал трёхстрочной обёрткой.
  • Xcode-пример в examples/macos/DemoApp/ — минимальный Xcode 16+ проект, который оборачивает галерею в обычный .app-бандл. Cmd+R и работает.
  • Параллельный набор тестов на XCTest для macOS. swift-testing на Apple-платформах вставляет autorelease-pool переходы между тестами, которые конфликтуют с Cocoa CFRunLoop источниками от gtk_init — на втором тесте всё падает. XCTest этого не делает, и тот же набор там проходит. Linux продолжает гонять swift-testing. Результат: 1181 тест / 0 падений на macOS.
  • Три специфичных для Apple бага, которые Linux/glibc маскировал: Variant.stringValue возвращал nil для валидных строк (висячий указатель на g_variant_type_checked_); хелперы локализации (localized, nlocalized) возвращали мусор без перевода (gettext возвращает входной указатель untouched, а Swift→C bridge уже освободил его); MediaStream.timestamp не компилировался, потому что gint64 — это long на Linux x86_64 и long long на Apple arm64.
  • macOS CI job на macos-26 с Xcode 26.4.1 (Swift 6.3). Только сборка, без прогона тестов: GitHub runner-ы headless, GTK4-Quartz падает без WindowServer-сессии.
  • REUSE 3.3 метаданные лицензий — SPDX-заголовки в каждом файле, reuse lint зелёный.

1.3.1: уборка

Maintenance-релиз без изменений API. Подтянул документацию по перехвату вставки в README, добавил adwaita-icon-theme во все инструкции по установке для macOS (наступили — записали), поднял в Xcode-примере deployment target до macOS 26, чтобы он совпадал с тем, на чём Homebrew собирает GTK4 — иначе линкер ругается на каждый dylib.

Что дальше

Главная незакрытая проблема — это всё ещё интеграция Swift Concurrency с GLib main-loop. Сейчас в GTK-приложении нельзя писать Task { @MainActor in ... } из обработчика клика, и это огорчает. Долгосрочный план — собственный SerialExecutor, который вместо DispatchQueue.main прокидывает работу через g_idle_add_full. Пока что callback-API закрывают все практические сценарии, но писать настоящий исполнитель когда-то всё-таки придётся.

Проект открытый, под MIT-лицензией. Исходники — на GitHub, документация с гайдами — здесь.

Star Fork

[↵] открыть пост swift-adwaita-from-1-2-0-to-1-3-1.md
-rw-r--r-- 3.4K 10 апр. 2026 · 1F1DDF3 · ~2 мин

Swifty Notes - менеджер markdown заметок для Linux на Swift

swift libadwaita open source linux

Swift Notes для Linux

После выпуска swift-adwaita я сосредоточился на создании своего первого приложения с его использованием. Что-то простое, но полезное для меня самого. И вот, первое приложение готово к использованию.

Swifty Notes — это нативное приложение для заметок в формате Markdown под Linux, использующее GTK/libadwaita и написанное на языке Swift.

Основной интерфейс — десктопное приложение: пишите, организуйте и просматривайте Markdown-заметки с помощью нативных виджетов GTK, функции автосохранения, сохранения состояния рабочего пространства и настраиваемых параметров редактора (размер шрифта, перенос строк, отступы и внешний вид).

В комплекте идет CLI (интерфейс командной строки), который работает с теми же файлами, поэтому shell-скрипты, инструменты автоматизации и AI-агенты могут безопасно просматривать или обновлять заметки без необходимости использования отдельной базы данных или фоновых служб.

  • Создание, переименование, дублирование, экспорт и удаление Markdown-заметок.
  • Автосохранение изменений, сохранение состояния рабочего пространства и хранение локальных изображений прямо вместе с каждой заметкой.
  • Настройка размера шрифта и других параметров редактора под разные экраны и рабочие процессы.
  • Выбор места хранения заметок, включая папки с облачной синхронизацией, что позволяет поддерживать актуальность одних и тех же файлов на разных устройствах.
  • Импорт изображений простым перетаскиванием (drag and drop) и рендеринг Markdown через нативный виджет GTK вместо использования веб-представления (web view).
  • Использование CLI для удобной автоматизации заметок в формате JSON с помощью скриптов, конвейеров оболочки (shell pipelines) и AI-агентов.

Скачать на Flathub

Swifty Notes для Linux — это проект с открытым исходным кодом, исходный код доступен на GitHub.

Star Fork

[↵] открыть пост swifty-notes-a-markdown-notes-manager-for-linux-in-swift.md
-rw-r--r-- 5.8K 10 апр. 2026 · D872E75 · ~3 мин

Swift Adwaita 1.1.0

swift libadwaita open source swift package linux

Swift Adwaita

Вышел релиз Swift Adwaita 1.1.0. Поскольку я работаю над первым приложением, созданным с использованием этой библиотеки, я расширил её возможности и исправил некоторые баги.

Основные изменения

  • Добавлена интеграция с GtkSourceView с использованием типизированных оберток на Swift для редактирования кода, подсветки синтаксиса, поддержки языков и схем стилей.
  • Расширен API виджетов в части работы с поповерами (popovers), окнами, совместимостью с календарем и обработкой жизненного цикла во время выполнения (runtime lifecycle).
  • Повышена стабильность релиза благодаря улучшенной интеграции с основным циклом GLib (main-loop), расширению покрытия CI и добавлению новых регрессионных тестов.

Добавлено

  • SourceView, SourceBuffer, SourceLanguage, SourceLanguageManager, SourceStyleScheme и SourceStyleSchemeManager.
  • Типизированные идентификаторы для языков и схем стилей GtkSource.
  • Новый демонстрационный пример редактора кода.
  • MainContext.task { ... }, task(after:) и task(every:) как отменяемые дескрипторы (handles) задач в основном цикле GLib.
  • Асинхронные помощники Async MainContext.run, yield и sleep(for:) для безопасного соединения конкурентности Swift (Swift concurrency) с циклом GLib.
  • Widget.unparent() и PopoverMenu.unparent().
  • Удобные помощники для работы с Popover и PopoverMenu.
  • Дополнительные регрессионные тесты для поиска родителя в цепочке виджетов/окон, а также расширенное покрытие для функций редактирования кода и поведения медиафайлов.

Изменено

  • GtkWindow.present() теперь удерживает окна открытыми до их закрытия, что делает использование временных или локально ограниченных окон более безопасным.
  • Widget.window теперь находит содержащее окно через цепочку родителей виджета, вместо того чтобы предполагать, что GTK root всегда является окном.
  • Обработка дат в календаре теперь использует прослойку (shim) для совместимости с GTK, что позволяет чисто собирать пакет как на старых, так и на новых версиях GTK.
  • Обновлены конфигурация генерации документации и хостинга документации.
  • В CI теперь выполняется установка и тестирование с системными зависимостями GtkSourceView 5.

Исправлено

  • Исправлена отложенная очистка сигналов/пользовательских данных (user-data): теперь она освобождает захваченные замыкания через основной цикл GLib, а не через задачи Swift main-queue, что позволяет избежать проблем с жизненным циклом в GTK-приложениях.
  • Исправлена распространенная ошибка планирования (scheduling pitfall) в GTK путем предложения API в стиле Task, который работает в основном цикле GLib вместо DispatchQueue.main.
  • Исправлены проблемы с видимостью сериализованного набора функций в Swift 6.1 в тестовом наборе.
  • Исправлены регрессионные тесты для popover/window, чтобы избежать путей падения (crash paths) GTK в CI при сохранении проверки ожидаемого поведения.
  • Обновлена документация по релизу и инструкции по установке: теперь они включают пакеты разработки GtkSourceView 5.

Документация и CI

  • В README добавлена ссылка на API-документацию.
  • Обновлены инструкции по установке для Ubuntu/Debian и Fedora.
  • Улучшена согласованность встроенной документации во всем API обертки.
  • Расширено покрытие CI для документации, форматирования и тестовых прогонов на Swift 6.1 / 6.2 / 6.3.

Это проект с открытым исходным кодом под лицензией MIT. Исходный код доступен на GitHub. Документация с руководствами доступна здесь.

Star Fork

[↵] открыть пост swift-adwaita-1-1-0.md
-rw-r--r-- 2.4K 31 марта 2026 · 6420B81 · ~1 мин

Библиотека Swift Adwaita

swift libadwaita open source swift package linux

Swift Adwaita

Это проект, который я всегда хотел создать: библиотека, позволяющая разрабатывать приложения для GNOME, используя Swift. Хотя существуют и другие подобные библиотеки, они кажутся либо неполными, либо заброшенными. Благодаря подходу Vibe Coding мне удалось завершить проект примерно за неделю.

swift-adwaita — это императивная обертка на Swift 6 для GTK4 и libadwaita, созданная для разработки нативных десктопных приложений GNOME под Linux.

Библиотека предоставляет современный Swift API поверх GTK и Adwaita с типобезопасными виджетами, сигналами, привязками свойств (property bindings), поддержкой асинхронных операций и удобными вспомогательными инструментами, сохраняя при этом нативный стиль, поведение и эстетику платформы GNOME.

  • Разработка нативных приложений для GTK4 и libadwaita на языке Swift.
  • Императивный API без использования кастомного DSL.
  • Типобезопасные перечисления (enums), сигналы и API свойств.
  • Поддержка Async/await для интеграции с основными возможностями платформы.
  • Широкая поддержка виджетов, подкрепленная полноценным демонстрационным приложением.

Вот демо-приложение, созданное с использованием swift-adwaita:

Это проект с открытым исходным кодом под лицензией MIT. Исходный код доступен на GitHub. Документация с руководствами доступна здесь.

Star Fork

[↵] открыть пост swift-adwaita-library.md
-rw-r--r-- 4.1K 28 марта 2026 · 506D0D0 · ~2 мин

Swift клиент для CouchDB 2.4.0

swift couchdb swift package vapor open source

Основные изменения

  • Проведен рефакторинг внутреннего устройства выполнения запросов в CouchDBClient для использования более чистых потоков в стиле async/await и уменьшения дублирования логики обработки ответов.
  • Улучшены пути буферизации и декодирования для сырых и типизированных запросов с упором на более безопасное выполнение в EventLoopGroup и повышение производительности при обработке ответов.
  • Расширено тестовое покрытие для выполнения запросов на базе EventLoopGroup, включая сырые и типизированные get, сырые и типизированные find, загрузку вложений, вывод списков индексов Mango и объяснение запросов Mango.
  • Обновлена документация для пользователей: README, статьи DocC и руководства (tutorials).
  • Обновлены инструменты документирования для новых рабочих процессов DocC, включая поддержку экспорта в Markdown.

Улучшения API и поведения

  • Добавлена и доработана поддержка EventLoopGroup для большего количества путей запросов в CouchDBClient.
  • Улучшены внутренние пути обработки ошибок и поведение декодирования ответов для операций CouchDB.
  • Упрощена многоуровневая структура помощников запросов и уменьшено дублирование кода обработки запросов/ответов.
  • Улучшено поведение, связанное с вложениями, и соответствующие тесты.
  • В манифест пакета добавлена поддержка visionOS.

Документация

  • Исправлены и обновлены примеры в README для использования CRUD и запросов Mango.
  • Обновлены целевые страницы DocC для лучшего отражения текущего API.
  • Исправлены фрагменты кода в руководстве по продвинутым запросам Mango, чтобы они соответствовали текущему типизированному API запросов.
  • Исправлены примеры в руководстве по Vapor.
  • Исправлена настройка пакета и зависимостей в руководстве по Hummingbird.
  • Обновлен buildDocs.sh для поддержки:
    • настраиваемого пути вывода и базового пути хостинга;
    • встраивания контента для статического хостинга;
    • экспериментального вывода в Markdown;
    • генерации манифеста Markdown.
  • Добавлен этап постобработки в buildDocs.sh, который внедряет относительную ссылку на Markdown в сгенерированные HTML-страницы DocC.

Инструменты и зависимости

  • Обновлены зависимости пакета в Package.resolved.
  • Обновлена матрица версий Swift для процесса сборки под Ubuntu.

CouchDB Client на GitHub | Документация с примерами и руководствами.

Star Fork

[↵] открыть пост couchdb-client-for-swift-2-4-0.md
-rw-r--r-- 2.1K 1 авг. 2025 · 508C136 · ~1 мин

Swift клиент для CouchDB 2.3.0

swift couchdb swift package vapor open source

Клиент CouchDB для Swift 2.3.0

Недавно я реализовал функцию, которую давно хотел добавить в CouchDB Swift client. Это поддержка Mango Queries. Наконец-то всё готово в версии 2.3.0. Кажется, это последняя крупная функция.

Список изменений:

  • Представлен надежный и типобезопасный MangoQuery API для построения сложных селекторов, проекций, сортировки и пагинации в CouchDB.
  • Добавлена поддержка указания индексов через useIndex в запросах для оптимизации производительности.
  • Добавлена полноценная поддержка загрузки, скачивания и удаления вложений документов (файлов/изображений).
  • Комплексный набор тестов для Attachments API обеспечивает надежность операций с файлами.
  • Добавлены модели и API для создания, перечисления и управления индексами Mango (MangoIndex, IndexDefinition).
  • В документацию теперь включены обучающие примеры и фрагменты кода для создания индексов.
  • Добавлена поддержка эндпоинта CouchDB Mango _explain через MangoExplainResponse, чтобы можно было проверить, как выполняются запросы и какие индексы используются.

CouchDB Client на GitHub | Документация с примерами и руководствами.

Star Fork

[↵] открыть пост couchdb-client-for-swift-2-3-0.md
-rw-r--r-- 2.6K 31 июля 2025 · 043C479 · ~2 мин

История одного бага в Leaf

swift vapor leaf open source

История одного бага в Leaf

Этот сайт работает на Vapor — серверном фреймворке для Swift. Он довольно старый, популярный и известный.

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

Я поддерживаю все зависимости в актуальном состоянии. Но недавно я заметил, что сайт начал потреблять слишком много памяти. Использование начинается примерно с 20 МБ, но спустя несколько недель я обнаружил, что на сервере оно разрослось до 750 МБ.

Для такого простого сайта это очень много, поэтому я начал копать. Изучив код сайта (который я долгое время не менял), я не смог найти никаких проблем (как и AI-агенты).

Для рендеринга HTML я использую Leaf — шаблонизатор от команды Vapor. И в их репозитории на GitHub как раз был открытый тикет, описывающий точно такую же проблему.

Поэтому я попросил Copilot помочь мне разобраться, в чем дело. Он довольно быстро добавил в мой форк новые тесты, которые выполняли множество рендерингов в цикле. И утилита leak обнаружила циклы сильных ссылок (retain cycles). Я исправил это с помощью аннотации weak, но во время проверки моего PR мейнтейнер предложил просто использовать unowned.

И вот и всё. 2 строки кода исправили рост потребления памяти. Трудно поверить, но спустя несколько часов после деплоя сайт всё еще потребляет всего 2 МБ памяти. Так что сегодня я очень собой горжусь.

Мораль: вносите вклад в Open Source проекты, которыми вы пользуетесь.

[↵] открыть пост a-story-of-one-bug-in-leaf.md
-rw-r--r-- 848B 10 июля 2025 · A43AF6A · ~1 мин

Swift package для проверки орфографии с использованием Яндекс Спеллер

swift open source yandex speller

Swift-пакет для проверки орфографии с использованием Yandex.Speller

Для своих личных целей я хотел добавить проверку орфографии в одно из своих приложений. Она должна проверять текст и автоматически исправлять его. Встроенные инструменты macOS для работы с текстом отлично работают с английским, но не работают с русским.

Так что, когда всё было готово, почему бы не поделиться этим?

https://github.com/makoni/YaSpellChecker

[↵] открыть пост a-swift-package-for-spell-checking-using-yandex-speller.md
-rw-r--r-- 3.2K 8 июля 2025 · 931C7C2 · ~2 мин

Недавние обновления моих проектов

swift couchdb app store release informer open source ai

В последнее время я много экспериментирую с AI-агентами, и это серьёзно вдохновило меня обновить мои проекты. Работа с реальными задачами делает процесс гораздо интереснее.

Swift клиент для CouchDB

Вышло 2 новых релиза библиотеки couchdb-swift. Copilot любезно добавил в библиотеку больше юнит-тестов для покрытия различных сценариев использования. В основном они завершались ошибками. Мне всё ещё пришлось многое дорабатывать вручную, но это подкинуло мне пару идей. Поэтому в этих двух последних релизах я добавил более детальную обработку ошибок. Также он нашел и исправил несколько мелких багов, проверил мой PR и предложил некоторые улучшения. Он еще далеко не идеален, но всё равно впечатляет.

Бот Release Informer для Telegram

В какой-то момент я обнаружил, что Copilot Agent стал доступен для моей учетной записи, и теперь я могу просто создать issue на GitHub, назначить его на агента и посмотреть, что он сделает. Я начал с запроса обновить README для Release Informer Bot, добавив подробности о том, как он работает и как его настроить.

После того как агент закончил работу над обновленным README, у меня появилась еще одна идея: процесс настройки можно автоматизировать. Поэтому я открыл Visual Studio Code и попросил Copilot добавить автоматическое создание необходимой базы данных и её настройку с нужными индексами CouchDB.

Он и тут справился неплохо, но всё равно потребовались некоторые ручные правки и изменения. И в процессе этого появился еще один релиз библиотеки CouchDB client, так как я захотел полагаться на корректную ошибку «not found».

Трудно сказать, сколько времени мне сэкономил Copilot. Но определенно много. Включая мой любимый автоматический перевод строк. Надеюсь, это вдохновит меня выпускать еще больше обновлений.

[↵] открыть пост recent-updates-of-my-projects-july-2025.md
-rw-r--r-- 1.9K 16 апр. 2025 · B2756B3 · ~1 мин

Swift клиент для CouchDB версия 2

swift couchdb swift package vapor open source

Клиент CouchDB для Swift, версия 2

Недавно я выпустил несколько новых версий CouchDB клиента для Swift. Последняя версия — 2, и она включает в себя несколько ключевых изменений:

  • Обновлена минимально необходимая версия Swift до 6.0.
  • Внедрена поддержка Swift Concurrency. CouchDBClient был обновлен и теперь является actor.
  • Библиотека была переименована из couchdb-vapor в couchdb-swift, чтобы лучше отражать её назначение как универсального клиента CouchDB для Swift, а не только для использования с Vapor.
  • Внесены изменения в инициализатор. Вместо передачи множества параметров теперь используется структура Config.
  • Теперь вы можете передать собственный экземпляр HTTPClient для использования в клиенте.
  • Добавлены переводы сообщений об ошибках.
  • Представлен специальный туториал по интеграции CouchDBClient с серверным фреймворком Hummingbird.
  • Добавлен метод shutdown() для корректного освобождения ресурсов, связанных с HTTPClient.

CouchDB Client на GitHub | Документация с примерами и руководствами.

Star Fork

[↵] открыть пост couchdb-client-for-swift-version-2.md
-rw-r--r-- 2.0K 8 апр. 2024 · BA68FE9 · ~1 мин

Swift клиент для CouchDB 1.5.0

swift couchdb swift package vapor open source

Клиент Swift для CouchDB 1.5.0

И вот, ещё одна новая версия библиотеки CouchDB client. После недавнего поста про версию 1.4.0, в Swift on Server вышла новая версия async-http-client, которая включает новую реализацию синглтона клиента. Теперь это HTTPClient.shared, поэтому я обновил библиотеку CouchDB, чтобы адаптировать её к этому изменению. Это также означает, что вам больше не нужно вызывать httpClient.syncShutdown(), если используется синглтон. Кроме того, они подняли минимальную версию Swift до 5.8 (что я также сделал в версии 1.4.0 клиента для CouchDB). Так что я продолжаю поддерживать библиотеку в актуальном состоянии.

Список изменений:

  • Обновил минимальную версию async-http-client до новой 1.21.0. Если вы не можете использовать её в своем проекте, вы всё еще можете оставаться на версии 1.4.0.
  • Теперь библиотека будет внутри использовать HTTPClient.shared (новое в async-http-client 1.21.0) для запросов, если не предоставлен EventLoopGroup.
  • Больше нет внутренних вызовов httpClient.syncShutdown() при использовании HTTPClient.shared.

CouchDB Client на GitHub | Документация с примерами и руководствами.

[↵] открыть пост swift-couchdb-client-1-5-0.md
-rw-r--r-- 1.7K 8 апр. 2024 · 08DA6BF · ~1 мин

Swift клиент для CouchDB 1.4.0

swift couchdb swift package vapor open source

Swift CouchDB client

Вышла новая версия CouchDB Client:

  • Библиотека перешла с HTTPClient.Response на HTTPClientResponse, что похоже на HTTPClient.Response, но предназначено для Swift Concurrency API. Также был выполнен переход с HTTPClient.Body на HTTPClientRequest.Body. Эти изменения влияют на методы get и find. Старые методы помечены как устаревшие (deprecated), для миграции смотрите документацию для получения информации об обновленных методах.
  • Минимальная версия Swift теперь 5.8.
  • Протокол CouchDBRepresentable теперь помечен как Codable.
  • Кроме того, была добавлена новая модель данных под названием RowsResponse. Она принимает обобщённый тип CouchDBRepresentable, что упрощает получение строк из базы данных. Например:
    let decodeResponse = try JSONDecoder().decode(RowsResponse<MyApp>.self, from: data)
  • Наконец, внесены небольшие улучшения в документацию и обучающие примеры.

CouchDB Client на GitHub | Документация с примерами и руководствами.

[↵] открыть пост swift-couchdb-client-1-4-0.md
-rw-r--r-- 1.4K 4 апр. 2024 · 0427246 · ~1 мин

Middleware для роутинга в Vapor 4 для обрезания слеша в url

vapor code snippets шпаргалки swift server side swift

Для веб-сайта или бэкенда — обычное дело, когда такие URL, как mySite.com/webpage и mySite.Com/webpage/, ведут на одну и ту же страницу. Для поисковых систем это разные адреса. Чтобы избежать дублирования контента, можно добавить простую middleware, которая будет удалять завершающий слеш и перенаправлять пользователя.

Вот пример кода для такого класса middleware в Vapor 4:

final class TrimSlashInPathMiddleware: Middleware {
    func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
        if request.url.path.count > 1, request.url.path.hasSuffix("/") {
            let newPath = String(request.url.path.trimmingSuffix(while: { $0 == "/" }))
            let response = request.redirect(to: newPath, redirectType: .permanent)
            return request.eventLoop.makeSucceededFuture(response)
        }
        return next.respond(to: request)
    }
}

Просто добавьте его в файл configure.swift:

import Vapor

public func configure(_ app: Application) throws {
    app.middleware.use(TrimSlashInPathMiddleware())

    app.http.server.configuration.port = 8081

    try routes(app)
}

Работает с Vapor 4.92.5.

[↵] открыть пост a-middleware-for-vapor4-to-trim-a-slash-in-url-path.md
-rw-r--r-- 1.3K 29 марта 2024 · EA452FF · ~1 мин

Swift клиент для CouchDB 1.3.2

swift couchdb swift package vapor open source

Клиент Swift для CouchDB

Недавно вышло несколько новых версий моей библиотеки Swift CouchDB. Вот последние изменения:

  • dateDecodingStrategy и dateEncodingStrategy теперь можно передавать в качестве параметров для методов get, update и insert.
  • Добавлена проверка для обработки истекших cookie аутентификации.
  • Сравнение заголовка set-cookie в ответе теперь выполняется в нижнем регистре.
  • Представлены новые методы для использования API _find, позволяющие искать документы с помощью декларативного синтаксиса JSON-запросов.
  • Исправлена ошибка, из-за которой метод update не использовал параметр dateEncodingStrategy.

CouchDB Client на GitHub | Документация с примерами и руководствами.

[↵] открыть пост swift-couchdb-client-1-3-2.md
-rw-r--r-- 1.2K 26 дек. 2022 · BE2067B · ~1 мин

Swift CouchDB client 1.2.1

swift couchdb swift package vapor open source

Swift CouchDB client 1.2.1

Небольшое обновление библиотеки Swift CouchDB client с парой новых методов, которые понадобились мне самому:

  • Новый метод для создания базы данных [docs].
  • Новый метод для удаления базы данных [docs].
  • Новый метод для проверки существования базы данных [docs].
  • Каждый запрос теперь обрабатывает ошибки unauthorised.
  • У модели CouchDBClientError теперь есть текстовое описание.

CouchDB Client на GitHub | документация с примерами и туториалами.

[↵] открыть пост swift-couchdb-client-1-2-1.md
-rw-r--r-- 2.0K 4 окт. 2022 · A996DD3 · ~1 мин

Swift CouchDB client 1.2.0

swift couchdb swift package vapor open source

Swift CouchDB client 1.2.0

Пару месяцев назад я начал разбираться с Apple’овским DocC — инструментом, который генерирует документацию из исходного кода. Я решил задействовать максимум возможностей, поэтому взял свою небольшую библиотеку CouchDB Client и добавил документацию к каждому методу, включая примеры использования, которые Xcode будет показывать в попапе автодополнения. Как часто бывает, в процессе написания доков и примеров я обнаружил, что многие вещи в библиотеке можно сделать гораздо лучше. Поэтому я обновил существующие методы и добавил несколько новых — таких, что принимают документ параметром и используют дженерики.

Следующим шагом были руководства. Apple даёт разработчикам возможность создавать ровно такие же руководства, как у них самих для SwiftUI. Так что я добавил парочку, и они тоже лежат в репозитории на GitHub.

Уверен, что на документацию я потратил больше времени, чем на саму библиотеку, но надеюсь, что оно того стоило.

CouchDB Client на GitHub | документация с примерами и туториалами.

[↵] открыть пост swift-couchdb-client-1-2-0.md
-rw-r--r-- 518B 30 нояб. 2020 · 411B1E1 · ~1 мин

Swift CouchDB client 1.0.0

swift couchdb swift package vapor open source

Swift CouchDB client library

Закончил новую версию Swift CouchDB client. Теперь она использует только async-http-client в качестве зависимости для http/https-запросов. Совместима с Vapor 4.

Проект на GitHub: https://github.com/makoni/couchdb-swift

[↵] открыть пост swift-couchdb-client-1-0-0.md
-rw-r--r-- 1.9K 29 марта 2020 · C1C7EDD · ~1 мин

Оборачиваем модели в SwiftUI для соответствия Identifiable

swiftui swift шпаргалки

Чтобы использовать массив моделей во вью List в SwiftUI, эти модели должны соответствовать протоколу Identifiable.

Вот пример модели:

struct MyDataModel {
    let title: String
    let message: String
} 

А вот пример вью SwiftUI, в котором хотим показать список:

struct TestView: View {
    var dataArray: [MyDataModel]
    var body: some View {
        List(dataArray) { data in
            VStack {
                Text(data.title)
                Text(data.message)
            }
        }
    }
} 

Если использовать MyDataModel внутри List, Xcode выдаст ошибку:

Ошибка соответствия Identifiable в Xcode

Иногда модель просто так не поменять. Например, она может быть из стороннего SDK, который вы используете в приложении. Но можно написать обёртку для этой структуры, которая соответствует протоколу Identifiable:

struct MyDataModelWrapper: Identifiable {
    var id = UUID()
    var data: MyDataModel
}

let testDataModel = MyDataModel(
    title: "Title 1",
    message: "I wanna be used inside of a List"
)

let wrappedData = MyDataModelWrapper(data: testDataModel)

Тогда вью будет выглядеть так:

struct TestView: View {
    var dataArray: [MyDataModelWrapper] 

    some View {
        List(dataArray) { wrappedData in
            VStack {
                Text(wrappedData.data.title)
                Text(wrappedData.data.message)
            }
        }
    }
}

Готово.

[↵] открыть пост wrapping-models-in-swiftui-for-identifiable-conformance.md
-rw-r--r-- 1.2K 23 сент. 2019 · B28AFA6 · ~1 мин

Синтаксический сахар для парсинга JSON в Swift

swift swift package json open source

Декодирование и кодирование JSON стало простым после появления протокола Codable в Swift 4.0. Но в процессе разработки мне хотелось чего-то более короткого и изящного, чем конструкция Do-Catch вида:

var myModel: MyModel?
let decoder = JSONDecoder()

do {
    myModel = try decoder.decode(MyModel.self, from: data)
} catch {
    print(error.localizedDescription)
}

Или вот такого:

let myModel: MyModel? = try? decoder.decode(MyModel.self, from: data)

Поэтому я написал протокол с реализацией по умолчанию, который позволяет декодировать вот так:

let myModel = MyModel.decodeFromData(data: data)

И так же — кодировать:

let data = MyModel.encode(fromEncodable: myModel)

Всё, что нужно, — добавить соответствие протоколу:

extension MyModel: Parseable {
    typealias ParseableType = Self
}

Доступно на GitHub как Swift Package: https://github.com/makoni/parsable

[↵] открыть пост syntax-sugar-for-json-parsing-in-swift.md
-rw-r--r-- 1013B 10 сент. 2019 · 64BE02E · ~1 мин

GitHub Actions как CI для Swift-проекта

swift github ci шпаргалки

Github Actions

Конфиг workflow для GitHub Actions, который собирает Swift-проект. В примере собирается проект на Vapor с помощью Swift 5.0.3 на Ubuntu 18.04:

name: Ubuntu 18.04 Swift 5.0.3

on: [push]

jobs:
  build_on_ubuntu:
    runs-on: ubuntu-18.04

    steps:
    - name: Install dependencies
      run: sudo apt-get update; sudo apt-get install -yq libssl-dev zlib1g-dev

    - name: Checkout
      uses: actions/checkout@master

    - name: Download Swift
      run: curl https://swift.org/builds/swift-5.0.3-release/ubuntu1804/swift-5.0.3-RELEASE/swift-5.0.3-RELEASE-ubuntu18.04.tar.gz --output swift.tar.gz

    - name: Unpack Swift
      run: |
          tar xzf swift.tar.gz
          mv swift-5.0.3-RELEASE-ubuntu18.04 swift

    - name: Swift build
      run: |
          export PATH=$(pwd)/swift/usr/bin:"${PATH}"
          swift build -c release
[↵] открыть пост using-github-actions-as-ci-for-building-swift-project.md
-rw-r--r-- 1.5K 1 окт. 2018 · 66FB865 · ~2 мин

Бенчмарки: Vapor 3 vs. Vapor 2

swift backend vapor ubuntu benchmark

После миграции (а по факту почти переписывания) своего небольшого проекта с Vapor 2 на Vapor 3 я прогнал бенчмарки, чтобы сравнить производительность. Я не запускал бенчмарки на последней версии Vapor 2.x, поэтому буду сравнивать Vapor 2.1.0 с результатами Vapor 3.1.0.

Конфигурация моего сервера:

  • 2 ГБ RAM
  • 1 ядро CPU
  • SSD
  • 125 Мбит/с исходящего трафика
  • Ubuntu 16.04.2 LTS
  • CouchDB

Бенчмарк с другого сервера запускался так:

wrk -t4 -c20 -d5m https://my_url

API просто берёт данные из CouchDB и отдаёт их как JSON. Проект на Vapor собирался Swift 4.2.

[↵] открыть пост benchmarks-vapor-3-vs-vapor-2.md
-rw-r--r-- 8.8K 24 нояб. 2017 · 210FD78 · ~4 мин

Анимация касания индикатора в UISlider

swift ios полезное uislider

Анимация касания индикатора в UISlider

Стояла задача - при касании слайдера анимировать в приложении сделать анимированное увеличение ползунка. Так же, как это сделано у Apple в приложениях Apple Music и Podcasts в плеере, когда начинаешь перематывать позицию воспроизведения. Пока искал способ стандартными средствами, убил немало времени. Очень не хотелось писать прям свой кастомный слайдер, хотелось использовать системный UISlider, что мне, в итоге, и удалось.

[↵] открыть пост animatsiya-kasaniya-indikatora-v-uislider.md
-rw-r--r-- 567B 5 сент. 2017 · E5E50B4 · ~1 мин

openssl и Vapor 2

vapor swift шпаргалки

Столкнулся с тем, что перестал компилиться проект на Ubuntu на Vapor 2. Точнее, одна из зависимостей - Crypto. Оказалось, что из-за добавленного репозитория, в котором были более новые версии некоторых библиотек, оно и не компилилось. Запишу сюда - пришлось даунгрейдиться:

apt install libssl-dev=1.0.2g-1ubuntu4.10
apt install openssl=1.0.2g-1ubuntu4.10
[↵] открыть пост openssl-i-vapor-2.md
-rw-r--r-- 1.4K 14 июня 2017 · 5C60537 · ~2 мин

Бенчмарки: Vapor 2 vs. Vapor 1

swift backend vapor ubuntu

После миграции своего пет-проекта с Vapor 1 на Vapor 2 я прогнал бенчмарки, чтобы сравнить производительность. Я не запускал бенчмарки на последней версии ветки 1.x (1.5.15), поэтому буду сравнивать Vapor 2.1.0 с результатами Vapor 1.2.5 из прошлого замера.

Мой сервер:

  • 2 ГБ RAM
  • 1 ядро CPU
  • SSD
  • 125 Мбит/с исходящего трафика
  • Ubuntu 16.04.2 LTS
  • CouchDB

Бенчмарк с другого сервера запускался так:

wrk -t4 -c20 -d5m https://my_url

API просто берёт данные из CouchDB и отдаёт их как JSON. Проект на Vapor собирался Swift 3.1.1.

[↵] открыть пост benchmarks-vapor-2-vs-vapor-1.md
-rw-r--r-- 2.5K 3 мая 2017 · 6BC24C7 · ~2 мин

Weak delegate в Swift 3

swift шпаргалки утечки памяти

Memory usage in Xcode

Боролся тут с утечками памяти в рабочем проекте. Копание привело к тому, что после ухода из UIViewController далеко не вся память освобождается. Если несколько раз открывать этот UIViewController, возвращаться назад и снова открывать - потребляемая память растёт и не очень слабо освобождается.

Суть проблемы оказалась в протоколах и делегатах. Классическая ошибка. У меня в UIViewController используется UICollectionView с кастомной ячейкой, у которой есть делегат. Мой UIViewController является для каждой ячейки делегатом. Пример реализации протокола и делегата в интернете и книге по Swift выгядит примерно так:

// Protocol
protocol MyCollectionViewCellDelegate {
    func someFunc()
}

// UICollectionViewCell
final class MessageCollectionViewCell: UICollectionViewCell {
    var delegate: MyCollectionViewCellDelegate?
}

// UIViewController
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    // some code....
    cell.delegate = self
}

Внутри объекта ячейки у нас strong-ссылка ;на делегата (UIViewController), в результате происходит утечка памяти.

Решение простое - сделать слабую ссылку (weak) для delegate. Для этого придётся указать протоколу, что реализовать его сможет только класс. Структуры уже пролетают, но и нужны в структурах нет. Меняется всё так:

// Protocol
protocol MyCollectionViewCellDelegate: class {
    func someFunc()
}

// UICollectionViewCell
final class MessageCollectionViewCell: UICollectionViewCell {
    weak var delegate: MyCollectionViewCellDelegate?
}

После этих простых изменений всё стало прекрасно - после возвращения назад из UIViewController использование памяти возвращается на исходный уровень.

[↵] открыть пост weak-delegate-v-swift-3.md
-rw-r--r-- 1.7K 9 февр. 2017 · F3580BC · ~1 мин

Сайт Space In Box

swift приложения space in box vapor

Сайт Space In Box

Решил информацию о своих приложениях вынести с apps.arm1.ru на отдельный домен. Раз я их когда-то начал публиковать под именем Space In Box, то и домен соответствующий: spaceinbox.me. Решил убрать уже не актуальные и не доступные приложения и максимально везде использовать векторную графику (иконки, логотипы), ибо на ретине глаз сильно режет.

Был отличный повод сделать что-то на Swift на фреймворке Vapor, пока всё нужное получилось. Обновлять, если что-то меняется в логике, конечно, тяжелее, т.к. приходится билдить на сервере всё заново, а это происходит минуты 3-4 каждый раз. А если что-то касается оформления (подправить html), то шаблоны подхватываются бинарником на лету и компилить заново не надо. Скорость работы и потребляемая память (пока 8.8 мб) радуют. Теперь можно обновлять старые и фигачить новые приложения.

UPDATE: С простым обновлением до Vapor 1.5.14 без изменения какого либо кода сайт стал есть ещё меньше памяти - 6.7-7.7 мб.

[↵] открыть пост sajt-space-in-box.md
-rw-r--r-- 1.7K 8 дек. 2016 · B2818A9 · ~2 мин

Бенчмарки Vapor 1.2.5

swift backend vapor ubuntu

Примерно месяц назад я прогонял бенчмарки для серверных Swift-фреймворков. Вчера я обновил свой проект до новой версии Vapor — 1.2.5. Раньше использовал 1.1.11.

Одно из самых важных изменений в Vapor 1.2.x — то, что Vapor теперь использует неблокирующий сервер.

Сегодня я решил прогнать новый бенчмарк проекта, обновлённого до Vapor 1.2, и результаты меня удивили. Он не только быстрее версии 1.1.x — он сравнялся с результатами Node.js!

Vapor vs. Node.js

Мой сервер:

  • 2 ГБ RAM
  • 1 ядро CPU
  • SSD
  • 125 Мбит/с исходящего трафика
  • Ubuntu 16.10
  • CouchDB

Бенчмарк с другого сервера запускался так:

wrk -t4 -c20 -d5m https://my_url

API просто берёт данные из CouchDB и отдаёт их как JSON.

[↵] открыть пост benchmarks-for-vapor-1-2-5.md
-rw-r--r-- 2.3K 12 нояб. 2016 · A3940ED · ~2 мин

Swift-бэкенд с CouchDB: Kitura vs. Vapor vs. Node.js

swift backend vapor kitura ubuntu

Я написал несколько тестовых проектов, реализующих API для моего простого приложения, чтобы прогнать бенчмарки. Они делают всего один запрос к базе данных и возвращают JSON.

Все проекты гонялись на VPS Linode в Лондоне:

  • 2 ГБ RAM
  • 1 ядро CPU
  • SSD
  • 125 Мбит/с исходящего трафика
  • Ubuntu 16.10
  • MySQL и CouchDB с одинаковыми данными (около 13к строк/документов)

Сами бенчмарки прогонялись с другого, более мощного выделенного сервера в Германии при помощи wrk:

wrk -t4 -c20 -d5m https://my_url

Результаты сильно отличаются от бенчмарков Райана Коллинза.

Что я попробовал:

  • Node.js 7.0 + MySQL 5.7.16
  • Node.js 7.0 + CouchDB 2.0 (через node-couchdb)
  • Vapor 1.1.11 + CouchDB 2.0 (через HTTP)
  • Kitura 1.1.1 + CouchDB 2.0 (через Kitura-CouchDB)

Всего запросов

Запросов в секунду

Средняя задержка

Результаты для меня очень разочаровывающие. Node.js оказался на 50% быстрее Swift. Похоже, что писать бэкенды на Swift пока ещё рановато — разве что вы не ждёте высокой нагрузки и просто хотите писать на Swift.

UPDATE: примерно через месяц я прогнал ещё одни тесты для новой версии Vapor (1.2.5) против Node.js, и Vapor оказался очень-очень быстрым. Так что теперь я смотрю на использование Swift в качестве бэкенда оптимистично.

[↵] открыть пост swift-backend-with-couchdb-kitura-vs--vapor-vs-node-js.md
-rw-r--r-- 3.4K 9 нояб. 2016 · 101665E · ~2 мин

Фреймворк Vapor для бэкэнда на Swift

swift vapor backend мечты о fullstack

Vapor logo

В предыдущем посте я описывал свои впечатления от 4 фреймворков на Swift. Точнее написал я про 3, а 4 меня заинтересовал и руки до него дошли только сейчас. В кратце - я в восторге. Он прекрасен.

В первую очередь у проекта хорошая документация. Не идеальная, но очень хорошая. С примерами, обучающими материалами. Всё в одном месте на сайте, класс.

Во-первых, у фреймворка есть консольная тулза, которая позволяет очень просто создавать новые проекты, запускать существующие, билдить, деплоить на разные сервисы, и даже создавать Docker-контейнер. По-моему это офигенно, потому что когда начинаешь кодить какой-то, даже тестовый проект, сразу вспоминаешь разные тулзы из мира Node.js и хочется тут иметь что-то подобное.

Во-вторых, набор библиотек у фреймворка отличный. Тут и HTTP-клиент, и обёртки для баз данных, и поддержка WebSocket, шаблоны, по умолчанию предлагается хорошая структура проекта, всё по папочкам. Сразу из коробки отличная работа с JSON и данными, которые можно перегонять и из JSON, и в JSON. Даже есть механизм для локализации.

В-третьих, мне удалось сделать свой мини-клиент для CouchDB через просто HTTP-запросы, и он работает. Пока только на чтение, на запись ещё не успел проверить, но то, что всё заработало, очень понравилось.

В-четвёртых, по умолчанию идёт логирование всех запросов, примерно как это сделано у Express.js. То есть ты сделал роутинг, и в консоли видишь куда приходят запросы, хотя бы понимаешь что происходит. К тому же в комплекте идёт тулза для логирования.

В-пятых - весь мой небольшой тестовый проект скомпилился и заработал на Linux без проблем и падений. Компилился долго, но зато работает, это классно.

По памяти пока всё хуже, чем у Perfect. Perfect стартовал с 3.5 мб памяти, дорос до 5 мб. Vapor стартует с 11 мб памяти, растёт до 14.9 мб. Посмотрим что будет дальше, пока это всё равно в 2 раза меньше, чем у Node.js.

Пожалуй, пока на Vapor я и остановлюсь, по-моему он прекрасен :)

[↵] открыть пост frejmvork-vapor-dlya-bekenda-na-swift.md
-rw-r--r-- 14K 6 нояб. 2016 · 9B2B8F3 · ~7 мин

Фреймворки для серверного Swift

swift backend perfect kitura zewo vapor мечты о fullstack

Пару месяцев назад мне попался пост про серверные фреймворки для Swift - сравнение производительности 4-х фреймворков на Swift для сервера между собой и с Node.js. Производительность меня заинтересовала. Судя по результатам, лучшим оказался Perfect. Через месяц автор выложил ещё одно сравнение производительности, но тестировал уже не в macOS, а в Linux. И снова Perfect оказался в лидерах.

На этих выходных я решил как следует его потрогать. Перед этим я бегло посмотрел в 4 сравниваемых фреймворка: Perfect, Kitura, Vapor и Zewo. Требований у меня к фреймворку было не много, кроме стандартного роутинга запросов и выдачи ответов, мне нужно:

  • Поддержка шаблонизаторов, чтобы отдавать не только JSON, но и HTML.
  • Поддержка сетевых запросов, т.к. в самом Foundation ничего для сетевых запросов ещё не готово.
  • Желательно, чтобы были готовые библиотеки для работы с разными БД.

Естественно, всё это нужно под Linux.

[↵] открыть пост frejmvork-perfect-dlya-servernogo-swift.md
-rw-r--r-- 611B 28 окт. 2016 · 7DC1233 · ~1 мин

Анимация-«тряска» для UIView

шпаргалки swift
// MARK: - Расширение UIView (или NSView)
extension UIView {

    /// Анимация-«тряска»
    func shake() {
        let animation = CAKeyframeAnimation(keyPath: "transform.rotation")
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        animation.duration = 1.0
        animation.values = [-Double.pi / 6, Double.pi / 6, -Double.pi / 6, Double.pi / 6, -Double.pi / 7, Double.pi / 7, -Double.pi / 8, Double.pi / 8, 0.0 ]

        layer.add(animation, forKey: "shake")
    }

}

myView.shake()

[↵] открыть пост shake-effekt-dlya-uiview.md
-rw-r--r-- 5.2K 29 сент. 2016 · E72E8DF · ~3 мин

Про переход на Swift 3 и Swift 2.4

swift

За последние пару недель состоялся релиз новых версия macOS, iOS, tvOS и watchOS. Вместе с ними вышел новый Xcode и новая версия Swift.

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

Сам проект конвертировать труда не составляет, ибо есть встроенный в Xcode конвертер. Я решил сначала попробовать 2.3, чтобы не углубляться в дебри изменений. Рабочий проект сконвертировался без проблем, а потом начался ад с зависимостями. Я пользуюсь Carthage, все зависимости компилируются в .framework, который ты импортируешь в свой проект. 1 раз импортировав, потом уже сам проект трогать не приходится, просто пересобирать зависимости. В первую неделю после релиза многие разработчики быстро выкатили обновления - и релиз на Swift 2.3, и релиз на Swift 3. Но не все. Например, SwiftyJSON - разработчики на какое-то время пропали, а их фреймворк очень популярен. Пришлось копать - те зависимости, которые не обновились, благо, подхватили сторонние разработчики. Они их форкнули, всё перевели на Swift 2.3 и отправили pull request'ы в оригинальные репозитории.

Пришлось потратить какое-то время и заменить некоторые зависимости на форки. И ура, проект собрался наконец. В течение следующей недели авторы всех зависимостей, которые я использую, уже выкатили релизы для Swift 2.3. Я с чистым сердцем вернул зависимости на оригинальные.

Само собой, есть смысл побыстрее перейти на Swift 3, потому что 2.3 - версия временная и поддержка её в будущих релизах Xcode исчезнет. Да и 3-й Swift обещают по возможности последней версией, которая сломает совместимость с предыдущими версиями.

Опять же - сам проект без особых проблем сконвертировался в Swift 3. Не всё, но многое. А то, что само не конвертнулось, Xcode сам при компиляции предлагал исправить, в основном надо было только тыкать мышкой и разрешать ему это поправить. Хоть я и дождался, когда все зависимости обновятся на Swift 3, пришлось немного попотеть. К слову - если framework скомпилен в Swift 2.3, то в проект на Swift 3 его нельзя включить. И обратно тоже. Разгрёб разные проблемы - начался гемор другой. AlamoFire для Swift 3 не просто обновился, а мажорно обновился - с переименованием классов и изменением API. В итоге пришлось некоторые методы вообще переписать на две трети. Хотя в большинстве случаев просто пришлось поменять местами аргументы и где-то что-то переименовать. Плюс, теперь чтобы передать get-параметры, нужно их ручками в строку URL добавлять, раньше это мог сделать аргумент parameters, но теперь эти данные шлются в теле запроса, как будто мы форму заполнили.

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

P.S. опенсорсная версия Swift 3, которую можно запускать на Linux, всё ещё не имеет полной реализации классов для работы с сетью. А фреймворк для бэкенда Perfect не компилится в Ubuntu 16.04. Пока Swift для бэкенда опять откладывается.

[↵] открыть пост pro-perehod-na-swift-3-i-swift-2-4.md
-rw-r--r-- 1.1K 26 сент. 2016 · 55D7D8F · ~1 мин

Элегантный extension для Notification.Name в Swift 3

swift шпаргалки

В новой версии Swift 3 теперь другой конструктор для NSNotification (теперь уже просто Notification, префикс NS отбросили):

struct Config {
    static let ShouldCloseBrowserNotification = "ShouldCloseBrowserNotification"
}

let notification = Notification(
    name: Notification.Name(rawValue: Config.ShouldUpdateDialogNotification),
    object: nil,
    userInfo: nil
)
NotificationCenter.default.post(notification)

Выглядит параметр name не очень то красиво теперь. Зато можно расширить Notification.Name, чтобы было красиво:

extension Notification.Name {
    static let shouldCloseBrowserNotification = Notification.Name("ShouldCloseBrowserNotification")
}

let notification = Notification(
    name: .ShouldUpdateDialogNotification,
    object: nil,
    userInfo: nil
)
NotificationCenter.default.post(notification)

Примерно так же сделано и в самом UIKit:

Код UIKit

[↵] открыть пост elegantnyj-extension-dlya-notification-name-v-swift-3.md
-rw-r--r-- 763B 18 авг. 2016 · 14091F5 · ~1 мин

Блок (замыкание) как свойство класса в Swift

swift шпаргалки

Пример использования блока (замыкания) как свойства класса в Swift.

class ChatViewController: UIViewController {

    private var someClosure: (() -> Void)?
    private var anotherClosure: ((arg: Double) -> Bool)?

    func executeClosures() {
        if self.someClosure != nil {
            self.someClosure()
        }

        if self.anotherClosure != nil {
            let boolResult = self.anotherClosure(arg: 2.0)
        }
    }

    func addSelfClosure(closure: (() -> Void)!) {
        self.someClosure = closure
    }

    func printSomething() {
        self.addSelfClosure() {
            print("closure called")
        }
        self.executeClosures() // prints: closure called
    }

} 
[↵] открыть пост blok-zamykanie--kak-svojstvo-klassa-v-swift.md
-rw-r--r-- 3.1K 12 авг. 2016 · EC7C9DE · ~2 мин

Кастомный бейдж для UITabBarController на Swift

swift шпаргалки uitabbarcontroller

Шёл 2016 год, а для того, чтобы изменить цвета для бейджа внутри таба всё ещё необходимо создавать свой UI-элемент и добавлять его в таббар вручную. Работающее решение на Swift:

// MARK: - UITabBarController extension for badges
extension UITabBarController {

    /**
    Set badges

    - parameter badgeValues: values array
    */
    func setBadges(badgeValues: [Int]) {

        for view in self.tabBar.subviews {
            if view is CustomTabBadge {
                view.removeFromSuperview()
            }
        }

        for index in 0...badgeValues.count-1 {
            if badgeValues[index] != 0 {
                addBadge(
                    index: index,
                    value: badgeValues[index],
                    color:UIColor.yellowColor(),
                    font: UIFont.systemFontOfSize(11)
                )
            }
        }

    }

    /**
    Add badge for tab

    - parameter index: index of tab
    - parameter value: badge value
    - parameter color: badge color
    - parameter font: badge font
    */
    func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
        let badgeView = CustomTabBadge()

        badgeView.clipsToBounds = true
        badgeView.textColor = UIColor.whiteColor()
        badgeView.textAlignment = .Center
        badgeView.font = font
        badgeView.text = String(value)
        badgeView.backgroundColor = color
        badgeView.tag = index
        tabBar.addSubview(badgeView)

        self.positionBadges()
    }

    override public func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.tabBar.setNeedsLayout()
        self.tabBar.layoutIfNeeded()
        self.positionBadges()
    }

    /**
    Positioning
    */
    func positionBadges() {

        var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
            return view.userInteractionEnabled // only UITabBarButton are userInteractionEnabled
        }

        tabbarButtons = tabbarButtons.sort({ $0.frame.origin.x < $1.frame.origin.x })

        for view in self.tabBar.subviews {
            if view is CustomTabBadge {
                let badgeView = view as! CustomTabBadge
                self.positionBadge(badgeView, items:tabbarButtons, index: badgeView.tag)
            }
        }
    }

    /**
    Position for badge

    - parameter badgeView: badge view
    - parameter items: tab bar buttons array
    - parameter index: index of tab
    */
    func positionBadge(badgeView: UIView, items: [UIView], index: Int) {

        let itemView = items[index]
        let center = itemView.center

        let xOffset: CGFloat = 12
        let yOffset: CGFloat = -14
        badgeView.frame.size = CGSizeMake(17, 17)
        badgeView.center = CGPointMake(center.x + xOffset, center.y + yOffset)
        badgeView.layer.cornerRadius = badgeView.bounds.width/2
        tabBar.bringSubviewToFront(badgeView)
    }

}

/// Custom UILabel class for badge view
class CustomTabBadge: UILabel {}
[↵] открыть пост kastomnyj-bejdzh-dlya-uitabbarcontroller-na-swift.md
-rw-r--r-- 1.2K 9 авг. 2016 · FDC24F3 · ~1 мин

Функция задержки на Swift

swift шпаргалки

Очень приятный синтаксический сахар - функция для запуска кода с задержкой через Grand Central Dispatch с помощью dispatch_after:

Update 2024. Swift 5.5+

Если нужна задержка внутри async-функции:
func someMethod() async throws {
    // sleep for 2 seconds
    try await Task.sleep(nanoseconds: NSEC_PER_SEC * 2)
}

Swift 2:

/**
Delay function using GCD. Syntax sugar.

- parameter delay: delay in seconds
- parameter closure: closure code to execute after delay
*/
func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
    dispatch_get_main_queue(), closure)
}

Swift 3:

/**
Delay function using GCD. Syntax sugar.

- parameter delay: delay in seconds
- parameter closure: closure code to execute after delay
*/
func delay(_ delay: Double, closure:()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

Использование:

delay(0.4) {
    // код
}
[↵] открыть пост funktsiya-zaderzhki-na-swift.md
-rw-r--r-- 877B 14 июня 2016 · B548F4B · ~1 мин

Модальный UIVIewController с прозрачностью

swift шпаргалки

Шпаргалка - как запрезентить UIViewController поверх другого так, чтобы он был полупрозрачный и под ним просвечивал контент.

Выставляем в Storyboard для Segue поле Kind как «Present Modally». Для UIViewController, который показываем, выставляем цвет фона Clear Color, делаем внутри фон (картинка или другой UIView) со значением alpha, например, 0.5.

В коде:

self.performSegueWithIdentifier("segueName", sender: self)

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "segueName" {
        let vc = segue.destinationViewController as! YourViewController
        vc.modalPresentationStyle = .Custom
    }
}
[↵] открыть пост modal-nyj-uiviewcontroller-s-prozrachnost-yu.md
-rw-r--r-- 1.8K 12 мая 2016 · 6FA87ED · ~1 мин

Тестирование асинхронного кода в XCTest

swift xctest тесты шпаргалки

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

В ходе гугления оказалось два решения - одно через семафоры из Grand Central Dispatch, воторое с помощью expectations. С expectations мне понравилось больше. Пример теста:

import XCTest

class apiClientTests: XCTestCase {
    override func setUp() {
        super.setUp()
    }

    override func tearDown() {
        super.tearDown()
    }

    /**
    SampleTest
    */
    func testSampleAsyncRequest() {
        let exp = expectationWithDescription("\(#function)\(#line)")

        APIClient.sharedInstance.someMethod { (responseObject, error) in
            XCTAssertNotNil(responseObject, "responseObject should not be nil")
            XCTAssertNil(error, "error should be nil")

            exp.fulfill()
        }

        waitForExpectationsWithTimeout(40) { (error) in
            if error != nil {
                XCTAssertTrue(false)
            }
        }
    }
}

P.S. - если хочешь, чтобы тесты запускались в определённом порядке - то надо помнить, что они запускаются тупо в алфавитном порядке. То есть надо именовать тесты test1Name, test2Name и т.д.

[↵] открыть пост testirovanie-asinhronnogo-koda-v-xctest.md
-rw-r--r-- 4.8K 12 мая 2016 · 46A3DE7 · ~3 мин

Про использование Swift на сервере

swift ubuntu

Начал пробовать использовать Swift на сервере для бэкенда. Если с макосью всё довольно понятно в плане как начать использовать Swift (да хотя бы тот же Xcode), то с Linux не так всё очевидно оказалось.

На сайте Swift.org есть инструкция для OS X и для Ubuntu (14.04 и 15.04). Сразу обрадовался, что они выкладывают уже скомпилированные бинарники. Вкратце - скачиваем архив, скачиваем всякие ключи и подписи, проверяем подпись архива на всякий случай, распаковываем архив и делаем export чтобы из консоли можно было выполнить команду. Например

swift --version
Swift version 2.2.1-dev (LLVM da67bff217, Clang 81d0486fb2, Swift 82adb8fc96)
Target: x86_64-unknown-linux-gnu

Начинаем какой-нибудь простой Hello World - просто создаём проектик, который в консоль выводит текст, что-то вроде:

import Foundation

print("hello world")

Далее надо проверить - работает ли оно. Выполняем:

swift build
error: unable to invoke subcommand: /home/webserver/swift-2.2.1-SNAPSHOT-2016-04-23-a-ubuntu14.04/usr/bin/swift-build (No such file or directory)

Сюрприз! В релизную версию Swift версии 2.2.1 не включён swift-build. То есть собрать проект релизной версии мы не можем. Слегка офигеваешь от этого. Гуглишь - оказывается, надо дев-версию Swift скачивать. А это предрелизная версия Swift 3.0.

В ней всё есть, проект собирается, бинарник запускается и выводит текст в консоль. Но как-то это странно, что туториал с сайта не работает на релизной версии. Хотя пишут, что в предыдущих релизах он был.

Самое грустное - что пока даже сетевые запросы нельзя слать из опенсорсного Swift - в Foundation не реализован NSURLRequest, значит мои мысли о том, чтобы юзать CouchDB через его REST API пока можно отложить. И на гитхабе пока красуется надпись в описании текущего состояния Swift:


NSURLSession and related classes are not yet implemented.

Можно, конечно, сишные библиотеки использовать. Например, IBM используют обёртку на CURL под названием CCurl. Но хочется как-то из коробки, средствами языка. Поэтому, пожалуй, подожду, когда это наконец сделают.

Мои ковыряния с web-фреймворком Swift Express показали, что сделать на нём сайт или REST API можно. Я вчера вечером даже сделал заготовку для переписывания этого блога - готовы шаблоны, роутинг, выводится всё оформление сайта, осталось подставлять данные из базы данных. И теперь стоит вопрос - как доставать данные из базы. Есть обёртки над сишными библиотеками для MySQL и MongoDB, но мне вот всё хочется продолжать использовать CouchDB, раз уж я им итак пользуюсь во многих проектах. А у него REST API, то есть доставать данные надо через сетевые запросы, которые в Foundation пока не завезли, а использовать сторонние инструменты как-то не хочется, потому что потом, когда всё реализуют, будет логичнее перейти на встроенные инструменты, то есть придётся выпиливать. Подожду пока с переписыванием своего бложека на Swift :)

[↵] открыть пост pro-ispol-zovanie-swift-na-servere.md
-rw-r--r-- 1.3K 11 мая 2016 · 61AD73C · ~1 мин

Заметка про Socket.IO-Client-Swift

socket.io swift шпаргалки

Небольшая шпаргалка - для socket.io есть клиент на Swift. Казалось бы, на гитхабе хорошо расписано, как его использовать. Вот только к моему бэкенду на Node.js он никак не хотел подключаться с кодом примеров.

Логи показывали (если пробовать через polling-запросы), что клиент стучится по адресу /engine.io и получает 404 ошибку. Изучение браузерной Javascript-библиотеки, которая с этим же бэкендом работает без каких-либо танцев с бубном, показало, что надо указать другой путь - /socket.io. Благо, для этого есть специальная опция.

Итого, получаем:

let socketURL = NSURL(string: "http://localhost:3000")
let socket = SocketIOClient(socketURL: socketURL!, options: [
    .ForceWebsockets(true),
    .Path("/socket.io/"),
    .ConnectParams(["token": "token"])
])

socket.on("connect") {data, ack in
    print("socket connected")
}

socket.connect() 
[↵] открыть пост zametka-pro-socket-io-client-swift.md
-rw-r--r-- 370B 22 апр. 2016 · 4E22F08 · ~1 мин

Конвертирование UTC даты с миллисекундами из String в NSDate на Swift

swift шпаргалки

Чтобы перевести дату из текста в NSDate из немного нестандартного вида с миллисекундами:

let dateString = "2016-04-22T17:42:46.762+03:00"

let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

let myDate = dateFormatter.dateFromString(dateString) 
[↵] открыть пост konvertirovanie-utc-daty-s-millisekundami-iz-string-v-nsdate.md
-rw-r--r-- 1.0K 18 апр. 2016 · E93D4A7 · ~1 мин

Лог-функция для Swift с выводом файла, метода и строки

шпаргалки swift полезное

Функция-шпаргалка для логирования в Swift:

import Foundation

/**
 Лог-функция с выводом файла, метода и строки, откуда вызывается. 
 Пример использования: DLog("привет")

 - parameter messages: тексты/объекты, которые надо вывести
 - parameter fullPath: путь до файла, который вызывается
 - parameter line: номер строки в файле
 - parameter functionName: название метода/функции вызова
*/
func DLog(messages: Any..., fullPath: String = #file, line: Int = #line, functionName: String = #function) {
     let file = NSURL.fileURLWithPath(fullPath)
     for message in messages {
          print("\(file.pathComponents!.last!):\(line) -> \(functionName) \(message)")
     }
}

// Пример:
DLog("message 1", "message 2")
// YourClass.swift:42 -> someMethod() message 1
// YourClass.swift:42 -> someMethod() message 2
[↵] открыть пост log-funktsiya-dlya-swift-s-vyvodom-fajla-metoda-i-stroki.md
makoni@arm1:~/blog$ cd .. // ↵ ко всем постам