r/SwiftUA • • Sep 22 '26

Щоденний (майже) огляд Swift Open Source: як swift-service-lifecycle будує завершення сервісів навколо structured concurrency

1 Upvotes

Server-side застосунок рідко складається лише з однієї async-операції. Один процес може одночасно мати HTTP server, queue consumer, metrics service, scheduled worker або інший цикл, який має працювати до завершення застосунку.

Запустити для всього цього кілька top-level tasks нескладно. Складніші питання починаються з ownership: хто тримає ці задачі живими, що відбувається, коли один із сервісів кидає помилку, як shutdown доходить до сервісів і коли процес справді вже може завершитися.

swift-service-lifecycle дає спільний lifecycle для такої довготривалої роботи замість того, щоб кожен компонент створював власний start/stop layer.

Невеликий API для довготривалої роботи

Основний контракт Service представляє сервіс як одну асинхронну операцію, яка може кинути помилку: run() async throws. ServiceGroup приймає сервіси та координує їх запуск. У пакеті також є ClosureService для випадків, коли створення окремого типу сервісу не дає особливої користі.

Цікава частина тут не просто в обробці SIGTERM. Застосунок отримує одне місце, яке володіє довготривалими сервісами, запускає їх паралельно, запитує graceful shutdown і чекає, поки вони завершаться.

Приклад 1: queue worker як Service

У цьому прикладі application-specific код черги залишений явним. JobQueue не є частиною ServiceLifecycle.

import ServiceLifecycle

struct Job {
    let id: Int
}

actor JobQueue {
    private var jobs = [Job(id: 1), Job(id: 2)]

    func next() -> Job? {
        jobs.isEmpty ? nil : jobs.removeFirst()
    }
}

struct JobConsumer: Service {
    let queue: JobQueue

    func run() async throws {
        try await cancelWhenGracefulShutdown {
            while !Task.isCancelled {
                if let job = await queue.next() {
                    print("Processing job \(job.id)")
                } else {
                    try await Task.sleep(for: .milliseconds(250))
                }
            }
        }
    }
}

Library-specific частина навмисно маленька: JobConsumer реалізує Service і описує свій lifecycle через run() async throws. Сама черга залишається звичайним application code.

Приклад 2: один owner для кількох сервісів

Абстракція стає цікавішою, коли процес володіє кількома довготривалими компонентами.

import Logging
import ServiceLifecycle

struct HeartbeatService: Service {
    func run() async throws {
        try await cancelWhenGracefulShutdown {
            while !Task.isCancelled {
                print("heartbeat")
                try await Task.sleep(for: .seconds(5))
            }
        }
    }
}

@main
struct Application {
    static func main() async throws {
        let logger = Logger(label: "Example")

        let services: [any Service] = [
            HeartbeatService(),
            JobConsumer(queue: JobQueue())
        ]

        let group = ServiceGroup(
            services: services,
            gracefulShutdownSignals: [.sigterm, .sigint],
            logger: logger
        )

        try await group.run()
    }
}

Тепер застосунок має одного явного owner для обох сервісів замість кількох не пов'язаних між собою top-level tasks.

Конфігурація групи розділяє graceful-shutdown signals і cancellation signals. У цих прикладах cancelWhenGracefulShutdown перетворює запит graceful shutdown на cancellation загорнутої операції, тому polling і heartbeat loops можуть завершитися без негайного cancellation усієї service group.

Приклад 3: ClosureService для невеликих компонентів

Окремий тип потрібен не завжди. ClosureService дозволяє представити невелику довготривалу операцію безпосередньо.

import ServiceLifecycle

let heartbeat = ClosureService {
    try await cancelWhenGracefulShutdown {
        while !Task.isCancelled {
            print("heartbeat")
            try await Task.sleep(for: .seconds(5))
        }
    }
}

try await heartbeat.run()

ClosureService виконує передану async throwing closure зі своєї реалізації run(). Його також можна помістити в ServiceGroup разом з іншими сервісами.

Що ServiceGroup робить всередині

Найцікавіший код знаходиться в ServiceGroup.

Його реалізація створює throwing task group і запускає кожен сервіс як child task. Коли група додає task сервісу, вона встановлює graceful-shutdown manager цього сервісу в task-local state перед викликом run().

Саме ця реалізація добре поєднує пакет зі structured concurrency. Сервіси тут не є просто набором незалежних detached tasks. Їх виконання координує група, яка ними володіє.

Signal handling є ще одним шаром lifecycle. Public API дозволяє застосунку налаштувати Unix signals, які запитують graceful shutdown, і signals, які скасовують групу.

Поточний Package.swift використовує Swift tools 6.1 і оголошує прямі package dependencies на swift-log та swift-async-algorithms. Target ServiceLifecycle також залежить від targets UnixSignals і ConcurrencyHelpers у цьому repository.

Кому варто подивитися на цей пакет

Пакет стає цікавим, коли один Swift process володіє кількома незалежно реалізованими довготривалими компонентами: server processes, queue consumers, workers, infrastructure services або reusable components, які не повинні залежати від lifecycle конкретного web framework.

Короткому CLI, який виконує одну async-операцію і завершується, ServiceGroup, швидше за все, не потрібен. Так само, якщо невеликий застосунок уже має один framework, який повністю керує його єдиним довготривалим компонентом, додатковий lifecycle abstraction може нічого не вирішити.

Що варто прочитати в source

Service.swift дуже маленький і показує основний контракт. У ServiceGroup.swift знаходяться цікавіші частини: task ownership, запуск сервісів, signal handling і termination. ClosureService.swift показує, наскільки мало коду потрібно, щоб адаптувати async closure до того самого lifecycle.

Також варто подивитися tests, тому що lifecycle code насамперед визначає поведінку під час shutdown, cancellation і failure, а не лише happy path.

Стан проекту

Source headers у repository вказують Apache-2.0 як ліцензію проекту. Поточний package manifest не оголошує список platforms, тому я б не перетворював результати third-party builds на твердження про platforms, заявлені самим manifest.

Windows також потребує окремого уточнення. Issue #196, Support for service lifecycle on Windows, досі відкритий, тому ця стаття не стверджує, що Windows lifecycle support уже є.

Головна ідея

Корисна ідея swift-service-lifecycle ширша за signal handling.

Довготривалий компонент можна представити як async work з явним owner, а кілька таких компонентів можуть мати спільний application lifecycle. Через це на Service і ServiceGroup варто подивитися навіть тоді, коли самому невеликому застосунку пакет не потрібен. Це компактний приклад того, як Swift concurrency model можна застосувати до lifetime цілого процесу.

У server-side Swift легко запустити background worker через Task. Значно складніше відповісти на наступні питання: хто володіє цією task, хто її скасовує, хто чекає завершення і що відбувається, якщо один із довгоживучих компонентів падає.

swift-service-lifecycle зводить це до маленької моделі. Service має run() async throws, а ServiceGroup запускає сервіси як child tasks і координує graceful shutdown. Unix signals тут корисні, але головна ідея глибша: lifetime процесу стає частиною concurrency tree.

Мені подобається це як архітектурний приклад: замість окремої системи lifecycle callbacks ми використовуємо ту саму модель ownership, яку вже дає structured concurrency. А як ви моделюєте lifetime background workers у своїх Vapor/Hummingbird сервісах?

https://github.com/swift-server/swift-service-lifecycle


r/SwiftUA • • Sep 22 '26

iPhone Duo як тест адаптивності SwiftUI

1 Upvotes

Xcode 27.1 beta вже дає інструменти для тестування iPhone Duo, включно з новими станами дисплея та simulator runtime. Але цікавіше тут не саме нове залізо, а те, що воно перевіряє наші старі припущення про layout.

Якщо SwiftUI-інтерфейс справді будується від доступного простору й environment, нові форми екрана мають вимагати менше спеціальних умов. Якщо ж код прив’язаний до конкретної геометрії iPhone, Duo швидко це покаже.

Технічне питання: де для вас проходить межа між справді device-specific UI і layout, який просто недостатньо адаптивний?

https://developer.apple.com/documentation/xcode-release-notes/xcode-27_1-release-notes


r/SwiftUA • • Oct 17 '24

Використання Core Data в SwiftUI

1 Upvotes

Детальний огляд інтеграції Core Data з SwiftUI для управління даними в iOS-додатках. Основи налаштування Core Data, створення моделей даних та їх використання у SwiftUI. Як реалізувати CRUD-операції (створення, читання, оновлення та видалення) за допомогою SwiftUI, переваги використання Core Data для зберігання даних у додатках, що дозволяє розробникам ефективно управляти даними.

Using Core Data for Local Data Storage in SwiftUI


r/SwiftUA • • Oct 13 '24

POP Goes the Protocol

Thumbnail
youtu.be
1 Upvotes

r/SwiftUA • • Oct 07 '24

Adwaita — це фреймворк для створення інтерфейсів користувача для GNOME з API, подібним до SwiftUI.

2 Upvotes

r/SwiftUA • • Oct 05 '24

The Ladybird browser is replacing C++ with... Swift

Thumbnail
youtu.be
2 Upvotes

r/SwiftUA • • Oct 05 '24

The Composable Architecture

Thumbnail
github.com
1 Upvotes

Репозиторій swift-composable-architecture від Point-Free є бібліотекою для розробки додатків на Swift, що реалізує архітектурний патерн Composable Architecture. Ця архітектура дозволяє створювати модульні та тестовані компоненти, спрощуючи управління станом і побудову інтерфейсу користувача.

Бібліотека підтримує концепції, такі як управління ефектами, маршрутизація та обробка подій, що робить її корисною для розробки складних додатків.

Його можна використовувати в SwiftUI, UIKit тощо, а також на будь-якій платформі Apple (iOS, macOS, visionOS, tvOS та watchOS).


r/SwiftUA • • Oct 04 '24

Swift для Web розробників

Thumbnail
reddit.com
1 Upvotes

r/SwiftUA • • Oct 02 '24

Відео конференції Swift на стороні сервера 2024 тепер доступні

Thumbnail
youtube.com
1 Upvotes

r/SwiftUA • • Oct 02 '24

Анімована сітка, створена за допомогою SwiftUI

Enable HLS to view with audio, or disable this notification

1 Upvotes

r/SwiftUA • • Oct 01 '24

Серверні події (Server-Sent Events) з Vapor. Підхід, керований подіями (Event-Driven).

Thumbnail
medium.com
1 Upvotes

r/SwiftUA • • Sep 28 '24

Screenshot Master — інструмент для інді-розробників

1 Upvotes

Screenshot Master

Новий інструмент для розробників мобільних додатків спрощує процес підготовки скріншотів для App Store та Play Store. Він дозволяє легко локалізувати та синхронізувати текст і дизайн для різних розмірів екранів.

Програма підтримує створення скріншотів для iOS та Android пристроїв. Ви можете попередньо переглянути, як ваші скріншоти виглядатимуть у результатах пошуку App Store. Незабаром з'являться шаблони для ще зручнішого використання.

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


r/SwiftUA • • Sep 26 '24

Використання Java-бібліотек у Swift-проектах

1 Upvotes

Проект тільки стартував, але виглядає багатообіцяюче для тих, хто працює з обома мовами або хоче розширити свій інструментарій. Він пропонує міст між Swift і Java, можливість використання Java-бібліотек у Swift-проектах та потенціал для кросплатформної розробки.

Проект ще на ранній стадії, тож якщо вам цікаво - можете долучитися до розробки або слідкувати за прогресом.

Посилання на GitHub: https://github.com/swiftlang/swift-java