Forwarded from Кот Денисова
Всем привет! Недавно я писал пост с иронией про новый скилл разработчиков: чистку кода от вайбкодинга. Оказалось все куда серьезнее и это не ирония, а реальность рынка 2025 года.
Что происходит?
Компании начали нанимать дорогих разработчиков для одной задачи - чистить код, который сгенерировали нейросети. Появились целые продукты, бизнес которых построен на приведении AI-кода в человеческий вид.
Три главные проблемы ИИ-генерации кода:
Неэффективность и изобретение велосипедов.
Нейросети прямолинейны как трамвай. Если в проекте уже есть функция умножения двух чисел, ИИ напишет новую. Если есть готовый хелпер для работы с датами, создаст свой.
Результат: дублирование кода, раздутые билды и архитектурный хаос.
Проблемы безопасности.
Нейросети игнорируют базовые правила безопасности. В коде остаются:
🔹 Проблемы с аутентификацией.
🔹 Утечки чувствительных данных.
🔹 Уязвимости инъекций.
Низкая читаемость кода.
ИИ любит:
🔹 Вкладывать функции в функции до бесконечности.
🔹 Создавать мега-файлы с десятками методов.
🔹 Генерировать многословные комментарии вместо понятного кода.
Получается не архитектура, а дерево зависимостей, где все вызывает все.
Мой опыт:
В личных проектах я активно использую ИИ для рутины, но перед релизом такой код требует серьезного рефакторинга. Особенно страдают:
🔹 Code style: совсем не соответствует внутренним стандартам проекта.
🔹 Переиспользование кода: дублирует существующую логику.
🔹 Архитектура: ИИ не понимает текущую архитектуру приложения и концепцию разделения ответственности.
Нейросети не заменяют разработчиков, они меняют их роль. Теперь мы не только пишем код, но и рефакторим и приводим в порядок то, что создает ИИ.
А как вы используете нейросети в работе? Сталкивались с похожими проблемами?
Кот Денисова
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24❤15🔥7 4👏1🙏1👀1
Привет, друзья! Apple тихо представила интересный фреймворк: AppMigrationKit. Это не просто еще одна библиотека, а реальный инструмент для решения старой боли: миграции данных между Android и iOS.
Что такое AppMigrationKit.
Данный фреймворк позволит безопасно переносить данные вашего приложения между iOS и Android. Вместо того чтобы пользователи вручную переносили свои настройки, прогресс или сохранения, всё происходит автоматически во время настройки нового устройства.
Как это работает.
Вы создаете extension в своем приложении, который реализует протокол AppMigrationExtension. Система сама вызовет ваш extension когда пользователь переходит с Android на iPhone (или обратно).
Что важно понимать:
🔹 Работает между Apple и не-Apple платформами (например, Android).
🔹 Не для iOS-iOS миграции (тут и так все просто).
🔹 Не работает на visionOS и macOS.
🔹 Cloud данные нужно синхронизировать отдельно после миграции.
Почему это важно для разработчиков.
Пользователи часто не хотят менять iPhone именно из-за страха потерять данные в приложениях. Теперь мы можем дать им плавный переход. Особенно важно для:
🔹 Игр с прогрессом.
🔹 Приложений для тренировок.
🔹 Трекеров привычек.
🔹 Любых приложений с пользовательскими данными.
Это большой шаг к открытости экосистемы Apple. Раньше такие миграции требовали костылей и сторонних сервисов. Теперь есть нативный способ. Жду, когда крупные приложения типа Duolingo начнут это использовать!
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍16❤8👀4 2🤯1🙏1
Друзья, сегодня поговорим о массивах в Swift. Казалось бы, самой базовой структуре данных, но с интересными особенностями под капотом.
Value type но со своими хитростями.
Массивы - это структуры (value types), но данные хранятся в куче. При присваивании в переменную создается новая структура, но буфер с данными остается общим до момента модификации (Copy-on-Write):
var array1 = [1, 2, 3]
var array2 = array1 // Пока нет копирования данных
array1.append(4) // Только теперь данные копируются
Работа с ссылочными типами.
С классами нужно быть аккуратнее:
class User {
var name: String
init(name: String) {
self.name = name
}
}
let users1 = [User(name: "Artem"), User(name: "Dima")]
let users2 = users1
users1[0].name = "Egor" // Изменение затронет оба массива
print(users2[0].name) // Egor
Для глубокого копирования нужно реализовать протокол NSCopying. Без него при копировании массива с классами создаются только новые ссылки на те же объекты в памяти. NSCopying позволяет создать настоящие копии каждого объекта.
Производительность операций с массивами.
На практике важно понимать сложность основных операций с массивами, чтобы выбирать оптимальные подходы:
🔹 append() - в среднем O(1), но при расширении буфера происходит копирование всех элементов.
🔹 removeLast() - O(1), так как не требует сдвига элементов.
🔹 remove(at:) - O(n) из-за необходимости сдвигать все последующие элементы.
🔹 insert(_:at:) - O(n) по той же причине, что и remove(at:).
Эффективная работа с частями массива (ArraySlice).
ArraySlice работает как указатель на определенный диапазон элементов в оригинальном массиве. Это особенно полезно при работе с большими массивами:
let largeArray = Array(0..<1000000) // Большой массив
let slice = largeArray[1000..<2000] // Не копирует миллион элементов!
// Используем срез как обычный массив
for element in slice {
print(element) // Работает с элементами 1000-1999
}
Но важно помнить: ArraySlice сохраняет сильную ссылку на весь исходный массив. Это может привести к неожиданному потреблению памяти. Вот решение:
let largeArray = Array(0..<1000000) // Большой массив
let slice = largeArray[1000..<2000] // Не копирует миллион элементов!
// Создаем новый массив если нужна независимая копия
let independentCopy = Array(slice) // Теперь largeArray можно освободить
Массивы под капотом.
Swift использует разные буферы в зависимости от контекста:
🔹 _ContiguousArrayBuffer - для Swift-типов.
🔹 _ArrayBuffer - при работе с Objective-C.
Массивы в Swift - это не просто списки значений. Понимание их устройства помогает писать более эффективный код и избегать неочевидных проблем.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24❤17🔥10🙏3 2🤝1 1
В Swift у разработчиков есть выбор: использовать традиционные циклы for или функции высшего порядка. Давайте разберемся, где каждый подход раскрывается лучше всего.
Сильные стороны функций высшего порядка.
Для простых преобразований данных map выглядит чище:
// Более читаемо
let numbers = [1, 2, 3, 4, 5]
let squaredNumbers = numbers.map { $0 * $0 }
// squaredNumbers = [1, 4, 9, 16, 25]
// Чем
let numbers = [1, 2, 3, 4, 5]
let squaredNumbers = [Int]()
for number in numbers {
squaredNumbers.append(number * number)
}
// squaredNumbers = [1, 4, 9, 16, 25]
filter идеален для отбора элементов:
let users = [
User(name: "Анна", isActive: true, age: 25),
User(name: "Петр", isActive: false, age: 30),
User(name: "Мария", isActive: true, age: 22)
]
let activeUsers = users.filter { $0.isActive }
// activeUsers = [User(name: "Анна", isActive: true, age: 25), User(name: "Мария", isActive: true, age: 22)]
reduce отлично подходит для агрегации:
let prices = [100, 200, 300]
let totalPrice = prices.reduce(0) { currentSum, price in
return currentSum + price
}
// totalPrice = 600
Когда циклы for предпочтительнее.
Логика с условиями:
var passedStudents: [String] = []
let studentGrades = [45, 82, 91, 33, 67, 74, 58, 29, 95]
for grade in studentGrades {
guard grade >= 60 else { continue } // Исключаем оценки ниже 60
guard grade <= 90 else { continue } // Исключаем оценки выше 90
passedStudents.append("Студент с оценкой \(grade)")
}
/*
passedStudents = [
"Студент с оценкой 82",
"Студент с оценкой 67",
"Студент с оценкой 74"
]
*/
Ранний выход из цикла:
// Поиск первого отрицательного числа
let dailyTemperatures = [15, 18, -2, 20, 25, -5]
var firstNegative: Int?
for temperature in dailyTemperatures {
if temperature < 0 {
firstNegative = temperature
break // Выходим при первом найденном
}
}
// firstNegative = -2
Изменение внешних переменных:
let itemsToProcess = [1, 15, 41, 3, 22, 61]
var processedItems = 0
var failedItems = 0
for item in itemsToProcess {
if processItem(item) {
processedItems += 1
} else {
failedItems += 1
}
}
Производительность.
Для массивов до 1000 элементов разница в производительности между функциями высшего порядка и циклом for практически незаметна. Современные оптимизации компилятора Swift отлично справляются с функциональными методами. Однако при обработке десятков тысяч элементов циклы for могут быть на 10-30% быстрее.
Функции высшего порядка часто создают промежуточные массивы. Цепочка map, filter, map может создать 3 временных массива, тогда как цикл for работает с одним. Для больших данных это влияет на память.
Swift предлагает нам два мощных подхода к обработке данных, и мудрый разработчик знает, когда применять каждый из них. Функции высшего порядка прекрасно справляются с простыми и средними по сложности преобразованиями данных. Они делают код более декларативным, читаемым и безопасным за счет неизменяемости.
Однако традиционные циклы for остаются незаменимыми, когда речь идет о сложной бизнес логике с множеством условий внутри цикла, необходимости раннего выхода из цикла или изменении внешних переменных.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Здравствуйте, друзья! Сегодня поговорим на одну неудобную, но очень важную тему. Ту самую, о которой многие молчат, но которая живет в голове у большинства из нас.
Вам знакомо чувство, когда смотришь на код коллеги и думаешь: «Вау, он такой умный, а я вообще ничего не понимаю»? Или когда получаешь оффер и первая мысль: «Ой, а они точно не ошиблись?»
Поздравляю, вы не одиноки. Это тот самый «синдром самозванца», и в мобильной разработке он распространён как нигде. Почему? Потому что экосистема iOS меняется со скоростью света. Только разобрался с UIKit, появился SwiftUI. Только освоил GCD, все переходят на async/await. Вчера Objective-C, сегодня Swift, завтра - кто знает, что еще.
Типы «синдрома самозванца».
🔹 Перфекционист: будет переписывать код десять раз, потому что «еще не идеально». Знакомо? Многие иногда ловят себя на том, что тратят час на нейминг переменной.
🔹 «Я же должен всё знать»: этот парень уверен, что Senior iOS разработчик должен помнить наизусть всю документацию Apple. А когда сталкивается с задачей, которую не может решить за пять минут, начинает сомневаться в своей профпригодности.
🔹 Одиночка: три дня бьётся над проблемой с Auto Layout, но не спросит помощи, потому что «буду выглядеть глупо». Признаюсь, я и сам через это проходил в начале карьеры.
🔹 Супермен: берет все задачи подряд, работает по ночам, доказывает что-то миру и выгорает через полгода.
🔹 Вечный студент: «мне нужно пройти еще один курс по iOS-разработке, прежде чем я буду готов к собесу». Узнаете?
Что может помочь улучшить ситуацию:
🔹 Фокус на решении, а не на знании: вместо «я должен выучить весь Swift» думайте «мне нужно решить вот эту конкретную задачу». Это сильно снижает уровень давления.
🔹 Блокнот маленьких побед: звучит банально, но работает. Раз в неделю записывайте 2-3 вещи, которые получились хорошо: пофиксили сложный баг, помогли коллеге разобраться, написал чистый код. Со временем начнете видеть реальную картину вместо выдуманной.
🔹 Экспериментальный подход: смотрите на сложные задачи не как на экзамен, а как на исследование. Не получилось с первого раза? Ну и ладно, получили опыт, собрали данные, пробуйте иначе.
🔹 Помните, что сомнения - это нормально: если вы в чем-то не уверены значит вы растете и выходите из зоны комфорта. Отсутствие сомнений это скорее повод для беспокойства.
Синдром самозванца не исчезает с опытом. Он просто трансформируется. Даже спустя годы в индустрии иногда ловлю себя на мысли: «А не переоценивают ли меня?» Но теперь я понимаю, что это не слабость, а обратная сторона развития.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤20👍17🔥7👀6😁1🙏1
Всем привет! Сегодня поговорим о, казалось бы, базовой теме: выборе между let и var в структурах Swift. Но, как оказалось, даже здесь есть много тонкостей, которые влияют на поведение нашего кода.
Все мы знаем, что let создает константу, а var переменную. Но в контексте структур есть важные нюансы:
Проблемы с let и опциональными типами.
Когда мы используем let для опциональных свойств, мы обязаны явно передавать значения в инициализаторе, даже если хотим передать nil:
struct User {
let id: UUID
let name: String
let email: String?
}
// Ошибка компиляции: Missing argument for parameter 'email' in call
let user1 = User(
id: UUID(),
name: "Андрей"
)
// Обязаны явно указать nil для свойства email.
let user2 = User(
id: UUID(),
name: "Вася",
email: nil
)
Теперь посмотрим на пример с var:
struct User {
let id: UUID
let name: String
var email: String?
}
// Оба варианта работают:
let user1 = User(
id: UUID(),
name: "Петр"
)
let user2 = User(
id: UUID(),
name: "Иван",
email: nil
)
Использование let для опциональных свойств создает неожиданные сложности при создании экземпляров структур. Компилятор требует явно передавать значения для всех let-свойств в автоматическом инициализаторе, даже если мы хотим установить nil. Это приводит к громоздкому коду, где приходится постоянно указывать nil для необязательных полей. С var такой проблемы нет, опциональные свойства автоматически получают значение nil, если их не указать явно.
Дефолтные значения и let.
Комбинация let и дефолтных значений может привести к неочевидному поведению, особенно при работе с Codable.
// id не будет задаваться в инициализаторе, он всегда будет новым (даже при декодировании из JSON):
struct User {
let id = UUID()
let name: String
}
let user = User(name: "Максим")
Когда свойство объявлено как let с дефолтным значением, оно всегда будет использовать это значение, даже при декодировании из внешних данных. Это означает, что JSON с другим значением для этого поля будет проигнорирован, что часто является ошибкой.
Кастомные инициализаторы в помощь.
Если нужна читаемост и гибкость, то напишем свой инициализатор:
struct User {
let id: UUID
let name: String
let email: String?
init(
id: UUID = UUID(),
name: String,
email: String? = nil
) {
self.id = id
self.name = name
self.email = email
}
}
Создание собственного инициализатора позволяет обойти ограничения автоматического. Мы можем определить параметры с дефолтными значениями, сохраняя при этом иммутабельность через let. Такой подход дает контроль над процессом инициализации, но требует написания дополнительного кода и его поддержки.
Property Wrapper для иммутабельности.
Можно создать кастомное решение для read-only свойств с дефолтными значениями:
@propertyWrapper
struct Readonly<Value> {
let wrappedValue: Value
}
struct User {
@Readonly var id = UUID()
var name: String
@Readonly var email: String?
}
Обертка свойства
@Readonly имитирует поведение let, но с поддержкой дефолтных значений. Однако этот подход добавляет сложности и может сделать код менее читаемым.Начинайте с let для всех свойств, это обеспечивает безопасность и предсказуемость. Переходите на var только когда появляется реальная необходимость изменять данные.
Для опциональных свойств, где let создает неудобства, можно использовать кастомные инициализаторы с параметрами по умолчанию - это сохраняет иммутабельность без потери удобства. Идентификаторы и другие гарантированно неизменяемые данные всегда должны объявляться через let.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
1 19❤13👍13🙏4🔥2👏1👀1
Привет, друзья! Сегодня разберем как правильно организовать задержку выполнения кода в iOS-приложении.
DispatchQueue.main.asyncAfter.
Решение для задержки 2 секунды на главном потоке:
func showHelloMessage() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Привет!")
}
// Код продолжает выполняться здесь без задержек
}
Task + await.
Для асинхронного контекста:
func showHelloMessage() async {
try? await Task.sleep(for: .seconds(2))
print("Привет!")
}
Timer.
Если нужно повторять действие с интервалом:
func showHelloMessage() {
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
print("Привет!")
}
}
Как делать не нужно.
Выполняем действие через 2 секунды:
func showHelloMessage() {
Thread.sleep(forTimeInterval: 2.0)
print("Привет!")
}
Кажется, что все работает, но за этой простотой скрывается серьезные проблемы:
🔹 Полностью блокирует поток. Пока поток «спит», он не может выполнять другие задачи, которые вы можете ожидать.
🔹 Особенно опасен на главном потоке. Если вызвать Thread.sleep() на главном потоке, ваш интерфейс зависнет на эти 2 секунды. Пользователь не сможет прокручивать скролл, нажимать кнопки, приложение будет непригодным для использования в течении этого времени.
Используйте асинхронные подходы вместо блокирующих. Ваше приложение должно оставаться отзывчивым в любой момент времени.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет, друзья! Сегодня разберем важную тему: как правильно работать с календарем пользователя в iOS-приложениях. Многие проекты требуют доступа к событиям и напоминаниям, но не все знают тонкости реализации.
Безопасность прежде всего.
Доступ к календарю - это приватные данные пользователя. Apple строго контролирует такие запросы, поэтому первым делом настраиваем ключи в Info.plist:
<key>NSCalendarsFullAccessUsageDescription</key>
<string>Мы хотим добавить событие в ваш календарь</string>
<key>NSCalendarsWriteOnlyAccessUsageDescription</key>
<string>Приложение будет создавать события, но не читать существующие</string>
Практический пример запроса доступа.
Вот так реализуется запрос прав на доступ к событиям:
import EventKit
class CalendarManager: ObservableObject {
private let eventStore = EKEventStore()
@Published var accessStatus: EKAuthorizationStatus = .notDetermined
func requestAccess() async throws -> Bool {
let granted = try await eventStore.requestFullAccessToEvents()
await MainActor.run {
accessStatus = EKEventStore.authorizationStatus(for: .event)
}
return granted
}
}
Важные нюансы:
🔹 Write-only доступ: идеален для приложений, которые только добавляют события (билеты, бронирования).
🔹 Full access: нужен там, где требуется чтение и анализ расписания.
🔹 Статус авторизации всегда проверяйте перед операциями:
func canAddEvents() -> Bool {
let status = EKEventStore.authorizationStatus(for: .event)
return status == .authorized || status == .writeOnly
}Использование календаря.
После успешного получения прав можно работать с событиями. Вот базовые операции:
import EventKit
class CalendarManager {
private let eventStore = EKEventStore()
// Проверяем доступ и добавляем событие
func addQuickEvent(title: String) -> Bool {
guard EKEventStore.authorizationStatus(for: .event) == .authorized else {
print("Нет доступа к календарю")
return false
}
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = Date() // начинается сейчас
event.endDate = Date().addingTimeInterval(3600) // длится 1 час
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
print("Событие '\(title)' добавлено")
return true
} catch {
print("Ошибка: \(error.localizedDescription)")
return false
}
}
// Показываем все события на сегодня
func showTodaysEvents() {
guard EKEventStore.authorizationStatus(for: .event) == .authorized else { return }
let startDate = Date()
let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)!
let predicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: nil)
let events = eventStore.events(matching: predicate)
print("События на сегодня:")
for event in events {
print("• \(event.title ?? "Без названия") - \(event.startDate)")
}
}
}
// Использование
let calendarManager = CalendarManager()
// Добавляем событие
calendarManager.addQuickEvent(title: "Встреча с командой")
// Показываем события
calendarManager.showTodaysEvents()
Рекомендации по использованию.
🔹 Запрашивать доступ в контексте (когда пользователь пытается создать первое событие).
🔹 Предварительно проверять статус и показывать соответствующий UI.
🔹 Всегда обрабатывать сценарий отказа.
🔹 Тестируйте на реальном устройстве, симулятор может не полностью эмулировать права доступа.
EventKit требует строгого соблюдения протокола безопасности. Весь процесс работы с календарем строится на трех обязательных этапах: настройка прав доступа, проверка статуса авторизации и только затем выполнение операций с событиями.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Коллеги, давайте честно: если вы разработчик и не можете найти работу, скорее всего это не ваша вина. Рынок изменился, но он не «умер». Он просто стал другим, более жестким, более избирательным и требующим новых стратегий. И сейчас я объясню, почему это даже хорошо для тех, кто готов адаптироваться.
Раньше IT воспринималось как социальный лифт: прошел курсы -> получил зарплату в 2-3 раза выше средней. Сейчас медианная зарплата разработчика в России около 200 тысяч рублей. Это достойные деньги, но уже не запредельные. Курьеры в крупных городах зарабатывают сопоставимо, а иногда и больше. IT перестало быть гарантией быстрого обогащения, но осталось сферой, где можно строить осмысленную и интересную карьеру.
Почему рынок изменился:
🔹 Перенасыщение. После бума 2010-х годов на рынок хлынули тысячи новичков. Сегодня на одну вакансию мобильного разработчика могут претендовать сотни кандидатов. Рекрутеры вынуждены устраивать «заградительные собеседования», чтобы отсеять тех, кто не соответствует требованиям.
🔹 Экономический кризис. Высокая ключевая ставка и общая экономическая нестабильность заставили компании сокращать бюджеты. В приоритете поддержка существующих продуктов, а не запуск новых. Многие проекты заморожены, а найм сведен к минимуму.
🔹 Санкции и уход западных компаний. Закрытие Apple App Store для российских разработчиков, ограничения Google Play, все это сократило количество рабочих мест и заставило многих iOS-разработчиков переквалифицироваться.
🔹 Рост no-code решений. Такие технологии, как BDUI (Backend-Driven UI), позволяют быстро создавать и обновлять интерфейсы без глубоких знаний нативной разработки. Бизнес все чаще выбирает экономию времени и ресурсов.
🔹 ИИ как конкурент. GPT и другие модели уже сегодня генерируют код на уровне джуниоров. Скоро контекстное окно увеличится до миллиарда токенов, и ИИ сможет самостоятельно создавать сложные приложения, интегрируясь с внешними API. Это изменит роль разработчика с «пишущего код» на «архитектора и контролера».
Но это не конец. Это новая реальность, в которой выживут сильнейшие, те, кто готов учиться, адаптироваться и предлагать уникальную ценность.
Что делать:
🔹 Сменить фокус с технологий на архитектуру. Умение проектировать системы, а не просто писать код, станет ключевым навыком. Изучайте чистую архитектуру, SOLID, паттерны проектирования. Ваша цель стать не «разработчиком», а «архитектором, который решает бизнес-задачи».
🔹 Освоить смежные области. Fullstack, DevOps, безопасность - все, что делает вас универсальным специалистом. Например, умение работать с облачной инфраструктурой (Kubernetes, Docker) или понимание принципов кибербезопасности резко повысит вашу ценность.
🔹 Использовать ИИ как инструмент, а не костыль. Не копируйте код из ChatGPT, а используйте его для генерации идей, декомпозиции задач и поиска решений. Ваша роль - критически оценивать предложения ИИ и дорабатывать их.
🔹 Прокачивать софт-скиллы и личный бренд. В мире, где рутину забирает ИИ, ценность приобретают человеческие качества: креативность, коммуникация, умение работать в команде. Ведите блог, выступайте на митапах, общайтесь в профессиональных сообществах. Ваша репутация может стать главным активом.
🔹 Рассмотреть смежные профессии. Data Engineering, ИИ-разработка - это направления, где спрос будет только расти. Да, придется учиться с нуля, но это инвестиция в будущее.
Да, рынок разработки больше не будет прежним. Но это не трагедия, а возможность перезагрузки. Те, кто готов меняться, углублять знания и брать на себя больше ответственности, не просто выживут, они окажутся в выигрыше. Ваша карьера зависит не от внешних обстоятельств, а от готовности расти вместе с технологиями.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤18👍13🔥8🙏4🤔1 1
Часто в повседневной работе нам кажется, что массивы, словари и множества покрывают все задачи, но есть инструменты, которые могут сделать ваш код эффективнее, чище и надежнее. Сегодня разберем несколько структур из библиотеки swift-collections, которые решат проблемы, о которых вы могли не подозревать.
Deque.
Deque - это оптимизированная версия массива, которая позволяет добавлять и удалять элементы с начала и конца за O(1). В то время как Array тратит O(n) на вставку в начало.
Пример:
Например, код из фоторедактора с функцией отмены последних действий.
import Collections
final class ActionHistory<Action> {
private var history: Deque<Action> = []
private let capacity: Int
init(capacity: Int = 50) {
self.capacity = capacity
}
func addAction(_ action: Action) {
if history.count >= capacity {
history.removeFirst() // Удаляем самое старое действие
}
history.append(action)
}
func undo() -> Action? {
history.popLast()
}
}
При частом изменении начала коллекции Deque работает значительно быстрее Array.
Heap.
Heap автоматически сортирует элементы по приоритету. Например, идеально для обработки задач с разной срочностью.
Пример:
Система уведомлений, где сообщения от администратора должны показываться первыми.
struct Notification: Comparable {
let message: String
let priority: Int
static func < (lhs: Notification, rhs: Notification) -> Bool {
lhs.priority < rhs.priority
}
}
var notifications = Heap<Notification>()
notifications.insert(Notification(message: "Обновление системы", priority: 1))
notifications.insert(Notification(message: "Новое сообщение", priority: 3))
// Сначала получим уведомление с priority = 3
let nextNotification = notifications.popMax()
OrderedSet.
OrderedSet - это гибрид Array и Set, который сохраняет порядок элементов и гарантирует их уникальность.
Пример:
Список недавно просмотренных товаров в маркетплейсе, где важно и избежать дублей, и сохранить порядок.
var recentProducts = OrderedSet<String>()
recentProducts.append("iPhone 17")
recentProducts.append("iPad Pro")
recentProducts.append("iPhone 17") // Не добавится
print(recentProducts) // ["iPhone 17", "iPad Pro"]
OrderedDictionary.
OrderedDictionary - это словарь, который запоминает порядок добавления элементов.
Пример:
Кэширование данных для экрана списка, где порядок элементов важен.
var userCache = OrderedDictionary<String, User>()
userCache["user_1"] = User(name: "Анна")
userCache["user_2"] = User(name: "Иван")
// Порядок сохраняется при обходе
for (id, user) in userCache {
print("\(id): \(user.name)") // user_1: Анна, user_2: Иван
}
Зачем это все нужно:
🔹 Производительность: правильные структуры данных экономят ресурсы.
🔹 Читаемость: код становится проще для понимания.
🔹 Надежность: меньше шансов допустить ошибку в реализации.
Разработка - это не только о написании работающего кода, но и о выборе оптимальных инструментов. Постепенно расширяя свой арсенал, вы не просто пишете код, а создаете эффективные и масштабируемые решения.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23 15🙏8🔥2 2❤1
Друзья, привет! Apple выпустила долгожданную библиотеку Swift Configuration - универсальное решение для управления настройками в Swift-проектах. Расскажу, чем она полезна и как начать ее использовать.
Что такое Swift Configuration:
Данная библиотека предоставляет единый интерфейс для чтения настроек из различных источников. Больше не нужно писать разный код для работы с переменными окружения, JSON-файлами и аргументами командной строки.
Основные возможности:
🔹 Единый API: одинаковый код для всех источников настроек.
🔹 Горячее обновление: изменения применяются без перезапуска приложения.
🔹 Поддержка вложенности: работа со сложными структурами настроек.
🔹 Приоритеты источников: четкая иерархия важности настроек.
Пример:
import Configuration
// Настройка чтения из разных источников
let config = ConfigReader(providers: [
EnvironmentVariablesProvider(), // Переменные окружения
CommandLineArgumentsProvider(), // Аргументы командной строки
try await JSONProvider(filePath: "/app/config.json") // JSON-файл
])
// Чтение значения (ищет в порядке приоритета)
let timeout = config.int(forKey: "http.timeout", default: 30)
let host = config.string(forKey: "database.host", default: "localhost")
Как это работает в реальном проекте:
Представьте, у вас есть базовые настройки в JSON:
{
"database": {
"host": "localhost",
"port": 5432
}
}
Но для production вы можете переопределить их через переменные окружения:
export DATABASE_HOST=production_db.test.ru
Библиотека автоматически выберет значение из переменной окружения как более приоритетное.
Преимущества:
🔹 Снижение количества шаблонного кода: больше не нужно писать парсеры для каждого типа конфигов.
🔹 Упрощение тестирования: легко подменять настройки в тестах.
🔹 Гибкость развертывания: разные среды через разные источники настроек.
Swift Configuration - это серьезный шаг к стандартизации работы с настройками в Swift-экосистеме. Библиотека пока сыровата для продакшена, но идеи стоят внимания. Особенно ценна унификация доступа к разным источникам конфигов.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Здравствуйте, друзья! Сегодня мы разберем как тестировать пуш-уведомления на симуляторе без заморочек с сервером и реальными устройствами. Это особенно актуально когда нужно быстро проверить внешний вид или логику обработки уведомлений.
Подготовка: запрашиваем разрешения.
Первым делом нужно запросить разрешение на показ уведомлений. Без этого даже идеально настроенный пуш не сработает:
import UserNotifications
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge]
) { granted, error in
print("Разрешение получено: \(granted)")
}
Два способа отправки пушей.
Способ 1: Drag & Drop (самый простой)
Создаем JSON-файл notification.apns с содержимым уведомления:
{
"aps": {
"alert": {
"title": "Новое сообщение",
"body": "Вам пришло новое сообщение в чате"
},
"badge": 1,
"sound": "default"
},
"Simulator Target Bundle": "com.yourapp.bundleid"
}
Просто перетаскиваем файл на симулятор, уведомление появится мгновенно!
Способ 2: Через терминал (для автоматизации)
Узнаем ID симулятора:
xcrun simctl list
Отправляем пуш:
xcrun simctl push booted com.yourapp.bundleid notification.apns
Ключевое слово booted автоматически определяет запущенный симулятор.
Что еще можно протестировать.
На симуляторе отлично работают:
🔹 Rich-уведомления с картинками.
🔹 Кастомные звуки.
🔹 Action-кнопки.
🔹 Deep links.
🔹 Badge-иконки.
Частые ошибки:
🔹 Не указан Simulator Target Bundle: симулятор не поймет, какому приложению предназначен пуш.
🔹 Нет разрешений: забыли вызвать requestAuthorization.
🔹 Неверный bundle identifier: проверьте точное написание в проекте.
Тестировать пуш-уведомления на симуляторе стало проще простого. Не нужно настраивать серверную часть, достаточно JSON-файла и пары команд. Единственное ограничение - некоторые продвинутые функции вроде фоновых обновлений контента лучше проверять на реальном устройстве. Но для 90% повседневных задач симулятора более чем достаточно.
Стоит отметить, что на iOS 26 симуляторах иногда наблюдаются проблемы, при тестировании пуш-уведомлений. Если у вас возникли проблемы, попробуйте протестировать на предыдущих версиях iOS.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
This media is not supported in your browser
VIEW IN TELEGRAM
Пока все обсуждают ChatGPT и Copilot, легендарный Stack Overflow тихо готовит ответный удар. И у них есть серьезное преимущество: гигантская база проверенных ответов, которые создавали мы с вами все эти годы.
Что происходит?
Stack Overflow анонсировал собственную ИИ-систему Overflow AI. И это не просто очередной чат-бот, а интеграция их огромной базы знаний в рабочие процессы разработчиков.
Ключевые фишки:
🔹 Умный поиск: можно задавать вопросы естественным языком.
🔹 Интеграция с VS Code: помощь прямо в редакторе кода.
🔹 Slack-бот: ответы на технические вопросы в рабочих чатах.
🔹 Корпоративная версия: поиск по внутренним базам знаний компаний.
Мое мнение:
Stack Overflow может стать антидотом от ИИ-багов. Особенно ценна корпоративная версия, представьте, ИИ ищет ответы не только в публичной базе, но и в вашем внутреннем Confluence, GitHub и документации.
Игра только начинается. Stack Overflow может занять нишу правильного ИИ для разработчиков, где важна точность, а не креативность.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤21🔥14👍9👏3👀1 1
Друзья, привет! Случилось то, о чем многие давно мечтали, но мало кто верил это произойдет! После месяцев работы рабочей группы по поддержке Android, компания Apple выпустила первую версию Swift SDK для разработки под операционную систему Android.
Масштабы поддержки:
Уже сейчас 25% пакетов в Swift Package Index поддерживают сборку для Android. Среди них многие популярные библиотеки, которые мы используем в повседневной разработке.
Стоит отметить, что код компилируется на Java и Kotlin, так как Android пока еще нативно не поддерживает Swift.
Что нужно для старта:
🔹 Установите Swift 6.3: раннюю версию с поддержкой Android.
🔹 Загрузите Swift SDK для Android: отдельно или в составе установщика для Windows.
🔹 Добавьте Android NDK: для нативной компиляции.
Подробные инструкции уже доступны в официальной документации.
Почему это важно:
🔹 Больше не нужно учить Kotlin/Java для разработки под Android, если вы владеете Swift.
🔹 Единая бизнес-логика, кодовая база и архитектурные подходы на обеих платформах.
🔹 Снижение затрат, одна команда вместо двух раздельных под каждую из платформ.
Перспективы:
Рабочая группа уже готовит дорожную карту развития платформы. В планах: улучшение отладки, оптимизация производительности и расширение поддержки Android API.
Что важно понимать:
🔹 Это предварительная версия, некоторые функции ещё в разработке.
🔹 Требуется время на стабилизацию и полировку.
🔹 Не все iOS-библиотеки сразу будут работать на Android.
Swift на Android - это не просто ещё одна кроссплатформенная технология, это возможность создать по-настоящему единую кодовую базу с нативной производительностью на языке программирования Swift.
Это может изменить расстановку сил на рынке мобильной разработки. KMM, Flutter и React Native получают мощного конкурента, а нативные разработчики новые возможности.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25❤15👍9🤔2 2 1
С выходом Swift 6.2 появился новый мощный инструмент для наблюдения за изменениями в
@Observable моделях. Сейчас расскажу, как это работает и чем отличается от предыдущих подходов.Что изменилось:
Раньше мы использовали withObservationTracking с его неудобным API и семантикой will set. Теперь появился объект Observations, который создает AsyncSequence для отслеживания изменений свойств.
Пример:
Допустим, у нас есть модель счетчика:
@Observable
class Counter {
var count = 0
var name = "Счетчик"
}
Старый подход:
class CounterObserver {
func observeOldWay(counter: Counter) {
withObservationTracking {
print("Текущее значение: \(counter.count)")
} onChange: {
self.observeOldWay(counter: counter) // Рекурсивный вызов
}
}
}
Новый подход:
class CounterObserver {
func observeNewWay(counter: Counter) {
Task { [weak self] in
let values = Observations { [weak self] in
guard let self else { return "" }
// Наблюдаем за count и name, но возвращаем форматированную строку
return "\(self.counter.name): \(self.counter.count)"
}
for await value in values {
print("Обновление: \(value)")
// Обновляем UI или выполняем другую логику
}
}
}
}
Еще один пример:
class UserProfileManager {
@Observable class UserProfile {
var username: String
var email: String
var isOnline: Bool
}
func startObservingProfile() {
Task { [weak self] in
let profileUpdates = Observations { [weak self] in
guard let self else { return false }
// Наблюдаем за несколькими свойствами
let status = self.userProfile.isOnline
let name = self.userProfile.username
return (status, name)
}
for await (isOnline, username) in profileUpdates {
await self?.updateUserInterface(isOnline: isOnline, username: username)
}
}
}
}
Преимущества нового подхода:
🔹 Более чистый код: никакой рекурсии.
🔹 AsyncSequence: интегрируется с современным асинхронным Swift.
🔹 Гибкость: можно возвращать любой тип данных.
🔹 Автоматическое отслеживание: наблюдаются все свойства, доступные в closure.
Observations существенно упрощает отслеживание изменений данных по сравнению с предыдущими подходами. Теперь не нужно писать рекурсивные вызовы, достаточно создать асинхронную последовательность и обрабатывать обновления в цикле.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19 15❤9🔥2👀2🙏1 1
В iOS 26.1 компания Apple представила новый тип расширений для PhotoKit: Background Resource Upload. Это обновление, которое перекладывает управление фоновыми загрузками на саму операционную систему.
Что это такое?
PHBackgroundResourceUploadExtension - это новый протокол, который позволяет системе управлять загрузкой ресурсов от имени вашего приложения. Теперь iOS сама обрабатывает загрузки, даже когда пользователь переключился на другое приложение или заблокировал устройство.
Как это работает.
Система предоставляет два основных метода:
// Запрос на обработку фоновых загрузок
func process() -> PHBackgroundResourceUploadProcessingResult
// Уведомление о завершении работы
func notifyTermination()
Ключевые компоненты API:
🔹 PHBackgroundResourceUploadExtension: основной протокол для расширений фоновой загрузки.
🔹 PHBackgroundResourceUploadProcessingResult: enum с результатами обработки.
🔹 PHAssetResourceUploadJob: представляет запрос на загрузку ресурса PHAsset.
🔹 PHAssetResourceUploadJobChangeRequest: для управления записями загрузок.
Преимущества подхода:
🔹 Автоматическое управление сетью: система сама оптимизирует подключения.
🔹 Энергоэффективность: загрузки в оптимальное время для батареи.
🔹 Надежность: обработка даже при блокировке устройства.
🔹 Умные тайминги: система выбирает лучшее время для загрузок.
Для каких задач подходит.
Идеально для приложений, которые:
🔹 Выполняют бэкап фотографий в облако.
🔹 Синхронизируют медиаресурсы с серверами.
🔹 Загружают тяжелые видеофайлы.
🔹 Работают с большими коллекциями изображений.
Важные нюансы:
🔹 Пока доступно только в PhotoKit.
🔹 API находится в бета-версии.
🔹 Требуется iOS 26.1+
🔹 Документация может измениться.
Новый API Background Resource Upload - это серьезное упрощение для разработчиков, работающих с медиаресурсами. Система берет на себя всю сложность управления фоновыми задачами, позволяя сосредоточиться на бизнес-логике приложения.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Сегодня поговорим о боли каждого iOS-разработчика: как тестировать приватные методы и свойства без нарушения принципов инкапсуляции. Оказывается, есть способ делать это элегантно!
Проблема, знакомая каждому.
Представьте ситуацию: у вас есть класс с приватным кэшем:
class PaymentProcessor {
private var cache: [String: Payment] = [:]
func processPayment(_ payment: Payment) {
// Сложная логика с использованием cache
}
}
Как протестировать, что кэш действительно работает? Раньше были только два неидеальных варианта:
🔹 Сделать cache internal: нарушаем инкапсуляцию.
🔹 Тестировать только через публичные методы: тесты становятся сложными и хрупкими.
Решение.
Swift предлагает атрибут
@_private(sourceFile:), который позволяет получить доступ к приватным членам в тестах:
#if ENABLE_PRIVATE_TESTS
@_private(sourceFile: "PaymentProcessor.swift")
import MyApp
#endif
func testCacheBehavior() {
let processor = PaymentProcessor()
// Теперь можем напрямую работать с приватным cache!
XCTAssertTrue(processor.cache.isEmpty)
}
Настройка.
🔹 Шаг 1: Включаем приватные импорты в основном модуле. В Package.swift:
.target(
name: "MyApp",
swiftSettings: [
.unsafeFlags(["-Xfrontend", "-enable-private-imports"])
]
)
🔹 Шаг 2: Условная компиляция в тестах:
#if ENABLE_PRIVATE_TESTS
@_private(sourceFile: "PaymentProcessor.swift")
import MyApp
#endif
🔹 Шаг 3: Настраиваем переменную окружения. В схеме тестирования:
ENABLE_PRIVATE_TESTS=1
@_private(sourceFile:) - инструмент для серьезных проектов, где важны качественные тесты. Используйте его осознанно, всегда оборачивайте в флаги компиляции и будьте готовы к тому, что в будущих версиях Swift он может перестать работать.Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥13🤔6👀5 2❤1🙏1🗿1
Друзья, Apple продолжает сближать UIKit и SwiftUI! В iOS 26 обе технологии получают мощные обновления навигации, которые избавят нас от кастомных решений и костылей.
Что нового в UIKit.
Подзаголовки в UINavigationItem:
// Базовый подзаголовок
let navItem = navigationItem
navItem.title = "Профиль"
navItem.subtitle = "Премиум аккаунт"
// Стилизованная версия
let attributedSubtitle = AttributedString(
"Активен до 25.12.2026",
attributes: AttributeContainer([
.foregroundColor: UIColor.systemGreen,
.font: UIFont.systemFont(ofSize: 12, weight: .medium)
])
)
navItem.attributedSubtitle = attributedSubtitle
Гибкий поиск с новыми вариантами размещения:
// Разные варианты поиска
searchController.searchBarPlacement = .integrated // Встроенный
searchController.searchBarPlacement = .integratedButton // Как кнопка
searchController.searchBarPlacement = .integratedCentered // Центрированный
Что нового в SwiftUI.
Подзаголовки в NavigationStack:
struct ProfileView: View {
var body: some View {
NavigationStack {
Text("Контент профиля")
.navigationTitle("Профиль")
.navigationSubtitle("Премиум аккаунт") // Новое в iOS 26!
}
}
}
Liquid Glass для навигации:
.navigationBarTitleDisplayMode(.large)
.background(.ultraThinMaterial) // Эффект стекла
iOS 26 делает навигацию единой для UIKit и SwiftUI. Больше не нужно выбирать между технологиями, можно создавать современные интерфейсы используя нативные API обеих платформ.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Создание миниатюр - одна из самых распространенных задач в мобильной разработке. Но не все методы одинаково эффективны. Рассмотрим, как разные подходы показывают себя на практике при работе с 12-мегапиксельным JPEG.
Классические методы показывают разную производительность:
🔹 NSImage на macOS: 710 мс.
🔹 UIGraphicsImageRenderer на iOS: 210 мс.
🔹 UIImage.prepareThumbnail() на iOS: 130 мс.
Специализированные методы демонстрируют лучшие результаты:
🔹 CGImageSource на macOS: 16-26 мс.
🔹 CGImageSource на iOS: 24-95 мс в зависимости от формата.
Пример использования CGImageSource:
let source = CGImageSourceCreateWithData(imageData as CFData, nil)!
let cgThumb = CGImageSourceCreateThumbnailAtIndex(source, 0, [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height)
] as CFDictionary)!
let thumbnail = UIImage(cgImage: cgThumb) // или NSImage в macOS
Ключевые параметры:
🔹 kCGImageSourceCreateThumbnailFromImageAlways: гарантирует создание миниатюры.
🔹 kCGImageSourceCreateThumbnailWithTransform: учитывает ориентацию изображения.
🔹 kCGImageSourceThumbnailMaxPixelSize: задает максимальный размер.
Получение информации о размерах:
let source = CGImageSourceCreateWithData(data as CFData, nil)!
if let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any],
let width = properties[kCGImagePropertyPixelWidth] as? Int,
let height = properties[kCGImagePropertyPixelHeight] as? Int {
print("Ширина: \(width), высота: \(height)")
}
Преимущества CGImageSource:
🔹 Мгновенное получение размеров изображения (2-4 мс).
🔹 Работа с различными форматами (HEIC, JPEG, PNG).
🔹 Эффективное использование памяти.
🔹 Поддержка последовательной загрузки.
CGImageSource показывает стабильно высокую производительность на обеих платформах, особенно заметно ускорение на macOS: до 40 раз по сравнению с классическими методами. Для задач, связанных с отображением галерей или списков изображений, этот подход становится оптимальным выбором.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Друзья, давайте честно: написание кода - это самая приятная, но далеко не самая большая часть нашей работы. Сегодня хочу поделиться типичным сценарием, который повторяется в проектах снова и снова.
Цикл разработки, который знаком каждому:
Неделя 1: начало разработки.
Вы получаете задачу, погружаетесь в контекст, продумываете архитектуру и бодро пишете код. Все идет по плану, фича почти готова. Вы чувствуете удовлетворение от хорошо сделанной работы и отправляете задачу на тестирование.
Неделя 2: ожидания тестирования.
Отдел тестирования перегружен, ваша задача висит в статусе «готова к тестированию». Вы переключаетесь на другие задачи и проекты, постепенно забывая детали реализации. Контекст стирается, а энтузиазм угасает.
Неделя 3: неожиданное возвращение.
Внезапно приходит сообщение: «Задача возвращена на доработку». Оказывается, требования изменились, нашли краевой случай или продукт-менеджер придумал улучшение. Теперь вам нужно заново вникать в задачу, которую вы уже считали завершенной.
Немного статистики:
🔹 Каждая третья фича возвращается на доработку минимум один раз.
🔹 15% задач требуют кардинальных изменений после тестирования.
🔹 Среднее время ожидания тестирования: от 2 дней до 7 дней.
🔹 Большинство разработчиков одновременно ведут несколько задач.
Почему так происходит:
Бизнес-требования постоянно эволюционируют: рынок не стоит на месте. То, что было актуально месяц назад, сегодня может устареть. Пользователи дают обратную связь, которая подсвечивает неочевидные проблемы. Тестирование выявляет ситуации, которые невозможно было предвидеть на этапе проектирования.
Как с этим работать:
Ментальные практики:
🔹 Не привязываться эмоционально к написанному коду.
🔹 Воспринимать изменения не как неудачу, а как возможность сделать продукт лучше.
🔹 Понимать, что выпиливание фичи - это не личное поражение, а забота о пользователе.
Построить рабочие процессы:
🔹 Организовать регулярные встречи с продукт-менеджерами для синхронизации.
🔹 Проводить промежуточные демо для стейкхолдеров до финального тестирования.
🔹 Выполнить детальные чек-листы перед сдачей задачи.
🔹 Назначить четкие критерии приемки для каждой фичи.
Процесс разработки - это живой организм, который постоянно меняется. Умение адаптироваться, сохранять гибкость и не терять энтузиазм в цикле бесконечных доработок - вот что отличает профессионала.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19❤14🙏10👀6🤝2 1
Привет, друзья! Сегодня поговорим о нововведения в Xcode 26.1 beta, которые повлияют на нашу повседневную работу:
SPM стал чуть умнее.
Локальное кэширование пакетов из реестра на час - это мелкое, но важное улучшение. Представьте: у вас в команде 10 человек, и все одновременно запускают pod install после обновления версии зависимости. Раньше каждый качал одно и то же, нагружая сеть. Теперь первый, кто скачал, становится локальным «источником» на час. Мелочь, а приятно, особенно для больших команд.
Instruments снова в строю.
На новых устройствах отказывал работать Processor Trace. Теперь это починили. Для тех, кто занимается глубокой оптимизацией и профилированием, это огромное облегчение. Теперь не нужно держать под рукой старый девайс, чтобы найти узкое место в производительности.
Coding Assistant перестал жрать память.
У кого были проекты с гигантскими git-репозиториями (например, с кучей субмодулей), тот знает, как Coding Assistant мог превратить MacBook в печку. Улучшение потребления памяти - это не про «удобства», это про возможность вообще работать с этим инструментом на реальных проектах.
Симулятор перестал чудить с JSON.
Команда simctl list devices --json снова работает корректно. Казалось бы, ерунда. Но это большая ерунда для всех, кто завязал на нее свои CI/CD-скрипты для автоматического тестирования на разных типах девайсов. Скрипты снова стали предсказуемыми.
Известные проблемы: будьте осторожны!
А вот здесь классика жанра. Apple выпускает бету, но перекладывает часть проблем на нас.
🔹 Симуляторы не запускаются. Первая сборка может упереться в то, что симулятор не загрузится.
Решение: Apple рекомендует вручную выполнить в терминале:
xcrun simctl runtime dyld_shared_cache update --all
🔹 Coding Assistant все еще подводит. Инструмент поиска по файлу может возвращать неверные номера строк. Представьте: вы хотите заменить все вхождения oldVariable на newVariable, а он из-за сбитого номера строки меняет не то, что нужно, и ломает вам логику. Пока что на этот инструмент лучше не полагаться в критичных операциях.
Xcode 26.1 beta - это типичный «качельный» апдейт. С одной стороны, мы видим долгожданные и очень точечные фиксы старых проблем (особенно в Instruments и SPM), которые реально облегчают жизнь. С другой получаем новые сюрпризы, которые могут остановить работу.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM