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

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

По всем вопросам обращаться к @itereznikov
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Пятничный Vibe-coding

👀 Решил попробовать этот ваш вайб кодинг

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

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

Но это намного лучше и быстрее, чем писать все изначально с 0 🔥

P.S. Я использовал Cursor и плагин для фигмы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥3
Building Type‑Safe, High‑Performance SwiftData / Core Data Models

🔍 В статье приводятся советы, как сделать модели для CoreData/SwiftData типо-безопасными и эффективными.

Вот некоторые моменты:

➡️ В отличие от SwiftData, CoreData требует использования NSNumber для опциональных числовых свойств, что нарушает стиль Swift. Решение включает использование вычисляемых свойств для обеспечения безопасности типов.

➡️ Использование библиотеки NonEmpty позволяет гарантировать, что строка не пустая, что улучшает семантику и предотвращает ошибки на этапе компиляции.

➡️ Для многовариантных выборок рекомендуется использовать битовые операции для хранения данных, что значительно повышает производительность запросов в SQLite.

➡️ SwiftData требует явного определения инициализаторов, что помогает предотвратить ошибки и обеспечивает строгий порядок создания моделей.

extension DataContent {
// Public array interface
public var optionSelections: [Int] {
get { optionIDsNumber.toArray() }
set { optionIDsNumber = optionIDsToInt64(optionIDs: newValue) }
}

// Stored as a bitmask
private var optionIDsNumber: Int64
}

func optionIDsToInt64(optionIDs: [Int]) -> Int64 {
var result: Int64 = 0
for id in optionIDs where id >= 0 && id <= 63 {
// Set the bit corresponding to each ID
result |= (1 << id)
}
return result
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32
How a Single Line Of Code Could Brick Your iPhone

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

🔔 Уязвимость в Darwin notifications позволяет любому процессу на iOS отправлять уведомления без специальных привилегий.

Вот эта строка:

notify_post("com.apple.MobileSync.BackupAgent.RestoreStarted")


+ виджет и fatalError() окирпичивали телефон. 🆒

🗓 Первоначальный отчет о проблеме был отправлен в Apple 26 июня 2024 года, а уязвимость была исправлена в iOS 18.3 с присвоением CVE-2025-24091.

💵 За это автор получил вознаграждение в размере 17.5 к $.

✔️ Теперь для отправки чувствительных уведомлений Darwin требуется наличие ограниченных прав, что предотвращает несанкционированные действия со стороны приложений.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍1
Swift Error Handling Done Right: Overcoming the Objective-C Error Legacy

👀 Описываешь ошибки в коде, которые конформят Error протокол, и ожидаешь получить внятное объяснение при выбрасывании, но вместо этого:
"The operation couldn't be completed. (YourApp.YourError error 0.)"

enum NetworkError: Error {
case noConnectionToServer
case parsingFailed

var localizedDescription: String {
switch self {
case .noConnectionToServer:
return "No connection to the server."
case .parsingFailed:
return "Data parsing failed."
}
}
}

// Using the error
do {
throw NetworkError.noConnectionToServer
} catch {
print("Error message: \(error.localizedDescription)")
// Expected: "No connection to the server."
// Actual: "The operation couldn't be completed. (AppName.NetworkError error 0.)"
}


Ну было, да? 🤦‍♂️

Если ответ положительный, то вам нужно прочитать эту статью.

Коротко:

✔️ Swift предлагает использовать протокол LocalizedError для обработки ошибок, но он имеет недостатки, такие как необязательные свойства и неясные названия.

💡 Автор предлагает свой протокол Throwable, который требует единственного обязательного свойства userFriendlyMessage, что упрощает создание понятных сообщений об ошибках.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍42
Руководство по использованию unsafe в Swift

ℹ️ В Swift существуют различные способы работы с небезопасными операциями, включая прямое использование указателей и встроенные обёртки, такие как UnsafePointer и UnsafeMutablePointer.

⚠️ Использование небезопасных механизмов оправдано при взаимодействии с C API, для оптимизации производительности, низкоуровневого программирования и работы с Objective-C.

⚙️ Важно минимизировать область использования unsafe, применять конструкции withUnsafe для безопасного доступа к указателям и тщательно документировать код для предотвращения утечек памяти.

🔴 Преимущества использования unsafe включают производительность и гибкость, однако риски связаны с утечками памяти и сложностью поддержки кода.

import Foundation

let count = 4
// Выделяем память для 4 байтов
let rawPointer = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: MemoryLayout<UInt8>.alignment)

// Инициализируем память
for i in 0..<count {
rawPointer.storeBytes(of: UInt8(i), toByteOffset: i, as: UInt8.self)
}

// Чтение данных через UnsafeRawPointer
let immutableRawPointer = UnsafeRawPointer(rawPointer)
for i in 0..<count {
let byte = immutableRawPointer.load(fromByteOffset: i, as: UInt8.self)
print("Байт \(i): \(byte)")
}

// Освобождаем память
rawPointer.deallocate()
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Bridging interfaces with the Adapter pattern in Swift

➡️ Паттерн адаптер позволяет интегрировать сторонние API или устаревший код, сохраняя при этом чистоту архитектуры приложения.

✏️ В примере показано, как адаптировать интерфейс стороннего SDK с использованием протокола SearchService, чтобы обеспечить совместимость с существующим кодом.

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

protocol SearchService {
func search(query: String) async throws -> [SearchResult]
}

class ThirdPartySearch {
func runSearch(term: String, completion: @escaping ([Any]) -> Void) {
// ...
}
}


class ThirdPartySearchAdapter: SearchService {
private let thirdPartySearch = ThirdPartySearch()

func search(query: String) async throws -> [SearchResult] {
try await withCheckedThrowingContinuation { continuation in
thirdPartySearch.runSearch(term: query) { rawResults in
let results = rawResults.compactMap { $0 as? SearchResult }
continuation.resume(returning: results)
}
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Custom subscripts in Swift explained with code examples

➡️ Subscript в Swift позволяют создавать короткие пути к элементам коллекций, что упрощает доступ к данным в классах, структурах и перечислениях.

⚙️ Custom subscripts
можно определить, как методы, и они могут принимать несколько параметров, что позволяет улучшить читаемость кода.

ℹ️ Подписки могут быть как только для чтения, так и для записи, что позволяет не только получать, но и устанавливать значения в коллекциях.

➡️ Статические подписки позволяют обращаться к элементам без необходимости раскрывать детали экземпляра, что улучшает инкапсуляцию.

➡️ Subscripts могут принимать необязательные параметры, что добавляет функциональность, например, для получения изображений по базовому URL.

extension ImageCache {
subscript(url: URL) -> UIImage? {
get {
imageStore[url]
}
set {
imageStore[url] = newValue
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4👎1
Regular Expressions in Swift

⚙️ В Swift регулярные выражения реализованы через класс NSRegularExpression из Foundation, который позволяет создавать шаблоны и искать совпадения в строках. Для удобства можно расширить этот класс, чтобы упростить создание и проверку выражений, например, добавить метод matches(), который проверяет наличие совпадений в строке.

⚠️ Однако стоит помнить, что чрезмерное использование регулярных выражений в больших текстах может негативно сказаться на производительности приложения. С выходом Swift 5.7 появился более современный и удобный инструмент - Swift Regex с лаконичным синтаксисом и билдерами регулярных выражений, который значительно упрощает работу и повышает читаемость кода по сравнению с NSRegularExpression

💡 В статье подробно рассмотрены основы работы с NSRegularExpression, примеры использования, а также преимущества новых подходов Swift Regex с множеством практических примеров. Рекомендуем к прочтению всем, кто хочет глубже понять регулярные выражения в Swift и писать более эффективный и чистый код.

// matching a url
let link = Reference(URL.self)

let linkRB = Regex {Capture (as:link){.url()}}
Please open Telegram to view this post
VIEW IN TELEGRAM
5
From 180 cm to 5′ 11″: A Complete Guide to Swift Measurement

💡 Если вам вдруг нужно поддерживать различные единицы измерения в своем приложении, то вот хороший гайд по работе с Measurement.

✏️ API Measurement в Swift позволяет безопасно и удобно работать с единицами измерения, обеспечивая автоматическую конвертацию и форматирование значений.

➡️ Можно выполнять математические операции с объектами Measurement, если они принадлежат одной категории единиц, что упрощает работу с данными.

➡️ Метод formatted() позволяет легко преобразовывать Measurement в удобочитаемый текст с учетом локализации, что делает его полезным для международных приложений.

➡️ Swift позволяет добавлять новые единицы измерения в существующие категории или создавать совершенно новые категории, что расширяет возможности API.

let heightMeasurement = Measurement<UnitLength>(value: 180, unit: .centimeters)

// Division with a scalar
let half = heightMeasurement / 2
print(half.value) // 90.0
print(half) // "90.0 cm"

// Addition: different units can be added; the result is in the category’s base unit
let h180cm = Measurement<UnitLength>(value: 180, unit: .centimeters)
let h1m = Measurement<UnitLength>(value: 1, unit: .meters)
let totalHeight = h180cm + h1m // 2.8 m (base unit of UnitLength is metres)
print(totalHeight) // "2.8 m"

// Comparison with automatic unit handling
h180cm < h1m // false (180 cm ≮ 1 m)

// Ranges respect units too
let range = h1m ... h180cm
range.contains(Measurement(value: 6, unit: .feet)) // false (~1.83 m is outside)
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10
Изоляция с помощью глобальных акторов в Swift Concurrency: варианты на примере @MainActor

🔍 В статье рассматриваются различные подходы к изоляции с использованием @MainActor включают полную изоляцию на уровне типа, изоляцию отдельных методов и свойств, а также частичную изоляцию через расширения.

⚠️ Ошибки, такие как разделение соответствия протоколу и реализации (например, через расширение), могут привести к потере ожидаемой изоляции, несмотря на наличие аннотаций @MainActor.

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

⚙️ Выбор подхода к изоляции зависит от контекста: для взаимодействия с UI лучше использовать полную изоляцию, а для повышения производительности — частичную.

@MainActor
protocol IUpdate {
func update(date: Date) async
}

@Observable
final class LS {
var date: Date = .now
}

// Здесь изоляцию @MainActor получит только update

extension LS: IUpdate {
func update(date: Date) async {
await internalUpdate(date: date)
}
// А здесь изоляция уже не применится

private func internalUpdate(date: Date) async {
self.date = date
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Обсуждем кейсы на митапе Яндекса по мобильной разработке

Я.Субботник — большой митап для мобильных разработчиков. В этот раз кроме докладов участников ждёт практический разрбор кейсов на PeerLab.

PeerLab — камерная встреча с экспертами Яндекса. Для неё мы отобрали актуальные темы из разработки и карьеры. Предложить кейс для обсуждения может каждый участник — приносите их в форму регистрации и приходите на обсуждение!

В Москве точно обсудим:

🔸Kotlin Multiplatform
🔸Карьерное развитие
🔸Платформенные команды
🔸AI в разработке

А в Питере:

T-Shape разработчик
🔸Тестирование
🔸AI в разработке

➡️ Регистрируйтесь и ищите список экспертов-участников дискуссии на сайте
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2👎1
Protecting mutable state with Mutex in Swift

Mutex в Swift используется для защиты состояния от одновременного доступа, обеспечивая эксклюзивный доступ к защищаемым данным.

➡️ Акторы в Swift удобны для асинхронного кода, но могут усложнить код, тогда как Mutex подходит для быстрого синхронного доступа к данным.

ℹ️ Пример класса Counter показывает, как использовать Mutex для безопасного увеличения и уменьшения счетчика, обеспечивая защиту от гонок данных.

⚙️ Выбор между Mutex и акторами зависит от удобства, согласованности и намерений разработчика; Mutex лучше подходит для простых операций без асинхронного кода.

➡️ Для использования Mutex с SwiftUI необходимо вручную уведомлять Observable о доступе и изменении состояния, чтобы обновления интерфейса работали корректно.

class Counter {
private let mutex = Mutex(0)

func increment() {
mutex.withLock { count in
count += 1
}
}

func decrement() {
mutex.withLock { count in
count -= 1
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
When the Swift Debugger Lies: The Hidden Cost of Compiler Optimizations

👀 Интересный кейс произошел у автора при отладке приложения: перед выходом из функции массив оказался пустой.

func getUserListForAdmin() async -> NetworkResponse<[OtherUser]> {
async let type4Request = RestClient.shared.request(Constants.Network.ENDPOINT_USERS, parameters: ["userType": 4])
async let type2Request = RestClient.shared.request(Constants.Network.ENDPOINT_USERS, parameters: ["userType": 2])

let (result4, result2) = await (type4Request, type2Request)

switch (result4, result2) {
case (.success(let users4), .success(let users2)):
for user in users4 {
user.canSeeThisUsersRequests = users2.contains(where: { $0.id == user.id })
}
return .success(users4) // At this breakpoint, users2 appears EMPTY 🤯
case (.error(let error), _), (_, .error(let error)):
return .error(error)
}
}


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

💡 Чтобы сохранить переменные, такие как users2, используйте withExtendedLifetime(users2) или извлеките их из switch-выражения заранее, чтобы избежать их удаления.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Exploring concurrency changes in Swift 6.2

ℹ️ Swift 6.2 вводит два основных изменения в параллельности: новый флаг по умолчанию nonisolated(nonsending) и выполнение кода на главном акторе по умолчанию с помощью настройки defaultIsolation.

⚠️ Флаг nonisolated(nonsending) позволяет асинхронным функциям наследовать контекст актора вызывающего кода, что уменьшает количество ошибок компиляции и упрощает работу с параллельностью.

⚠️ С помощью defaultIsolation можно установить выполнение кода на главном акторе по умолчанию для всех объектов в пакете, что упрощает разработку UI и моделей.

❗️ Рекомендуется включить оба новых флага для упрощения работы с параллельностью, однако важно избегать избыточного использования @concurrent, чтобы не усложнять код.

actor SomeGenerator {
// not allowed
@concurrent
func randomID() async throws -> UUID {
return UUID()
}

// allowed
@concurrent
nonisolated func randomID() async throws -> UUID {
return UUID()
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2
Swift Enums vs Structs - Picking the Best Tool for the Job

Очередная статья про выбор между Enum и структурами (классы остались за бортом 🫠)

➡️ Enums обеспечивают исчерпывающее переключение, что делает код более надежным, особенно когда состояния являются взаимно исключающими.

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

⚙️ Можно комбинировать Enums и Structs для достижения наилучших результатов, используя каждый инструмент в зависимости от конкретной ситуации.

💡 Выбирайте Enums для управления состоянием с фиксированным набором опций и Structs для хранения сложных состояний с несколькими свойствами.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Swift 6 Refactoring in a Camera App - SLIT_STUDIO Development Log

➡️ Swift 6 требует от разработчиков учитывать новые требования к потокобезопасности, что создает сложности при миграции, особенно с фреймворками Apple.

⚙️ Разработчик Megabits успешно адаптировал приложение SLIT_STUDIO, используя новые компоненты, такие как actor и GlobalActor, для улучшения структуры кода и безопасности.

⚠️ AVFoundation использует GCD, что не совместимо с Swift Concurrency, требуя от разработчиков находить нестандартные решения для обеспечения потокобезопасности.

➡️ Код приложения был перегружен, и Megabits разделил его на более управляемые компоненты, такие как CaptureManageObject и Recorder, чтобы улучшить читаемость и поддержку.

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

@globalActor actor CameraActor: GlobalActor {
static let shared = CameraActor()
}

@CameraActor class Camera: NSObject {
let captureQueue = DispatchQueue(label: "com.linearCCD.capture")
var captureSession = AVCaptureSession()

var currentDevice: AVCaptureDevice?

var videoInput: AVCaptureDeviceInput?
var audioInput: AVCaptureDeviceInput?
var videoOutput: AVCaptureVideoDataOutput?
var audioOutput: AVCaptureAudioDataOutput?
...
}

@CameraActor class CapturePipeline: NSObject, ObservableObject {
let renderContext: MTIContext
var renderPoolForFilter = RenderPool()
var renderPoolForRecordingImage = RenderPool()
var cameraFeedRenderTask: Task<(), Never>?
...
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Для чего я написал собственный аудиопроигрыватель

👀 Автор разработал собственный аудиопроигрыватель из-за неудовлетворенности существующими решениями, такими как Apple Music и сторонние приложения, которые не обеспечивали нужного функционала для управления локальной музыкальной библиотекой.

⚙️ Приложение было создано с использованием SwiftUI для упрощения разработки интерфейса и управления данными, а также SQLite для реализации полнотекстового поиска и хранения данных.

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

🫠 Существующие приложения часто требуют подписки, имеют ограниченные функции и неудобный интерфейс, что побудило автора создать собственное решение.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Unique values in Swift: Removing duplicates from an array

В Swift нет встроенных методов для удаления дубликатов из массива, поэтому необходимо использовать альтернативные подходы, такие как использование Set или создание расширения для массива.

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

➡️ Если порядок важен, можно создать расширение для массива, которое использует протокол Hashable для фильтрации дубликатов, обеспечивая линейную временную сложность.

👀 Выбор между Set и расширением массива зависит от необходимости сохранения порядка элементов: используйте Set, если порядок не важен, и расширение массива, если он важен.

extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var seen: Set<Iterator.Element> = []
return filter { seen.insert($0).inserted }
}
}

print(array.unique()) // prints: [1, 2, 3]
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6