Kotlin/Swift (iOS) Туда и Обратно
36 subscribers
127 photos
21 videos
9 files
84 links
Канал - журнал, рассказывающий об опыте изучения Swift & iOS backend-разработчиком на Java & Kotlin
Download Telegram
Для сравнения списков мест использовали такой подход:

struct Page: Codable, Comparable {
let pageid: Int
let title: String
let terms: [String: [String]]?

var description: String {
terms?["description"]?.first ?? "No further information"
}

static func <(lhs: Page, rhs: Page) -> Bool {
lhs.title < rhs.title
}
}


Я думаю, что он неправильный, а точнее не совсем корректный.

Если взять два объекта с title = “a”, в таком случае мы получим:

print(p1 < p2) // false
print(p2 < p1) // false
print(p1 == p2) // false


В документации сказано, что так же нужно реализовывать ==, тогда этого недоразумения быть не должно. Печально, что автор об этом не говорит. Хотя я спросил у ChatGPT 💬 об этом, он утверждает, что реализация без == - корректная 😄

В Java это реализовано удачнее, на мой взгляд, результатом сравнения является int, который может принимать:
➡️ отрицательные значения если меньше
➡️ положительные значения если больше
➡️ ноль если равно

Реализовывать нужно всего один метод. Да, использование выглядит не так красиво (сравнение вида a.compareTo(b) >= 0, но Kotlin это исправляет и позволяет писать a >= b.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
Вот, кстати, выдержка из документации
https://developer.apple.com/documentation/swift/comparable
👨‍💻1
Всем привет! Закончил Проект 15 (дни 74, 75, 76)

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

Для меня это напоминает логирование: когда разрабатываешь приложение, важно думать о traceability (особенно в распределенной системе), но по-настоящему ощущаешь полноту логов, только когда начинаешь их анализировать в тестовой среде или при отладке. То же самое с доступностью - пока не попробуешь это на устройстве, сложно оценить, что действительно нужно улучшить.

#hackingwithswift
👍2
Тест на этот раз был очень хороший! Вопросы довольно таки хорошо раскрывают тему 💪
Делаю челлендж по дням 77 и 78

Пришлось сделать небольшой перерыв, но я вернулся 😉

Пока испытываю трудности с организацией вьюх при нажатии на плюсик - учусь работать с асинхронными данными корректно
PhotosPicker долго загружает фото. Сейчас ContentView перегружен функционалом, и вывод списка, и добавление новой записи, и выбор фото… 🤯
Размышляю как это декомпозировать.

Завтра планирую сделать рабочую версию, поэтому буду писать больше 🧑‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
От этого начинает уже иногда глаз дёргаться.

Проблема
Если у меня в определении sheet есть ошибка, то я вижу Ambiguous use of ‘toolbar(content:)’

Как решаю сейчас
В какой-то момент я понял, что с тулбаром всё в порядке. А как решение можно его закомментить и смотреть реальную ошибку.

Как это должно правильно решаться?
Подскажите кто знает, пожалуйста, я трачу реально много времени на это. Переместить тулбар вниз/наружу не получилось, т.к. он привязан к List, который находится внутри NavigationStack. Возможно стоит sheet на один уровень с toolbar перенести?
This media is not supported in your browser
VIEW IN TELEGRAM
Во время реализации столкнулся с таким странным поведением. Sheet не видел изменения в selectedImage, до тех пор пока не будет введен символ в TextField.
Пофиксил созданием отдельного View для добавления.
Пока ещё загадок для меня слишком много 😱 Последовательно будем разбираться!
Please open Telegram to view this post
VIEW IN TELEGRAM
👨‍💻1
Media is too big
VIEW IN TELEGRAM
По итогу получилось приложение в котором можно взять фото из галереи и добавить ему имя. Автоматически подтягивается последняя известная локация.

Второй день челенджа подразумевал добавление карты и отображение сохранённых записей на ней.

Тут я дополнительно посмотрел
- как делать фокус на конкретных полях (в данном случае имя записи получает фокус)
- TabView, чтобы переключаться между списком и картой
🔥1
Проект 16 (день 79)

После проекта, сделанного в рамках челенжа, мне не терпится поскорее пройти курс дальше! Небольшой, но, как всегда, полезный урок:

🟢 Выбор нескольких элементов в списке. Активируется либо горизонтальным свайпом 2-мя пальцами, либо добавлением кнопки EditButton

🟢 TabView - пришло его время, но я уже использовал его в челенже ✌️ Концепция вполне простая, но очень полезная. Вижу почти в каждом приложении.

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Обратил внимание только сейчас.

Загруженное изображение как UIImage(systemName), а потом переданное в 👶 Image становится пиксельным.

Если использовать Image(systemName), то получается векторное изображение, красивое при любом разрешении.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1🤔1
Проект 16 (день 80)

Сегодня тоже не много тем, точнее они не очень объемные, но они не становятся менее интересными:

🟢 Result - тип похожий на Either из функциональных языков. Может содержать в себе либо success, либо failure. В Kotlin такой тоже есть.

🟢 Интерполяция изображений - у Image есть interpolation расширение, можно настроить степень интерполяции. Если простыми словами, это дорисовать новые пиксели на основе существующих при увеличении изображения.

🟢 Контекстные меню - при нажатии на элемент (удерживая палец) можно вызвать меню. Автор рекомендует им не увлекаться, т.к. такой функционал не очень очевиден пользователям.
Кстати, на маке в Swift Playground это не работает, видимо только для тачскринов.

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Решил посмотреть на приложение, что получилось с ChatGPT в паре написать - я плачу 😆
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1😁1
Проект 16 (день 81)

Поскольку уроки небольшие, прохожу несколько за раз.
День 81 предлагает следующие темы:

🟢 Можно добавить кастомные свайп-кнопки на элементы списка

List {
Text("Taylor Swift")
.swipeActions {
Button("Delete", systemImage: "minus.circle", role: .destructive) {
print("Deleting")
}
}
.swipeActions(edge: .leading) {
Button("Pin", systemImage: "pin") {
print("Pinning")
}
.tint(.orange)
}
}


🟢 Нотификации. Фреймворк, насколько я понял, используется, скажем, не новый 😆

Запрашиваем права

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
print(“Всё ок!”)
} else if let error {
print(error.localizedDescription)
}
}


Делаем отправку

let content = UNMutableNotificationContent()
content.title = “Накорми кота”
content.subtitle = “Он “голодный
content.sound = UNNotificationSound.default

// показать нотификацию через 5 секунд
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

// выбираем рандомный ид
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)

UNUserNotificationCenter.current().add(request)


🟢 Подключение проектов как зависимости. Выглядит, на самом деле, как git submodule. Что тут ещё написать…?

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1😁1
Пример git submodule: https://github.com/robocorp/example-use-git-submodule-for-shared-code

Выглядит это как отдельная папка с указанием хэша коммита 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Проект 16 (день 82)

И вновь день с небольшим количеством материала. Мы начали объединять пройденные ранее уроки в приложение для отслеживания людей, которых мы встретили на конференции:

🟢 Создали 4 основных таба, отражающих списки людей: все, с кем познакомились, с кем предстоит встретиться и информация о себе.
Для первых 3 табов используем одну и ту же View. Определяем тип с помощью перечисления:


enum FilterType {
case none, contacted, uncontacted
}


🟢 Добавили SwiftData, для этого подготовили такую модель


@Model
class Prospect {
var name: String
var emailAddress: String
var isContacted: Bool

init(name: String, emailAddress: String, isContacted: Bool) {
self.name = name
self.emailAddress = emailAddress
self.isContacted = isContacted
}
}


🟢Добавили динамическую фильтрацию в зависимости от FilterType при инициализации View:


init(filter: FilterType) {
self.filter = filter

if filter != .none {
let showContactedOnly = filter == .contacted

_prospects = Query(filter: #Predicate {
$0.isContacted == showContactedOnly
}, sort: [SortDescriptor(\Prospect.name)])
}
}


#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Применяю полученные навыки для самообучения!
Не todo-list же делать 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21
Проект 16 (день 83)

Сегодня мы разбирались:

🟢 Как генерировать QR код
Для генерации использовали CIFilter.qrCodeGenerator(). Даже не подозревал, что настолько просто это можно сделать.
Тут пригодилась интерполяция, т.к. она включена по-умолчанию и изображение при масштабировании становится “мыльным”. Скину скрины для сравнения.

🟢 Как сканировать QR код
Для чтения использовали код автора (думаю, что это как раз тот код, упоминаемый тут https://t.me/kotlin2swift/86, по крайней мере называется он именно так).

🟢 Добавили возможность удаления записей с помощью свайпа.

Ниже приложу скриншоты 📸

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
1 - интерполяция
2, 3 - кнопки для разного типа записей
4 - множественное редактирование
👍2🔥1