EasySwift iOS🍏
3.02K subscribers
268 photos
8 videos
393 links
Все самое интересное в мире iOS разработки 🧑🏻‍💻

Предложить статью или новость: @EasySwiftBot

По всем вопросам обращаться к @itereznikov
Download Telegram
The Memory Leak: An Xcode Detective Story

История о том, как инженер пытался отладить простой по описанию баг и как глубоко он зашел. Если очень коротко, то проблема была в неправильном использовании [weak self] в кложуре 🫠

Понимание механизмов работы с памятью помогает избегать таких ситуаций.
4🔥21
Announcing Swift 6

Анонсировали релиз Swift 6 🥳 и нас ждет очень много изменений, вот небольшой список:
- поддержка 128-битных целых чисел
- новая библиотека синхронизации, которая включает примитивы, такие как mutex
- новый подход к выбрасыванию ошибок
- владение некопируемых типов
- расширение интеропа с C++
- новый макрос для отображения информации в LLDB @DebugDescription
- фреймворк для тестирования Swift Testing
🔥10
AVAudioSourceNode, AVAudioSinkNode: Low-Level Audio In Swift

AVAudioSourceNode
– узлы AVAudio, предоставляющие способ обработки входных и выходных аудиоданных. Для работы с аудио в режиме реального времени, придется выйти на уровень работы с указателями через UnsafeMutableAudioBufferListPointer и UnsafeMutableBufferPointer. Стоит помнить, что в таком случае управление памятью ложиться на плечи разработчика.

Если вам интересна работа со звуком на низком уровне – эта статья точно для вас.
🔥3
How to keep Date’s microseconds precision in Swift

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

Автор предлагает использовать расширение ISO8601DateFormatter для ручного извлечения микросекунд из строки даты и добавления их к сконвертированному значению Date.


extension ISO8601DateFormatter {
func microsecondsDate(from dateString: String) -> Date? {
guard let millisecondsDate = date(from: dateString) else { return nil }
guard let fractionIndex = dateString.lastIndex(of: ".") else { return millisecondsDate }
guard let tzIndex = dateString.lastIndex(of: "Z") else { return millisecondsDate }
guard let startIndex = dateString.index(fractionIndex, offsetBy: 4, limitedBy: tzIndex) else { return millisecondsDate }
// Pad the missing zeros at the end and cut off nanoseconds
let microsecondsString = dateString[startIndex..<tzIndex].padding(toLength: 3, withPad: "0", startingAt: 0)
guard let microseconds = TimeInterval(microsecondsString) else { return millisecondsDate }
return Date(timeIntervalSince1970: millisecondsDate.timeIntervalSince1970 + microseconds / 1_000_000.0)
}
}
2👍2
Псевдоним типа в Swift

Ключевое слово typealias в Swift позволяет создавать удобный для восприятия код, например:


// Повысить восприятие кода
typealias StringDictionary<T> = Dictionary<String, T>

// Упростить сигнатуры сложных типов и кортежей
typealias CompletionHandler = (Result<String, Error>) -> Void
typealias GridPoint = (x: Int, y: Int)

// Указать ограничения
typealias StringArrayDictionary = Dictionary<String, [String]>



В статье больше примеров с разборами и пояснениями.
🔥11
Как встроить распознавание звуков в ваше iOS приложение на Swift с использованием SoundAnalysis

Если вы хотели попробовать реализовать приложения для распознавания звуков – это статья для вас.
Автор разбирает базовые концепции работы с фреймворком SoundAnalysis. При этом вы можете воспользоваться моделями из коробки, они позволяют распознавать до 300 различных звуков, либо натренировать свою модель через CreateML.
🔥5
Прокачайте свой код с @dynamicMemberLookup

Атрибут dynamicMemberLookup в Swift позволяет обращаться к свойствам объекта, как если бы они были статически определены, что упрощает работу с динамическими структурами данных и делает API более гибким и расширяемым.


json[0]?["name"]?["first"]?.stringValue

// заменится на

json[0]?.name?.first?.stringValue


Но тут есть минус – компилятор не сможет подсказать вам, обращаетесь ли вы к существующему свойству или нет.

В статье разбираются основные моменты при работе с данным атрибутом на примере.
🔥4
Data alignment: Straighten up and fly right

Хорошо проработанная статья на инженерном языке про работу процессора.

Главная мысль – раскрыть концепцию работы процессора: как он обращается к памяти в 2, 4, 8, 16 или 32-байтовых фрагментах, а не в байтовых кусках, как мы привыкли думать.
3
UNSAFE SWIFT

Уже не новая, но актуальная статья про использование указателей в Swift.
Автор подробно разбирает выравнивание: для чего это может понадобиться, почему порядок типов в объекте имеет значение и как работать с объектами через один из 4 доступных указателей: UnsafeRawPointer, UnsafeMutableRawPointer, UnsafeRawBufferPointer и UnsafeMutableRawBufferPointer


class Foo {
let a: UInt8
let b: UInt16
}
MemoryLayout<Foo>.size // 8
MemoryLayout<Foo>.alignment // 8
MemoryLayout<Foo>.stride // 8


Понимание того, как это работает важно для организации высокопроизводительного кода.
🔥11👀2
Как ускорить запуск iOS-приложения в 2 раза с помощью Network Instrument

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

В статье про использование Network Instrument, который позволяет выявить и решить множество проблем, например, неэффективное использование URLSession, блокирование запросов подключением, долгие редиректы и парсинг больших объемов данных.
🔥5
Как реализовать спойлер-эффект как в Telegram на Swift?

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

Для достижения нужного эффекта автор использует CAEmitterLayer. Однако, помимо лейера тут еще очень много различных тонкостей, которые с первого взгляда не очевидны.
🔥41
Beware UserDefaults: a tale of hard to find bugs, and lost data

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

Автор предлагает несколько решений, включая проверку isProtectedDataAvailable и использование собственной реализации UserDefaults для надежного доступа к данным.
🔥3
The perfect iOS networking layer does not exist - Part 1

Несмотря на название, автор приводит пример написания достаточно универсального и самодостаточного сетевого слоя.

Внутри используется async/await, а также внедряются промежуточные сущности для модификации уже собранных реквестов, например, логированием.
🔥5
The perfect iOS networking layer does not exist - Part 2

Продолжение статьи про написание сетевого слоя.

В этой части разбирается подход к тестированию уже написанного ранее слоя. Это часто бывает полезно и удобно, когда сам бекэнд еще не готов, а фронт уходит вперед. Покрытие самого клиента, сервиса запросов, а также middleware сущностей.

Будет полезным для тех, кто немного работал с тестами – дает базовое понимание о том, каким образом можно писать тесты.
🔥3
Calculating the semantic distance between words with the Natural Language framework

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

Класс NLEmbedding позволяет создавать векторное пространство, в котором строки представлены в виде векторов, и рассчитывать семантическое расстояние между словами, а также находить синонимы.

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


import NaturalLanguage

func getSemanticDistance(for word: String, in language: NLLanguage) {

// 1. Create the embedding for the language the word belongs to
if let embedding = NLEmbedding.wordEmbedding(for: language) {

// 2. Find the neighbors for the word
embedding.enumerateNeighbors(for: word, maximumCount: 10) { neighbor, distance in

// 3. Acces the neighbor distance from the word
print("\(neighbor): \(distance)")

return true
}
}
}
🔥32👍2
Faster iOS Networking with Shared Dictionary Compression

Сетевые задержки остаются проблемой для пользовательского опыта на iOS устройствах, и одним из способов улучшения этой ситуации является использование shared dictionary compression.

Что такое shared dictionary compression?

Это метод сжатия, при котором клиент и сервер обмениваются копией некоторых данных (словаря), что позволяет уменьшить размер запросов.

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

Однако, стоит понимать, что без доработок со стороны бекэнда данная практика невозможна 🫢
👍5🤯1
Корзина в Додо Пицце на iOS 14: баг длиной в полгода

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

Сама же проблема была простая: после редизайна приложения Додо Пиццы, кнопка корзины стала невидимой для пользователей на iOS 14, вызывая жалобы и проблемы с отображением стоимости продуктов. И фикс был в пару строк 🫣
👍51👎1🤔1🤬1
Beginner’s Guide to Protocol Buffers and gRPC with Swift

Отличная статья про Protocol Buffer или «protobuf», разбираются основные особенности его использования и gRPC.
Также автор описывает подробные шаги по настройке базового сервера gRPC в Swift с использованием библиотек Swift Protobuf и gRPC, включая создание структур данных Swift, создание интерфейса gRPC и внедрение интерфейса на стороне сервера.
Кстати, для сети он использует акторы и async/await.

func completeTodo(
request: ServerRequest<Todos_TodoID>,
context: ServerContext
) async throws -> ServerResponse<Todos_Todo> {
guard
var todo = todos.first(where: { $0.todoID == request.iss.onessage.todoID })
else {
return .init(
error: RPCError.init(
code: .notFound,
message: "Todo not found."
)
)
}
todo.completed = true
todos = todos.filter { $0.todoID != request.iss.onessage.todoID }
todos.append(todo)
return .init(message: todo)
}
🔥4
Азартная разработка iOS приложения игры 2048 с ChatGPT

Объемная и подробная статья про совместную разработку с ChatGPT известной игры 2048.

Также в игру вводится новый элемент: ИИ - алгоритм, который будет автоматически ходить. В качестве примера были взяты два: Monte Carlo и Expectimax

Все достаточно подробно описано и сопровождается обильным количеством скриншотов.
🔥4
Why Do View Controllers Need init(coder:)?

Класс UIViewController поддерживает протокол NSCoding, что требует реализации инициализатора init(coder:), даже если он не используется напрямую.

Даже при создании view controllers программно, необходимо использовать UIStoryboards, которые в процессе создания используют NSCoding для воссоздания состояния view controller и его представлений.
🔥2👎1