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

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

По всем вопросам обращаться к @itereznikov
Download Telegram
NSAttributedString: Formatting Rich Text in Swift

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

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

➡️ Можно изменять цвет текста, шрифт, добавлять зачеркивание и подчеркивание, а также создавать кликабельные ссылки и настраивать стили абзацев для выравнивания и интервалов.

❗️ NSAttributedString можно использовать с UIKit через свойства attributedText в UILabel и UITextView, а также с SwiftUI, начиная с iOS 15, с помощью нового типа AttributedString.

💡 Рекомендуется кэшировать атрибутированные строки и использовать NSMutableAttributedString только при необходимости модификации текста для повышения производительности.

let attributedString = NSMutableAttributedString(string: "A text with a background color.")

// Applying foreground color to "text"
attributedString.addAttributes(
[.foregroundColor : UIColor.red],
range: NSRange(location: 2, length: 4)
)

// Applying background color to "text with a background"
attributedString.addAttributes(
[.backgroundColor: UIColor.yellow],
range: NSRange(location: 2, length: 22)
)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🤔1
Rendering quadratic Bézier curves with Metal

💡 Интересная статья про то, как начали использовать упрощенный метода рендеринга квадратичных кривых Безье с помощью Metal. Данный метода основан на алгоритме Kokojima. Он включает в себя создание трафаретного буфера и шейдинга.

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

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

Для сглаживания краев используют встроенную многообразную выборку Metal, что упрощает процесс по сравнению с более сложными решениями.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2
iOS Performance tips you probably didn't know (from an ex-Apple engineer)

Крутая статья трюки оптимизации в приложении. Вот краткий обзор.

⚠️ UILabel дорого стоит:

Он может потреблять значительное количество памяти, особенно если он не монохромный. Для уменьшения использования памяти рекомендуется обнулять текст меток в ячейках UITableView/UICollectionView, когда они скрыты.

➡️Использование очередей:

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

ℹ️ Симуляция предупреждений о памяти:

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

🛡 Избегайте семафоров;

Использование dispatch_semaphore_t для ожидания асинхронной работы может привести к инверсии приоритетов. Вместо этого лучше использовать синхронные прокси с XPC.

✏️ Проблемы с тегами UIView:

Использование тегов в UIView может негативно сказаться на производительности из-за дополнительных затрат на поиск. Лучше избегать их в производительном коде.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6😱32👍2
How to inspect .ipa files and secure your iOS app from common mistakes

ℹ️ Интересная статья для тех, кто не знал, что можно достать путем анализа .ipa файла приложения.

🤫 Автор выделяет три основных правила для обеспечения базовой безопасности информации.

➡️ Правило 1: Защита данных

Не добавляйте чувствительные данные в Info.plist, так как этот файл не зашифрован и доступен для чтения.

➡️ Правило 2: Избегайте тестовых данных

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

➡️ Правило 3: Защита строковых литералов

Не храните чувствительные ключи и секреты в строковых литералах; используйте методы обфускации или шифрования - через утилиту strings можно много чего увидеть.

🔍 Для более глубокого анализа используйте специальные инструменты, такие как IDA pro или Hopper, которые позволяют дизасемблировать приложение и изучить внутренние структуры и не только.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Swift Reduce: Combining elements into a single value

💡 Метод reduce в Swift позволяет объединять элементы коллекции в одно значение, что делает его полезным для преобразования массивов в словари или суммирования чисел.

⚠️ Метод reduce имеет сложность O(n) и может использоваться с вариантом reduce(into:), который более эффективен, избегая копирования аккумулятора на каждой итерации.

➡️ Хотя reduce делает код более элегантным, для сложной логики может быть лучше использовать обычные циклы, чтобы повысить читаемость.

let animals = ["Dog", "Cat", "Dog", "Bird"]
let counts = animals.reduce(into: [:]) { result, animal in
result[animal, default: 0] += 1
}
// counts == ["Dog": 2, "Cat": 1, "Bird": 1]
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Modern URL construction in Swift

➡️ Swift 5.9 представил макросы, которые позволяют компилировать и валидировать статические URL-строки, улучшая безопасность кода.

➡️ С введением новых API в iOS 16, можно легко создавать динамические URL без опциональных значений, используя методы appending.

✏️ Современные API для создания URL упрощают код, уменьшают вероятность ошибок и позволяют работать с URL более структурированно.

@freestanding(expression)
public macro staticURL(_ value: StaticString) -> URL = #externalMacro(
module: "StaticURLMacros",
type: "StaticURLMacro"
)

public struct StaticURLMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
// Verify that a string literal was passed, and extract
// the first segment. We can be sure that only one
// segment exists, since we're only accepting static
// strings (which cannot have any dynamic components):
guard let argument = node.arguments.first?.expression,
let literal = argument.as(StringLiteralExprSyntax.self),
case .stringSegment(let segment) = literal.segments.first
else {
throw StaticURLMacroError.notAStringLiteral
}

// Verify that the passed string is indeed a valid URL:
guard URL(string: segment.content.text) != nil else {
throw StaticURLMacroError.invalidURL
}

// Generate the code required to construct a URL value
// for the passed string at runtime:
return "Foundation.URL(string: \(argument))!"
}
}

let url = #staticURL("https://swiftbysundell.com")
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
libdispatch efficiency tips

👀 Нашел тут довольно старую, но актуальную подборку советов по работе с GCD. Вот некоторые из них:

➡️ Создавайте несколько долгоживущих и четко определенных очередей, чтобы использовать их как контексты выполнения в вашем приложении. Обычно достаточно 3-4 очередей.

➡️ Начинайте с серийной обработки и только при обнаружении узких мест переходите к конкурентности, тщательно измеряя производительность.

➡️ Глобальные очереди могут привести к взрыву потоков и неэффективности. Используйте свои собственные очереди для лучшей производительности.

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

➡️ Проводите реальные тесты производительности вашего продукта, чтобы убедиться, что изменения действительно улучшают скорость, а не ухудшают.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
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