Kotlin/Swift (iOS) Туда и Обратно
36 subscribers
128 photos
21 videos
9 files
85 links
Канал - журнал, рассказывающий об опыте изучения Swift & iOS backend-разработчиком на Java & Kotlin
Download Telegram
Прохожу Проект 11, и там, наконец-то, то, чего я ждал - работа с данными. Думаю, завтра завершу и напишу пост об этом.
1👏1
Это, как говорится, ни в какие ворота не лезет. Я потратил ❗️ 15 минут ❗️ на поиск простой опечатки (параметр называется configurations).

Бесполезный linter… 😤
Please open Telegram to view this post
VIEW IN TELEGRAM
😢1
А теперь вопрос к SwiftUI.
Почему нет конструктора для ClosedRange, но есть для Range? Забыли посахарить? 😄

На первый взгляд ClosedRange является подтипом Range. Но первое впечатление обманчиво, видимо нужно погружаться в теорию типов.

Фиксится через добавление id

ForEach(1...maxRating, id: \.self)

В этом случае тип становится Data

public init(_ data: Data, id: KeyPath<Data.Element, ID>, @ViewBuilder content: @escaping (Data.Element) -> Content)
🤔1
Проект 11

Этот проект оказался самым полезным, поскольку его функционал сильно пересекается с тем, что я планирую реализовать в своём приложении.

Основные темы:

🟢 @Binding - позволяет изменять значение, переданное извне. Используем, когда нужно передать тип данных из View 1 в View 2, и при этом View 2 может модифицировать переданное значение. Это полезно для двусторонней связи между представлением и моделью.
🟢 TextEditor - многострочная версия TextField. Подходит для ввода текста, который может “расти” в высоту или ширину в зависимости от содержимого. Очень удобен для реализации многострочных полей ввода.
🟢 SwiftData - новый механизм для персистентного хранения данных в iOS, аналогичный CoreData, но более абстрактный и с упрощённым интерфейсом. Несмотря на скромные возможности по сравнению с CoreData, SwiftData легче в освоении и значительно проще в использовании для базовых задач. Это именно то, что мне нужно для быстрого старта.
🟢 Кастомные View, которые можно комбинировать и использовать в других View. Это помогает создавать переиспользуемые компоненты, улучшая читаемость и структуру кода.
🟢 @Query - позволяет фильтровать и сортировать данные, хранимые в SwiftData, без необходимости писать сложные запросы вручную. Это упрощает работу с данными и позволяет быстрее получить нужную информацию.

Кстати, когда я пытался сделать приложение с помощью ChatGPT, он почему-то предложил использовать CoreData, и про SwiftData ничего не сказал. Хотя последнее идеально подходит для моей задачи.

hackingwithswift.com отличный курс, который уже научил меня многому.

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1👏1💯1
💪
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1👏1
Попробовал посмотреть другие ресурсы для изучения Swift. Вроде выглядит неплохо - codecademy
Проходил модуль про enum: https://www.codecademy.com/courses/learn-swift-enumerations/lessons/swift-enumerations-lesson/exercises/introducing-enumerations

Вроде как сам урок бесплатный, но тест и проект (тут нужно без примеров по инструкции реализовывать функционал) уже платные.

Из полезного: увидел как делается и попробовал на практике

🟢 RawValue - можно каждому enum сопоставить какое-то значение. Тут можно и String сделать.

enum Team: Int {
case alpha = 1
case bravo
case charlie
case delta
}

print(Team.delta.rawValue) // вывод: 4
let team = Team(rawValue: 3) // создаст .charlie


🟢 CaseIterable - чтобы проитерироваться по всем значениям

enum Season: CaseIterable {
case winter
case spring
case summer
case fall
}
// потом можно так
for season in Season.allCases {
print(season)
}


🟢 Ассоциированные значения - каждый enum может содержать свою переменную (наверное, несколько тоже можно)

enum Dessert {
case cake(flavor: String)
case vanillaIceCream(scoops: Int)
case brownie
}
let tonightsSpecial = Dessert.vanillaIceCream(scoops: 4)
switch tonightsSpecial {

case let .cake(cakeFlavor):
print("Time for \(cakeFlavor) cake")
case let .vanillaIceCream(scoopCount):
print("\(scoopCount) scoops of vanilla ice cream")
case .brownie:
print("Decadent goodness")
}

// Вывод: 4 scoops of vanilla ice cream


🟢 Мутирующие методы

enum Season {
case winter, spring, summer, fall

mutating func changeSeason() {
switch self {
case .winter:
self = .spring
case .spring:
self = .summer
case .summer:
self = .fall
case .fall:
self = .winter
}
}
}


Попробую ещё несколько модулей, расскажу по мере прохождения. Но пока курс hackingwithswift в приоритете, и я планирую пройти его до конца.

А кто-то уже пользовался codecademy, можете поделиться своим опытом?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1🤔1👀1
Проект 12: дни 57-59

Это был технический проект направленный на чуть большее понимание деталей работы с SwiftData.

🟢 Чтобы SwiftData заработал, достаточно:
1. Создать модель в виде класса и добавить @Model макрос.
2. Заинжектить modelContainer для этого класса.
3. Во View получить доступ к modelContext через @Environment(\.modelContext) var modelContext
Всё действительно просто 😍

🟢 Для #Preview можно сделать свой modelContainer, который будет храниться в памяти. Выглядит это немного громоздко, но явно есть инструменты позволяющие сократить код. Удивлюсь если их нет, preview весьма полезный инструмент, и используется часто.

🟢 Показана фильтрация в @Query - как же ужасно это выглядит! Особенно, если развернуть макрос. Почему там нельзя использовать обычную лямбду/замыкание? 😮

🟢 SwiftData умеет неявно делать связи между моделями. Мы можем просто объявить новое поле, тип которого другая модель (или даже список/массив). Каскадное удаление также присутствует, но тут уже нужно добавить макрос как в этом примере: @Relationship(deleteRule: .cascade) var jobs = [Job]()

🟢 CloudKit - пока загадочный для меня фреймворх для хранения там данных приложения. Классно, что SwiftData может с ним синхронизироваться, позволяя также синхронизировать данные между девайсами. Но, почитав документация, я понимаю, что он предназначен для публичных данных, а iCloud для приватных. Я могу сказать, что всё что на моём телефоне и связано со мной - приватное! Какой у этого реальный юзкейс ещё предстоит выяснить 🔍

Забавно, что у SwiftData есть свой значок 📦

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1👏1
В дополнение к пункту про фильтрацию в @Query
Посмотрите сами… этот код не является валидным

@Query(filter: #Predicate<User> { user in
if user.name.localizedStandardContains("R") {
if user.city == "London" {
return true
}
}

return false
}, sort: \User.name) var users: [User]
Не плохо, но и не отлично 🙂
Please open Telegram to view this post
VIEW IN TELEGRAM
Продолжаю эксперименты с ChatGPT - что называется, ожидание vs. реальность 😄
Но в целом, результат неплохой!

Я тем временем присматриваюсь к различным дизайнам, внимание уделяю больше функциональности, а не красоте.

Кстати, заметил, что стал иначе воспринимать приложения, которыми пользуюсь ежедневно. Теперь обращаю внимание на детали интерфейса: что удобно, а что нет, как реализованы те или иные элементы. Такой новый взгляд помогает лучше понимать, какие решения стоит взять на заметку, а что нужно избегать.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍11
День 60

Сегодняшний челлендж:

Загрузить JSON, содержащий массив объектов с информацией о людях и списком их друзей.
Отобразить список людей.
При выборе конкретного человека показать его подробную информацию.

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1
JSON можно посмотреть тут: https://www.hackingwithswift.com/samples/friendface.json

Это задание я сделал практически самостоятельно. Единственное исключение - поиск примеров некоторых конструкций в официальной документации и, конечно, на всеми любимом Stack Overflow.

.toolbar {
ToolbarItem(placement: .principal) {
HStack {
Image(systemName: user.isActive ? "person.fill.checkmark.rtl" : "person.fill.xmark.rtl")
Text(user.name).font(.headline)
}
}
}

Этот трюк с настройкой тулбара как раз нашел там 👩‍💻

Кстати, официальная документация Apple организована действительно хорошо: удобная навигация, детальные описания, кросс-ссылки на связанные элементы и, главное, примеры. Правда, не все из них оказываются полезными. Например, настройка декодера там не отражена:

decoder.dateDecodingStrategy = .iso8601


Хотя, если подумать, это instance property, так что логично, что его нужно просто присвоить. Просто это выбивается из уже привычного синтаксиса, когда мы вызываем метод и конфигурируем всё через параметры.

https://developer.apple.com/documentation/foundation/jsondecoder/2895216-datedecodingstrategy
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Всем привет ✌️ Сегодня порешаем leetcode 👩‍💻 используя Swift, конечно!

Задача:
Вам дана строка blocks длины n с 0-индексацией, где blocks[i] - это либо 'W', либо 'B', представляющие цвет i-го блока. Символ 'W' обозначает белый цвет, а 'B' - черный.

Также дано целое число k, которое представляет желаемое количество последовательных черных блоков.

За одну операцию можно перекрасить белый блок в черный.

Необходимо вернуть минимальное количество операций, необходимых для того, чтобы в строке появилась хотя бы одна последовательность из k подряд идущих черных блоков.

Решение (попробуйте решить самостоятельно, перед тем как смотреть мой вариант 👇):

class Solution {
func minimumRecolors(_ blocks: String, _ k: Int) -> Int {
var whiteCount = 0
var minRecolors = k
var left = blocks.startIndex

for (rightOffset, char) in blocks.enumerated() {
if char == "W" { whiteCount += 1 }

if rightOffset >= k - 1 {
minRecolors = min(minRecolors, whiteCount)
if blocks[left] == "W" { whiteCount -= 1 }
left = blocks.index(after: left)
}
}
return minRecolors
}
}


https://leetcode.com/problems/minimum-recolors-to-get-k-consecutive-black-blocks
#leetcode
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Всё же работа со строками в Swift устроена сложнее, чем в Kotlin.
В Swift индексы представлены специальными типами (String.Index), поэтому нельзя просто обратиться к символу по числовому индексу, как в Kotlin (string[index]). Вместо этого используется index(_:offsetBy:):


let text = "Hello"
let index = text.index(text.startIndex, offsetBy: 1)
print(text[index]) // "e"



val text = "Hello"
println(text[1]) // "e"


Говорят, что это делает работу со строками в Swift более безопасной, особенно при обработке многобайтовых символов, таких как emoji. Компромиссное решение, которое вызывает у меня смешанные ощущения.
🤔1
День 61

Снова челленж, но он является продолжением предыдущего. Нужно теперь сохранить, тот JSON что мы скачиваем, в SwiftData.

Чтобы проверить, что данные берутся из SwiftData, я добавил лишний символ в URL - РАБОТАЕТ!

Впереди нас ждет работа с картинками, а потом и с картами. Звучит интригующе 🤩

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1🆒1
Чтобы работали и SwiftData, и JSONDecoder, нужно сделать такие страшные вещи 😱
Написать кастомные encoder/decoder методы.
Please open Telegram to view this post
VIEW IN TELEGRAM
😱2
Проект 13 (Day 62) - состоит аж из 6 частей. Обычно проект состоит из 3. Буду его дробить на части поменьше.

🟢 Заглянули во внутренности State проперти враппера. Но не настолько сильно, чтобы понять как он работает на 100%. Однако эти детали помогают понять почему этот код не работает при изменении поля через bindings:

@State private var blurAmount = 0.0 {
didSet {
print("New value is \(blurAmount)")
}
}


🟢 onChange - позволяет подписаться на любые изменения переменной

Slider(value: $blurAmount, in: 0...20)
.onChange(of: blurAmount) { oldValue, newValue in
print("New value is \(newValue)")
}


🟢 confirmationDialog - диалоговое окно похожее на alert, но мне оно кажется функциональнее, т.к. оно и его кнопки находятся внизу, что позволяет быстрее и легче взаимодействовать с ним (не нужно тянуться пальцем к середине экрана, как в случае с `alert`).

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Проект 13 (Day 63)

Сегодня были 2 темы. Порой это сильно удивляет, насколько малосвязанные темы включаются в один урок.

🟢 CoreImage - оказывается, для представления изображений в iOS есть четыре основных типа:
🟠 SwiftUI Image - View для отображения изображений в SwiftUI.
🟠 UIImage - изображение из UIKit. Обширный функционал, поддерживает форматы PNG, SVG и другие.
🟠 CGImage - Core Graphics. По сути двумерный массив пикселей.
🟠 CIImage - Core Image. Автор называет его “рецептом для создания изображения”. Поддерживает различные фильтры, такие как pixellate, crystallize, sepiaTone и другие.

🟢 ContentUnavailableView - view с иконкой, текстом и кнопкой (которые можно кастомизировать по своему усмотрению), предназначенное для отображения, когда контент недоступен.

#hackingwithswift
Please open Telegram to view this post
VIEW IN TELEGRAM