Devlog #4: TrackMyLayout Utility

Devlog #4: TrackMyLayout Utility

@CompTalks (Denys Skobalo)

Можна сказати закінчив розробку ядра утиліти. Наступним кроком, буде реалізувати UI, і це буде Swift. В мене досвід з Swift не велекий, але я мав досівд на курсі в університеті. Також, вибір впав на SwiftUI, так як захотів спробувати щось нове тому, що в університеті вивчали Swift+Storyboard, а мене не сильно прикалує робити через UI, надаю перевагу коду.

Перед початком роботи над інтерфейсом, змінив архітектуру проєкта, та провів невеликий рефакторинг. А саме розбив головну функцію агента слідкуваня за активним вікном на дві функції, agetn_active_app_start та agent_active_app_stop , з архітектурних та життєвих причин, а не з естетики. Головна причина це керування життєвим циклом (lifecycle management). Раніше функція startActiveApplication реалізовувала одночасне ініціалізування агента, запускала CFRunLoop, і фактично блокувала потік назавжди. Це нормально для CLI утиліти, але погано для SwiftUI, де:

  • RunLoop уже існує (керується SwiftUI),
  • компоненти мають запускатись і зупинятись реактивно.

Що дали оновлені функції:

agent_active_app_start()

  • створює AXObserver/NSWorkspace observer
  • реєструє callback, та не блокує потік

active_active_app_stop()

  • знімає observer
  • відписується від notifications
  • звільняє ресурси ( AXObserver, CFRunLoopSource, memory)

Це критично важливо, бо у SwfitUI, View може з’являтися і зникати (onAppear / onDisappear), App може переходити у background, та агент не повинен жити вічно без контролю. І також це чіткий контракт API, start починає слухати, stop припиняє слухати. Без сюрпризів, без implict run loop.


Після рефакторингу, відкрив XCode, створив проєкт. Перше що я хотів, це протестувати агента який відслідковує додатки, тобто, щоб було вікно на якому є кнопка Start Agent і в терміналі виводилось активний додаток. Написав базовий код для вікна та використання агента в додатку.


Запустив, отримав warning'и та помилки, все як завжди. Почав розбиратись, створив TrackMyLayout-Bridging-Header.h як міст між Core та UI , щоб використовувати функції з Objective-C/C в Swfit коді, зайшов у Project Settings, підключив міст, переніс ядро в файлову архітектуру додатка. Спроба номер два, очищення кешу білда, заново білд, успішно, запуск, і все готово. Додаток стартує, має кнопку, при натисканні запускається агент, в терміналі аутпут логів. Отже підключення агента до Swift'а пройшло успішно.


Так, як я переніс файли ядра до файлової архітектури додатка, прийшлось трохи переписати main.c , а точніш додати CFRunLoop для того, щоб агент міг реагувати на події в системі без SwiftUI, тобто без GUI, яке саме підтримує RunLoop. CLI не мала б власного циклу обробки подій, і тоді агент просто виконав би функції і завершився, не відстежуючи зміни активного додатку. Додавши CFRunLoop, створюється власний event loop, який слухає події системи, таймери та observer-и, завдяки чому агент працює у real-time та реагує на зміни активного додатку так само, як це відбувається у графічному додатку.


Наступний крок, це реалізувати GUI для утиліти, також розібратись як зробити, щоб утиліти працювало в Menu-bar тобто у фоновому режимі, на кшталт утиліт Rectangle, RunCat тощо. Це поки що всі ідеї, що прийшли мені у голову.

Report Page