# macOS

## Що це таке

macOS — це операційна система, яку Apple постачає на кожному Mac: робочий стіл і
Finder, вбудовані програми, а під ними система Unix із терміналом, оболонкою та
звичними інструментами командного рядка. Apple створює її для власного
обладнання й продає їх разом, тож на практиці обрати macOS означає обрати Mac.

Основа системи — Darwin, вихідний код якого Apple публікує. Її ядро, XNU, —
гібрид, що поєднує ядро Mach з Університету Карнегі-Меллона з компонентами
FreeBSD і драйверним фреймворком на C++ під назвою IOKit; назва розшифровується
як «X is Not Unix». Значна частина користувацького простору теж походить із
FreeBSD, і сторінки довідки для `sed` і `date` на Mac описують саме версії цих
інструментів із FreeBSD. Починаючи з macOS 10.15 Catalina, оболонкою за
замовчуванням є zsh.

Нещодавно змінилося й обладнання. Наприкінці 2020 року Apple почала переводити
Mac із процесорів Intel на власні чипи, Apple silicon, і тепер кожна лінійка
Mac, від MacBook Air до Mac Pro, має моделі на Apple silicon. Ці чипи виконують
код arm64, і це стає важливим, щойно програму, зібрану на Mac, треба запустити
на сервері x86-64.

На ноутбуках і настільних комп'ютерах macOS порівнюють із Windows і Linux.

## Чому це важливо

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

Як робоча станція Mac відрізняється від серверів Linux, на які розгортають код,
у трьох місцях. Інструменти командного рядка походять із FreeBSD, а не з GNU,
тож той самий прапорець у скрипті може означати різне. Процесор — arm64, тоді як
сервер може бути x86-64. І ядра Linux на Mac немає, а контейнери використовують
ядро свого хоста, тому інструменти для контейнерів на Mac спершу запускають
віртуальну машину з Linux; власний інструмент Apple, `container`, якому потрібні
Apple silicon і macOS 26, дає кожному контейнерові окрему легку віртуальну
машину. Це не причина відмовлятися від Mac, а причина тестувати на тій системі,
куди розгортаєте, замість довіряти зеленому запуску на ноутбуці.

Як платформа macOS ставить питання, як під неї будувати. Кросплатформна
оболонка, тобто одна кодова база з вбудованим браузерним рушієм, яку постачають
для Windows, Linux і Mac, дешево тримає кілька систем узгодженими, але на Mac
вона тягне за собою середовище виконання браузера, непотрібне нативній програмі,
і малює власні елементи керування замість системних. Нативна програма на Swift і
SwiftUI використовує системні елементи керування й матеріали та без додаткового
коду йде за налаштуваннями користувача: виглядом, акцентним кольором,
контрастом, розміром тексту і зменшенням руху. Ціна — окремий клієнт і люди, які
знають платформу. Нативний шлях окупається, коли клієнти — це користувачі Mac.

Постачання на Mac проходить ще й через контроль. Програми, які поширюють поза
Mac App Store, підписують сертифікатом Developer ID і надсилають до служби
нотаризації Apple — автоматичної перевірки на шкідливий вміст і проблеми з
підписом коду, яка є окремою від App Review. Починаючи з macOS 10.15, усе
програмне забезпечення, зібране після 1 червня 2019 року й поширюване з
Developer ID, має бути нотаризоване. Apple повертає квиток, який прикріплюють до
програми, і публікує той самий квиток онлайн, де Gatekeeper шукає його, коли
користувач уперше відкриває програму. Сертифікат дає членство в Apple Developer
Program за 99 доларів на рік. Закладіть членство й крок підписування в перший
реліз.

## Як це виглядає на практиці

Настільна версія одного продукту для обміну повідомленнями на macOS була
кросплатформною оболонкою, яка й виглядала, і поводилася інакше, ніж програма
для Mac. Добре випробувана основа продукту, написана на Rust, має C-інтерфейс, а
Swift уміє викликати C, тож
[нативний клієнт на Swift і SwiftUI](/uk/portfolio/built-a-native-macos-messaging-client-in-swift-6-154/)
збудували безпосередньо на цьому інтерфейсі. Основу вкомпоновано як статичний
архів із закріпленої висхідної ревізії, що робить збирання відтворюваним, а
тонка обгортка володіє кожним вказівником, який перетинає межу між мовами.
Клієнт запускається як програма для Mac, використовує власні матеріали й
елементи керування платформи і не містить вбудованого браузера. Сторінка чесно
називає прогалину: немає ні нотаризованого розповсюдження, ні каналу оновлень,
тож це робоча програма, а не продукт, яким користуються інші. Саме описаний вище
крок нотаризації стоїть між цими двома станами.

Вигляд платформи можна закріпити правилом, а не наслідуванням.
[Система дизайну з 48 іменованих токенів](/uk/portfolio/built-a-48-token-design-system-160/)
у шести групах будує колір і матеріал на семантичних кольорах платформи та її
скляному матеріалі замість сталих значень, тож інтерфейс іде за системним
виглядом, акцентним кольором і налаштуваннями контрасту без коду, який би за
ними стежив, а текстові стилі масштабуються з розміром, який обрав користувач.
Лінтер відхиляє голе число там, де має бути токен, жорстко заданий розмір шрифту
й анімацію, що ігнорує налаштування зменшеного руху. Сторінка називає й свою
межу: токени роблять інтерфейс узгодженим, а це не те саме, що зробити його
добрим.

Як macOS поводиться з пам'яттю, показала
[робота над застосунком для macOS](/uk/portfolio/stopped-an-application-filling-memory-at-41mb-a-second-155/),
де програма росла приблизно на 41 МБ за секунду, доки операційна система її не
завершила. Компресор пам'яті macOS у зафіксованому інциденті тримав 111 ГБ
стиснутих сторінок, а фізично машина мала 36 ГБ: стиснення й підкачування дали
програмі вирости далеко за цю межу, і коли настало примусове завершення, решта
робочого столу вже перестала відповідати. Три причини множили одна одну:
необмежений потік подій, передплатники, кожен із яких отримував кожну подію, і
журналювання без бюджету. Обмежені буфери, передплата за типом події та бюджет
швидкості на журналювання виправили всі три, а регресійний тест утримує стелю
пам'яті. Сеанс, який писав 610 996 рядків журналу, тепер пише 1 411, і сторінка
прямо називає ціну: коли щось швидко йде не так, журнал навмисно неповний.

## Де виникають проблеми

**Монітор активності показує майже повністю зайняту пам'ять на Mac, який працює
цілком нормально.** macOS кешує нещодавно використані файли в пам'яті, яка
інакше простоювала б, а коли пам'ять заповнюється, стискає неактивні програми,
щоб звільнити місце для активних. Тому використана пам'ять, близька до фізичної,
і великий обсяг стиснутої — це норма. Сама Apple пише, що вільна пам'ять ще не
означає вищої продуктивності комп'ютера. Дивіться натомість на графік
«Навантаженість пам'яті»: зелений означає, що оперативна пам'ять
використовується ефективно, жовтий — що згодом комп'ютеру може знадобитися
більше, червоний — що її потрібно більше. Найпростіший сигнал — підкачування:

```sh
sysctl vm.swapusage
vm_stat | grep -E '^Swap(ins|outs)'
```

Лічильники `vm_stat` ідуть від завантаження системи, тож порівнюйте два
зчитування з інтервалом у хвилину; якщо swapouts і далі зростають, роботі вже
затісно в оперативній пам'яті.

**Скрипт, який працює на Mac, падає з дивною помилкою на сервері Linux, або
навпаки.** Похідний від FreeBSD `sed` на Mac сприймає слово після `-i` як суфікс
резервної копії, тож звичний для GNU виклик `sed -i 's/a/b/' file` бере вираз за
суфікс, а ім'я файлу — за скрипт. Звичка з Mac, `sed -i '' …`, змушує GNU `sed`
читати вираз як ім'я файлу. Суфікс, записаний разом із прапорцем, працює в обох
системах:

```sh
sed -i.bak 's/^port=80$/port=8080/' app.conf && rm app.conf.bak
```

Надійне рішення — запускати скрипти в CI на тій системі, з якою вони
зустрінуться в робочому середовищі.

**Збірку, яку передали за межі команди, блокує Gatekeeper, щойно отримувач її
відкриває.** Програму підписано для розробки або підписано, але не нотаризовано.
Підпишіть її сертифікатом Developer ID з увімкненим hardened runtime і захищеною
позначкою часу, надішліть до служби нотаризації й прикріпіть квиток, щоб
Gatekeeper знаходив його навіть без мережі. ZIP-архів можна нотаризувати, але
прикріпити до нього квиток не вийде, тож прикріплюйте його до програми всередині
архіву:

```sh
ditto -c -k --keepParent App.app App.zip
xcrun notarytool submit App.zip --keychain-profile notary --wait
xcrun stapler staple App.app
```

**Образ контейнера працює на Mac розробника й падає на сервері з `exec format
error`.** Docker за замовчуванням збирає під платформу демона BuildKit, який
виконує збирання, а на Mac з Apple silicon цей демон працює у віртуальній машині
arm64. Контейнер використовує ядро хоста, тож без емуляції хост x86-64 виконує
лише код x86-64. Перевірте сервер командою `uname -m`, а потім збирайте під
нього з `docker build --platform linux/amd64` або збирайте в CI на платформі
сервера.

## Що почитати далі

- [Mac User Guide](https://support.apple.com/guide/mac-help/welcome/mac)
  — власний посібник Apple з macOS, який оновлюють для кожного актуального
  випуску.
- [Check if your Mac needs more RAM](https://support.apple.com/guide/activity-monitor/check-if-your-mac-needs-more-ram-actmntr34865/mac)
  — графік навантаженості пам'яті і що означають три його кольори.
- [Notarizing macOS software before distribution](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution)
  — Developer ID, hardened runtime і як Gatekeeper використовує квиток.
- [Multi-platform builds](https://docs.docker.com/build/building/multi-platform/)
  — чому хостам arm64 і amd64 потрібні різні образи і як одне збирання дає
  обидва.
- [container](https://github.com/apple/container)
  — інструмент Apple для запуску контейнерів Linux на Mac, кожного в окремій
  легкій віртуальній машині.
- [XNU](https://github.com/apple-oss-distributions/xnu)
  — вихідний код ядра macOS із коротким поясненням, як поєднуються Mach, FreeBSD
  та IOKit.

<https://engineer.company/uk/glossary/macos/>
