Мой первый проект на Swift

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

Я рассказал о цели этого проекта в этом посте, но вкратце напомню, что это приложение для macOS, предназначенное для моей предыдущей работы в качестве специалиста по поддержке ИТ. Приложение предназначалось для экономии времени, отображая соответствующую информацию о пользователе, которому мы помогали (его права доступа, лицензии на программное обеспечение и т. Д.).

Вот как это выглядело в конце:



Он называется LionSearch (Lion был логотипом нашей компании, и многие внутренние службы были названы в его честь).

Некоторые из его особенностей:

  • Запускается мгновенно, без загрузки
  • Мгновенный поиск любого пользователя в организации (более 60 000 пользователей)
  • Автозаполнение при вводе имени пользователя (нет необходимости запоминать полное имя пользователя)
  • Позвоните пользователю через Skype, нажав прямо на его номер телефона.
  • Отфильтруйте членство в группах, чтобы найти конкретную информацию (например, доступ к VPN)

Начиная

Идея этого приложения основана на сценарии bash, написанном одним из моих коллег. Скрипт опрашивает Active Directory и получает информацию о пользователе, которому я помогаю. Когда я изучал Swift, я начал думать, как было бы здорово иметь легкое приложение для Mac, которое позволяет мне искать любого пользователя в компании и отображать всю необходимую ИТ-информацию в одном централизованном месте.

Для начала мне пришлось изучить несколько тем:

  • Как мне создать приложение для Mac? (на данный момент я едва мог делать приложения для iOS)
  • Как я мог взаимодействовать с Active Directory через Swift?
  • Как мне отфильтровать информацию, которую я получаю из Active Directory, чтобы получить что-то полезное? (он возвращает массивный текстовый файл для каждого пользователя)

Я начал решать эти вопросы один за другим. К счастью, оказалось, что macOS не сильно отличается от iOS. Многие элементы, с которыми я был знаком, имели аналоги для Mac (UIView - ›NSView, UITableView -› NSTableView и т. Д.), Хотя детали реализации часто отличались (например, NSTableView может иметь несколько столбцов)

Прочитав несколько руководств по разработке приложений для macOS и немного познакомившись с платформой, я обратил внимание на следующую проблему. Как вообще я могу общаться с Microsoft Active Directory?

Ответ, на который я остановился, заключался в том, чтобы выполнить сценарий оболочки с использованием классов Foundation Process и Pipe 😅

Скрипты оболочки в Swift

Было трудно найти в Интернете ресурсы по запуску произвольных сценариев оболочки с помощью Swift; в основном я хотел выполнить эту команду терминала:

dscl localhost -read Active Directory/LL/All Domains/Users/<User>

После некоторого онлайн-исследования и множества проб и ошибок я наконец получил этот код для этого:

Команда dscl localhost -read возвращает большой текстовый файл (тысячи строк!) Для запрошенного пользователя. Так что дальше мне пришлось поискать в этом файле, чтобы получить нужную информацию. Я искал такие вещи, как их адрес электронной почты, время истечения срока действия пароля, уровень доступа к VPN и т. Д.

Например, дата последней смены пароля будет указана рядом со строкой PasswordLastSet: внутри файла, членство для доступа будет указано после memberOf: и т. д. Итак, после некоторого исследования я решил, что Регулярные выражения будут моим лучшим вариантом.

Что такое регулярное выражение?

Я впервые слышал о регулярных выражениях и понятия не имел, как они работают! (не говоря уже о том, как запускать их в Swift, что само по себе неудобно).

Итак, я вернулся в режим обучения и начал изучать Regex. После нескольких дней чтения и большой практики с Regex 101 я почувствовал себя достаточно комфортно с Regex, чтобы продолжить работу над проектом. Но затем я столкнулся с другим препятствием, запустив Regex на Swift.

После еще большего количества исследований, проб и ошибок я остановился на этом коде:

Это некрасиво, но выполняет свою работу! Например, чтобы найти дату последней смены пароля, я могу сделать это:

let lastPassRegex = "(?<=PasswordLastSet: )[^(\n)]+"
reg(lastPassRegex)

Затем мне пришлось создать регулярное выражение для всех различных значений, которые я хотел извлечь. На этом этапе я знал, что мне нужно сделать, чтобы закончить этот проект, просто на его создание ушло некоторое время (нужно учесть множество крайних случаев и мелких деталей).

Если посчитать все время исследования, то на создание начальной версии приложения у меня ушло около 2 недель. Эта версия была принята положительно, и мои ИТ-коллеги начали запрашивать функции, такие как время последнего входа в систему, лицензии на программное обеспечение, членство в группах и т. Д.

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

Членство в группе

После "доставки" приложения я добавил следующую важную функцию - отображение членства в группах. Каждый пользователь в нашей организации был членом нескольких групп Active Directory, таких как группа доступа VPN (которая давала им доступ к нашей корпоративной VPN), группа Adobe Creative Suite и т. Д.

При устранении неполадок с пользователями мне было очень полезно узнать, какие у них членства. Например, если кто-то пожаловался на то, что он не может использовать офисный список рассылки (который позволяет им отправлять электронную почту всем в определенном офисе), я могу легко подтвердить, являются ли они частью группы, которая может использовать DL, и сообщить им, если они не хватает доступа.

Пользователи могли быть членами более 90 групп, поэтому мне пришлось добавить в приложение второй экран, чтобы вместить эту информацию. Этот второй экран будет представлен в стиле «Лист».

По умолчанию контроллеры листов могут изменять размер на Mac, даже если это невозможно в базовом окне приложения! Это приводило к разного рода странному поведению, так как вы могли сделать лист настолько крошечным, что кнопка «Закрыть» исчезла бы, или сделать его размером с весь экран.

Чтобы предотвратить эти проблемы, я добавил следующую строку в viewDidAppear, чтобы отключить изменение размера:

view.window!.styleMask.remove(NSWindow.StyleMask.resizable)

Остальная часть контроллера групп была довольно простой. Существует NSTextField для фильтрации по определенным группам. NSTableView для отображения результатов и кнопка буфера обмена.

Получение данных групп было просто вопросом применения правильного регулярного выражения к текстовому файлу Active Directory.

Автозаполнение

Реализовать эту функцию оказалось намного сложнее, чем я ожидал. Во-первых, в приложении должен быть актуальный список всех имен пользователей сотрудников нашей компании (более 60 000 пользователей). Затем он должен отобразить самые близкие совпадения в табличном представлении и позволить пользователю легко перемещаться между результатами и полем поиска с помощью клавиатуры, а затем нажать Enter, чтобы выбрать результат. (или используйте мышь)

Чтобы получить список пользователей, я пробовал различные команды Терминала, но безрезультатно. Максимум, что я смог получить, - это 1000 (казалось бы, случайных) пользователей из 60 000. Судя по тому, что я читал в Интернете, на Mac невозможно перечислить более 1000 пользователей из Active Directory.

К счастью, у меня был доступ к экземпляру Windows Server. Итак, я погуглил, как создавать сценарии PowerShell, написал сценарий, который загружает имена пользователей всех пользователей Active Directory, и запланировал ежедневную задачу Windows, чтобы запускать этот сценарий каждое утро, чтобы получить обновленный список имен пользователей, а затем он загружал этот список в Box папка в облаке по определенному URL-адресу.

Я настроил LionSearch (приложение для Mac) на автоматическое получение этого списка пользователей по указанному выше URL-адресу при запуске.

Теперь, когда у меня были данные, которые мне были нужны в приложении, я начал создавать tableView для отображения результатов автозаполнения. Я добавил несколько условий для отображения таблицы, например, она останется скрытой, если в поле поиска не будет введено 2+ символа и не будет найдено хотя бы одно совпадение.

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

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

Вы можете отслеживать события с помощью этой строки:

NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask.keyDown, handler: myKeyDownEvent)

Эта суть должна дать вам представление о том, как работает функция myKeyDownEvent: https://gist.github.com/almaleh/8265e69e9d7a28199276b89b785f530f

Думаю, результат выглядел довольно мило 🙂

Некоторые сюрпризы по пути

  1. NSSearchField не имеет встроенной анимации встряхивания, которую можно использовать для недопустимых записей. Для этого пришлось использовать кастомную анимацию (спасибо, HWS!):

2. Мне потребовалось время, чтобы придумать правильный синтаксис для вызова телефонного номера прямо из приложения (с помощью FaceTime, Skype или другого программного обеспечения, которое вы могли установить). Вот:

NSWorkspace.shared.open(URL(string: "tel:" + number)!)

3. Цепочка респондентов гораздо важнее на Mac, чем на iOS.

4. Работа с Regex в Swift - неприятное занятие! Хотя Raw Strings в Swift 5 сделает это немного менее болезненным

В целом, мне очень понравился этот проект, и я многому научился в процессе. Как упоминалось выше, на создание начальной версии приложения у меня ушло около 2 недель, но дополнительные функции и исправления ошибок были завершены в течение нескольких месяцев после выпуска.

Исходный код приложения доступен здесь: