Мой первый проект на 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
Думаю, результат выглядел довольно мило 🙂
Некоторые сюрпризы по пути
- NSSearchField не имеет встроенной анимации встряхивания, которую можно использовать для недопустимых записей. Для этого пришлось использовать кастомную анимацию (спасибо, HWS!):
2. Мне потребовалось время, чтобы придумать правильный синтаксис для вызова телефонного номера прямо из приложения (с помощью FaceTime, Skype или другого программного обеспечения, которое вы могли установить). Вот:
NSWorkspace.shared.open(URL(string: "tel:" + number)!)
3. Цепочка респондентов гораздо важнее на Mac, чем на iOS.
4. Работа с Regex в Swift - неприятное занятие! Хотя Raw Strings в Swift 5 сделает это немного менее болезненным
В целом, мне очень понравился этот проект, и я многому научился в процессе. Как упоминалось выше, на создание начальной версии приложения у меня ушло около 2 недель, но дополнительные функции и исправления ошибок были завершены в течение нескольких месяцев после выпуска.
Исходный код приложения доступен здесь: