easy_dev991
624 subscribers
29 photos
19 videos
3 files
169 links
Делюсь интересными находками/лайфхаками в процессе разработки под iOS, и возможно ты найдешь что-то полезное для себя!

GitHub: https://github.com/easydev991
Download Telegram
#ios #swift #swiftui #binding #bug

Как сломать Xcode 16.3, чтобы в логах не было нормального описания ошибки:

import SwiftUI

struct BreakSwiftExample: View {
@State private var isOn = false
private var isOnBinding: Binding<Bool> {
.init(
get: { isOn },
set: changeToggle // не собирается
// set: { isOn = $0 } // собирается
)
}

var body: some View {
Toggle("Демо-тоггл", isOn: isOnBinding)
}

private func changeToggle(_ value: Bool) {
isOn = value
}
}


Результат: Command SwiftCompile failed with a nonzero exit code
2👍2🔥1
#ios #swift #swiftui #maps #geocoding #hint

На прошлой неделе заметил, что релиз 3.10 приложения с площадками стал сильно греть телефон, и даже в статистике аккумулятора спустя всего 10 минут использования мое приложение было первым в списке по потреблению энергии 🙈

В том релизе я сделал много классных штук, и в том числе немного обновил старый код для работы с картой, в результате нагрузка на девайс выросла в разы.

На днях выпустил релиз с исправлением этих проблем, и вот главные выводы:

1️⃣ Если нужно получить локацию пользователя с использованием CLLocationManager, то лучше не использовать startUpdatingLocation(), а просто вызывать requestLocation() по таймеру, например раз в 10 секунд - нагрузка ощутимо упадет

2️⃣ Использовать reverseGeocodeLocation нужно не чаще раза в минуту, а лучше еще реже - если координаты ощутимо изменились (на 50 метров, например), и это точно нужно для правильной работы фичи

3️⃣ Если используете MKMapView внутри SwiftUI-обертки, то напишите явную проверку в методе updateUIView, чтобы обновление карты выполнялось только тогда, когда это 100% нужно - я для этого сделал флаг shouldUpdateRegion и передаю его в карту, а в методе updateUIView вызываю замыкание при обновлении карты, чтобы снять флаг
🔥52
#ios #swift #swiftui #contest

Нужно сверстать стек, где есть несколько Text с одинаковой структурой:


private struct UserInfo: Identifiable {
let id = UUID()
let label: String
let value: String
}


Должно получиться примерно как на картинке.

Шрифт для label: .system(size: 20, weight: .bold)
Шрифт для value: .system(size: 16, weight: .regular)

Не используем markdown в тексте.

Вот готовые модели для превью:


let items: [UserInfo] = [
.init(label: "Почта", value: "[email protected]"),
.init(label: "Телефон", value: "+79123456789"),
.init(
label: "Адрес",
value: "117513, г. Москва, ул. Профсоюзная, д. 129, корп. 3, кв. 117, подъезд 5, этаж 8"
)
]


Кто первый сделает, тот молодец 🚀
👌3
#ios #swift #swiftui #hint

На днях добавлял SwiftUI-вьюху внутрь UIKit-ячейки для экрана с коллекцией.
Вьюха просто принимает на вход данные и показывает что-то внутри себя.

⚠️Но есть нюанс: на iOS < 16 нельзя просто так добавить SwiftUI-вьюху в ячейку - для этого нужно делать костыль с хостингом и т.д.

И вот я все сделал, но наткнулся на проблему: при первом появлении ячейки вьюха не успевает целиком отобразиться, и получается полная фигня. Зато если прокрутить экран вниз или вверх, чтобы вьюха исчезла, и снова вернуться к той ячейке, то все отлично.

На этом экране еще и кнопка есть для скролла к той самой ячейке - и при первом скролле к ней она выглядит плохо.

Решается эта проблема очень просто - можно добавить вьюхе два состояния: "загрузка" и "готово".
В "загрузке" показываем любой принятый в проекте индикатор загрузки или скелетон, а в "готово" - обычное состояние вьюхи.
Ну и в саму вьюху внутрь onAppear добавляем смену состояний.

Теперь при первом рендере этой ячейки появится индикатор загрузки, а потом сразу корректное состояние.
2🔥1
#ios #swift #swiftui #contest

Субботний челлендж по знанию SwiftUI 🤓

Во вложении два варианта анимации разворачивания и сворачивания секции.

Нужно реализовать целевое поведение.
Задача очень простая, если знать как делать 😁

Код для старта тут.

Кто первый, тот молодец 🚀
👀3👍2
#ios #swift #swiftui #contest

Описание задачи и код для старта есть в гитхабе.
Кто первый, тот молодец! 🚀
1
#ios #swift #swiftui #uikit

Все еще верстаете на ките и пользуетесь таблицами/коллекциями для создания списков? При этом хочется использовать в этих экранах SwiftUI?
Не беда! В iOS 16 добавили нативную интеграцию SUI в ячейках с помощью contentConfiguration.

Вот так нужно было в apple презентовать этот инструмент😁, а не тихонечко сказать в одном из видео, что вот теперь вы можете в ячейках добавлять SUI.

Алгоритм работы с этой штукой:

1️⃣ Верстаем вьюху для ячейки на SUI
2️⃣ В самой ячейке делаем проверку на iOS 16, и если версия подходит, то добавляем вьюху как показано в документации, можно обращаться напрямую к свойству contentConfiguration внутри ячейки
3️⃣ Если версия iOS < 16, то либо не показываем обновленный UI (😁), либо используем костыли с UIHostingController

Важный момент: если просто добавить SUI-вьюху в ячейку и не настроить отступы для конфигурации, то будут добавлены дефолтные отступы по горизонтали и вертикали.
Чтобы такой фигни не было, нужно вручную поверх конфигурации добавить отступы через метод margins.

Пример настройки contentConfiguration с нужными отступами:

func setup(with viewModel: DemoCollectionCellViewModel) {
if #available(iOS 16.0, *) {
contentConfiguration = UIHostingConfiguration {
SwiftUIView(viewModel: viewModel)
}.margins(.horizontal, 12) // <- вот тут задали отступы по горизонтали
} else {
setupOld(with: viewModel)
}
}


Весь код для демо-ячейки можно посмотреть в гитхабе.
3🔥2
#ios #swift #swiftui #swiftdata #cloudkit #hint

Создали новое iOS-приложение с использованием Swift Data и CloudKit, но оно крашится после добавления контейнера?

Не беда!
На скриншоте исправление краша в только что созданном приложении, где включена галка Host in CloudKit и добавлен ваш CloudKit Container.

Все поля в моделях Swift Data должны быть или опциональными, или иметь дефолтное значение.

Если оставить поле timestamp без дефолтного значения, будет краш:

Thread 1: Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil).

При этом краша не будет, если снять галку с контейнера в настройках проекта 😐

Будет здорово, если в новом Xcode появятся более прозрачные ошибки на этот случай (бету не ставил).
🔥8
easy_dev991
#ios #swiftui #performance Убираем лишние вычисления body в SwiftUI View
#ios #swiftui #equatable

Наглядный пример использования протокола Equatable, чтобы избавиться от лишних перерисовок вьюхи. На первом видео до исправления, на втором - после.

Вьюха принимает на вход несколько свойств, и ни одно из них не изменяется при нажатии на кнопку "избранного" в навбаре, но по умолчанию вьюха обновляется при изменении этого State-свойства на экране.

Все, что нужно было сделать для исправления проблемы:


struct HTMLContentView: UIViewRepresentable, @preconcurrency Equatable {
static func == (lhs: HTMLContentView, rhs: HTMLContentView) -> Bool {
lhs.infopost == rhs.infopost && lhs.fontSize == rhs.fontSize
}
// остальной код
}


Это первый случай, когда мне пригодился протокол Equatable с момента публикации статьи об этом, и все же он пригодился 👌
🔥52
This media is not supported in your browser
VIEW IN TELEGRAM
#ios #ios26 #swiftui #navigationview #bug #contest

Представляю вашему вниманию очередной баг iOS 26 при работе с NavigationView, который отлично работает на всех версиях iOS, в том числе на iOS 26.

На iOS 26 экран автоматически закрывается при первом вызове любого из menu/picker/confirmationDialog, а вот код этого экрана, и там нет никаких ошибок, и чинить там нечего 🤦‍♂️

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

--------

Позже напишу статью на эту тему, а пока что объявляю мини-конкурс: кто правильно назовет причину возникновения бага в iOS 26, тот молодец 🚀
🔥5
easy_dev991
#ios #swiftui #scrollview #scrollviewreader #scrollto Скроллим к нужному элементу
#ios #swiftui #scrollview #scrollto

На днях застрял со скроллом к нужному элементу и думал "что же не так?".
У меня есть список элементов в ForEach, внутри массив с Identifiable-элементами, но scrollTo(id) не работает.

Все оказалось очень просто - нужно явным образом добавить модификатор .id для всех элементов в цикле ForEach.

Потом вспомнил, что у меня уже есть статья на эту тему 😁
4😁4😴1