How to inspect .ipa files and secure your iOS app from common mistakes
ℹ️ Интересная статья для тех, кто не знал, что можно достать путем анализа .ipa файла приложения.
🤫 Автор выделяет три основных правила для обеспечения базовой безопасности информации.
➡️ Правило 1: Защита данных
Не добавляйте чувствительные данные в
➡️ Правило 2: Избегайте тестовых данных
Не включайте данные для разработки или тестирования в целевые приложения, чтобы избежать утечек информации.
➡️ Правило 3: Защита строковых литералов
Не храните чувствительные ключи и секреты в строковых литералах; используйте методы обфускации или шифрования - через утилиту
🔍 Для более глубокого анализа используйте специальные инструменты, такие как
Не добавляйте чувствительные данные в
Info.plist
, так как этот файл не зашифрован и доступен для чтения.Не включайте данные для разработки или тестирования в целевые приложения, чтобы избежать утечек информации.
Не храните чувствительные ключи и секреты в строковых литералах; используйте методы обфускации или шифрования - через утилиту
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 без опциональных значений, используя методы
✏️ Современные API для создания URL упрощают код, уменьшают вероятность ошибок и позволяют работать с URL более структурированно.
appending.
@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 и плагин для фигмы
Выглядит, конечно, как магия - тут тебе и дизайнер и разработчик в одном лице. Главная задача - хорошо и понятно написать промпт.
Похоже на то, что в ближайшее время мы уже будем меньше писать кода сами, а больше времени уделять ревью и отладке - местами, код, который получился после генерации, похож на код джуна
Да и видно, что дизайну он соответствует не полностью (хотя он сам его придумал
Но это намного лучше и быстрее, чем писать все изначально с 0
P.S. Я использовал Cursor и плагин для фигмы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥3
Пробовали Vibe-coding
Anonymous Poll
29%
Да, Крутая штука
14%
Да, но больше не буду
31%
Нет
26%
Что это вообще такое?
Building Type‑Safe, High‑Performance SwiftData / Core Data Models
🔍 В статье приводятся советы, как сделать модели для
Вот некоторые моменты:
➡️ В отличие от
➡️ Использование библиотеки NonEmpty позволяет гарантировать, что строка не пустая, что улучшает семантику и предотвращает ошибки на этапе компиляции.
➡️ Для многовариантных выборок рекомендуется использовать битовые операции для хранения данных, что значительно повышает производительность запросов в
➡️
CoreData
/SwiftData
типо-безопасными и эффективными.Вот некоторые моменты:
SwiftData
, CoreData
требует использования NSNumber
для опциональных числовых свойств, что нарушает стиль Swift
. Решение включает использование вычисляемых свойств для обеспечения безопасности типов.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
👍3❤2
How a Single Line Of Code Could Brick Your iPhone
🔍 Крутая статья про то, к чему приводит любознательность и понимание механизмов работы различных компонентов.
🔔 Уязвимость в
Вот эта строка:
🆒
🗓 Первоначальный отчет о проблеме был отправлен в Apple 26 июня 2024 года, а уязвимость была исправлена в iOS 18.3 с присвоением CVE-2025-24091.
💵 За это автор получил вознаграждение в размере 17.5 к $.
✔️ Теперь для отправки чувствительных уведомлений Darwin требуется наличие ограниченных прав, что предотвращает несанкционированные действия со стороны приложений.
Darwin notifications
позволяет любому процессу на iOS отправлять уведомления без специальных привилегий.Вот эта строка:
notify_post("com.apple.MobileSync.BackupAgent.RestoreStarted")
+ виджет и fatalError()
окирпичивали телефон.
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
👍4❤2
Руководство по использованию unsafe в Swift
ℹ️ В
⚠️ Использование небезопасных механизмов оправдано при взаимодействии с C API, для оптимизации производительности, низкоуровневого программирования и работы с Objective-C.
⚙️ Важно минимизировать область использования
🔴 Преимущества использования
Swift
существуют различные способы работы с небезопасными операциями, включая прямое использование указателей и встроенные обёртки, такие как UnsafePointer
и UnsafeMutablePointer.
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 регулярные выражения реализованы через класс
⚠️ Однако стоит помнить, что чрезмерное использование регулярных выражений в больших текстах может негативно сказаться на производительности приложения. С выходом Swift 5.7 появился более современный и удобный инструмент -
💡 В статье подробно рассмотрены основы работы с
NSRegularExpression
из Foundation
, который позволяет создавать шаблоны и искать совпадения в строках. Для удобства можно расширить этот класс, чтобы упростить создание и проверку выражений, например, добавить метод matches()
, который проверяет наличие совпадений в строке. 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
🔍 В статье рассматриваются различные подходы к изоляции с использованием
⚠️ Ошибки, такие как разделение соответствия протоколу и реализации (например, через расширение), могут привести к потере ожидаемой изоляции, несмотря на наличие аннотаций
🔴 Аннотация
⚙️ Выбор подхода к изоляции зависит от контекста: для взаимодействия с UI лучше использовать полную изоляцию, а для повышения производительности — частичную.
@MainActor
включают полную изоляцию на уровне типа, изоляцию отдельных методов и свойств, а также частичную изоляцию через расширения.@MainActor
.@MainActor
повышает безопасность данных в многопоточной среде, но требует внимательного проектирования для предотвращения ошибок.@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 в разработке
➡️ Регистрируйтесь и ищите список экспертов-участников дискуссии на сайте
Я.Субботник — большой митап для мобильных разработчиков. В этот раз кроме докладов участников ждёт практический разрбор кейсов на PeerLab.
PeerLab — камерная встреча с экспертами Яндекса. Для неё мы отобрали актуальные темы из разработки и карьеры. Предложить кейс для обсуждения может каждый участник — приносите их в форму регистрации и приходите на обсуждение!
В Москве точно обсудим:
А в Питере:
T-Shape разработчик
➡️ Регистрируйтесь и ищите список экспертов-участников дискуссии на сайте
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
👀 Интересный кейс произошел у автора при отладке приложения: перед выходом из функции массив оказался пустой.
❓ Swift использует оптимизацию управления памятью, что приводит к преждевременному освобождению переменных, таких как
💡 Чтобы сохранить переменные, такие как
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)
}
}
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 для хранения сложных состояний с несколькими свойствами.
Очередная статья про выбор между Enum и структурами (классы остались за бортом
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3