iOS Roadmap - Полезное | Swift, SwiftUI, Интервью
444 subscribers
107 photos
3 videos
8 files
195 links
iOS Roadmap: Swift, SwiftUI, архитектуры, интервью. Еженедельные подборки статей и видео, плейбуки, чек-листы, решения задач с пояснениями. Полезно начинающим и практикующим iOS-разработчикам.
Download Telegram
#заметка #cow #устройство_работы

А ты знал, что Swift хранит счетчик сильных ссылок как "extra count" - дополнительное значение? 🤔

Когда у объекта одна strong ссылка, в памяти хранится 0.
Когда две ссылки — хранится 1.

- Физическое значение = то, что реально хранится в битах.
- Логическое значение = реальное количество ссылок = физическое + 1


// Практический пример с Copy-on-Write:
var array1 = [1, 2, 3]
// StrongExtraRefCount = 0 (в памяти)
// Реальных ссылок = 1 Можно изменять!

var array2 = array1
// StrongExtraRefCount = 1 (в памяти)
// Реальных ссылок = 2 Нужно копировать!

array2.append(4)
// Swift проверяет: getStrongExtraRefCount() == 0?
// Нет! Значит создаём копию массива


Именно поэтому isKnownUniquelyReferenced проверяет getStrongExtraRefCount() == 0 - это означает, что есть только одна ссылка на объект, и его можно безопасно изменять без копирования.


bool isUniquelyReferenced() {
static_assert(Offsets::UnownedRefCountBitCount +
Offsets::IsDeinitingBitCount +
Offsets::StrongExtraRefCountBitCount +
Offsets::PureSwiftDeallocBitCount +
Offsets::UseSlowRCBitCount == sizeof(bits)*8,
"inspect isUniquelyReferenced after adding fields");

// Unowned: don't care (FIXME: should care and redo initForNotFreeing)
// IsDeiniting: false
// StrongExtra: 0
// UseSlowRC: false

// Compiler is clever enough to optimize this.
return
!getUseSlowRC() && !getIsDeiniting() && getStrongExtraRefCount() == 0;
}


Это как номерки в гардеробе, которые начинаются с 0, а не с 1. Apple сэкономила на единичке, потому что одна ссылка — самый частый случай. А когда у нас миллиарды объектов, даже один бит на счету!

Представь диалог:
- Сколько у тебя ссылок?
- Ноль!
- То есть объект мёртв?
- Нет, это значит одна! Просто мы экономные

Մտածեք. Բաժանորդագրվել.

cc: https://github.com/swiftlang/swift/blob/eb29949fe7d2c90644410ef9cef76abe30658aad/stdlib/public/SwiftShims/swift/shims/RefCount.h#L609C3-L626C4
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥52👍1🌚1
#wwdc25

Improve memory usage and performance with Swift

Сессия с WWDC25, где расскажут про InlineArray и его разницу между Array из Swift, причным нам. Спойлер - копируется сразу, CoW не поддерживает. Ну и как работа с InlineArray сделает работу ещё-более оптимизированной на ресурсы (график увидишь на сессии).

Так же понравилось, в самом начале, что при помощи Instruments: Time Profiler и Allocations, можно продебажить на bottle neck - "узкое горлыжко" в приложении. Посмотреть колличество аллокаций, деаллокаций, фильтрануть светлого нужное и прыгнуть в проблемное место.

Ну и под конец будет про новую обёртку - Span. Всё это уже можно посмотреть / потрогать в Xcode 26. Жаль в wwdc notes ещё нет краткого пересказа, но возможно, он скоро будет? - Если-что вот ссылка на него 🙂

https://developer.apple.com/videos/play/wwdc2025/312
👍51🔥1
#middle #задачка

Что из перечисленного ниже нельзя переопределить в подклассе? Опрос будет ниже 🤖


class Vehicle {
static let wheels = 4

class var description: String {
return "Vehicle with \(wheels) wheels"
}
}

class Motorcycle: Vehicle {
override static let wheels = 2

override class var description: String {
return "Motorcycle with 2 wheels"
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
#заметка #soft_skills

История одной задачи + софт скиллы в деле с QA и Backend

Доброго дня. Решил начать писать небольшие заметки про софт скиллы. Кто знает, мб в отдельное видео, со структурой и гайдом выйдет. Но пока этого нет - буду чуть по чуть сюда писать интересные вещи.

Давечне мне надо было починить баг - отсутствие переводов для некоторых языков. Этот баг выглядит просто:"Наверное затерялась локализация для необходимы языков, сейчас пойду поправлю. Делов на 5 минут."

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

Иду смотреть в задачу, и нигде не вижу об этом ни слова. И тут я хотел-бы добавить, что не во всех компаниях или проектах тестировщики должны собирать логи. Или собирать логи прям везде и до 3-его колена. Нет, всё как всегда просто - как договорились, так и будете работать.

Так вот, оказывается тестер не написал этот факт, что текст приходит с бекенда, и что iOS тут особо ничего и не сделает, а задча должна БЫЛА БЫ содержать лейбл [backend], а не [iOS], но что поделать...

Иду до тестера и кумекаю, что так и так, что задача так-то не про iOS, а про backend, но тестеру не нужно волноваться, я сейчас схожу и уточню.

Иду в нужное место, тегаю PM, Backend и пишу, что текст, который багает, он приходит бекенда, и что лучше править на бекенде в одном месте, чем на iOS и Android отдельно - в двух местах. И аргументирую это тем, что если править на 2-ух платформах сразу, то это придётся:

- Делать небольшую логику в 2-ух местах
- Заводить локализацию
- Маппить нужное место с нужной локализацией


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

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

Какой вывод можно сделать из этой истории?

Не стоит идти к тестировщику и как-то язвить или сетовать на то, что он оказался где-то неправ. Потому-что напомню:
- Как договорились в команде так и будет.
- Опыт коллеги, кума, зятя, соседа не всегда релевантный к тебе, т.к ты не всегда знаешь всех под-капотных вещей.

Если - бы я отправил тестера вместо себя изучать, ибо проблема backend'a, то мог - бы получиться сломанный телефон из 3-ёх человек + к этому долго бы обсуждали. А лично я не люблю тягомотину эту, когда понимаешь, что с буксом + хрустом можно за 5 минут обговорить и сделать, чем 30 минут выяснять кто прав, а кто виноват и придти к такому-же результату.

Возможность для выноса на ретроспективу, чтобы тестировщики проверяли чуть глубже, если они этого не делают. А если должен делать, но конкретно тут [по какой-то причине забыл сделать], то в мягкой форме донести:"Что мне бы помогло, если-бы в результате заведения бага были логи".

Все мы люди, разные бывают обстоятельства и зачем токсичить или язвить, если можно этого не делать? - Если - вдруг ты считаешь, что токсичишь, то всегда можно:
- После написания текста не отправлять его, взять пауза на 5 минут. Спустя паузу перечитать.
- Закинуть ИИ на оценку токсичности, и в случае её обнаружения попросить поправить. А что нет то, мы ж не пишем рассказ, а доносим мысль.

Ну и последнее, чтобы backend'у было легче, я сразу же заострил внимание, что можно сделать легче и без проблем - прокинуть дополнительный параметр и по нему получать перевод. Потому-что для backend'a пойти в другой микросервис, узнать там локаль, с этими знаниями придти обратно - это лишние сложности.

А мы бы их создали, если бы могли упереться, не согласиться, дескать вот пусть backend разруливает сам ибо мы просто получаем и показываем... Разные ветви пути могли-бы быть...
🔥4👍21
Какой вывод можно сделать?

1. Понимание, хоть и небольшое, backend части может улучшить всем жизнь.
2. Задачки на фиксы багов с репортами - это гуд, так как не нужно будет тратить время на поиск причины бага. Если тестер уже на этом пути, иногда лишиние 5-10 минут сэкономят твои 30-60 минут.
3. Если хочется ткнуть носом в промах человека, то лучше вспомнить, что не все и всегда и во все периоды времени работают на полную мощнь.
4. Видишь место для улучшения работы? Запиши, ибо забудешь, а на ретро можно обсудить
5. Если человек человеку волк, то почему зомби зомби зомби?


Если понравилось, жду голубя, буду софтовыми вещами по-чаще делиться, если нужна reels version, то голубя ставь.
🕊16🔥2👍1
Полный экскурс по разработке + день iOS Разработчика

Почти 1 год прошел с публикации этого видео, где я полностью и по полочкам раскидал важное и необходиоме для тех, кто хочет понимать:"А что же там происходит, внутри этой разработки?". Вот небольшой список тем:
- Кто состоит в команде и чем занимается
- Какие задачи и откуда они берутся
- Feature / Core team, а что это вообще?
- Мобильный девопс и что он делает? Кстати подробный воркшоп и про него есть, он здесь
- Про каждый процесс подробно + дополнительные активности, опционально.

И это лишь малая часть того, что есть внутри видео. Из этого видео можно понять, и очень даже неплохо, про внутренню кухню внутри разработки ПО.

Как - всегда, все подробные таймкоды ждут внутри. Презентация и пояснение = результат 🙂
🔥126👍6🫡1
#middle #задачка

Старая добрая задача на print + работа с замыканием. Самые разные вариации с ДАНО: value / ref type и тоже самое с замыканием - implicit capture vs explicit capture.

Посмотри на код ниже, хорошо подумай, и выбери нужный вариант ответа, будет следующим постом.


final class Person {
var name: String

init(name: String) {
self.name = name
}
}

var person = Person(name: "Alice")

let closure1 = {
print(person.name)
}

let closure2 = { [person] in
print(person.name)
}

person.name = "Bob"
closure1() // Что выведет тут? [1]
closure2() // Что выведет тут? [2]

person = Person(name: "Charlie")
closure1() // Что выведет тут? [3]
closure2() // Что выведет тут? [4]
👍1
В сети интернет ходит интересная стать про перформанс SwiftUI и способы на него взаимодействовать, благоприятно. Хочу сразу подсветить, что эта информация актуальна для 13-16 версии, начиная с 17 доступен макрос Observable, который оптимизирован в плане перформанса.

Не поделиться не могу, поэтому вот: https://medium.com/airbnb-engineering/understanding-and-improving-swiftui-performance-37b77ac61896

Но хочу добавить, что это давным-давно есть сами знаете где и намного подробнее 🙂

Но статью прочитать тоже стоит, зря старались коллеги из Airbnb? 👍
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍3😁1
#swiftui #собеседование #jun

Доброй ночи! Такая задача на подумать. Сколько раз выведется принт:"evaluate body", если сделать два действия:
1. Запустить код, чтобы отобразить VIew
2. Нажать на кнопку


import SwiftUI
import PlaygroundSupport

struct ContentView: View {
@State var value = 0

var body: some View {
let _ = print("evaluate body")

Button("Изменить") {
value += 1
}
}
}

PlaygroundPage.current.setLiveView(ContentView())


Варианты ответа будут ниже!
🤓5🔥3🤝3
Ой ой ой ой.....
😁11😭8😢1
#swiftui #собеседование #middle

Мы поменяли @State var value = 0 на @StateObject var viewModel и дёргаем increment. Так же сделаю 2 действия: запущу это код, чтобы отобразить view и нажму на кнопку. За два этих действия сколько раз вызовется принт “re-evaluate body”? И почему?


import SwiftUI
import PlaygroundSupport

final class ViewModel: ObservableObject {
@Published var age: Int = 0

func increment() {
age += 1
}
}

struct ContentView: View {
@StateObject var viewModel = ViewModel()

var body: some View {
let _ = print("evaluate body")

Button("Изменить") {
viewModel.increment()
}
}
}

PlaygroundPage.current.setLiveView(ContentView())


Опрос будет ниже, перед выбором нужного ответа постарайся обосновать свой ответ, почему именно ЭТА цифра.
👍1🔥1
#swiftui #собеседование #middle

Окей с двумя премерами выше мы разобрались. У нас есть правильный ответ и обоснование его. Если-вдруг есть сомнения в корректности, всегда можно проверить в Playground.

И чтобы продолжить ветку этого вопроса дальше, давай теперь ответим на следующий вопрос. Ответы выбирай тот, который считаешь НАИБОЛЕЕ корректным.

"В чём разница между re-evaluate и re-draw? Что происходит в предыдущем примере: re-evaluate и / или re-draw?"
#swiftui #собеседование #middle+

Есть такой код и при нажатии на кнопку “Увеличить возраст на 1 ед.” счётчик counter не увеличивается и не показывает числа, он всегда показывает 0.

Задача - понять почему, пофиксить и объяснить в чём была проблема. Код при этом, концептуально, должен остаться таким же, какой он изначально, не считая исправления.


import SwiftUI
import PlaygroundSupport

final class ViewModel: ObservableObject {
@Published var age: Int = 0

func increment() {
age += 1
}
}

struct ChildView: View {
@ObservedObject var viewModel = ViewModel()
@Binding var value: Int

var body: some View {
Button("Увеличить возраст на 1 ед.") {
viewModel.increment()
}
.onChange(of: viewModel.age) { _, newValue in
value = newValue
}
}
}

struct ContentView: View {
@State private var counter: Int = 0

var body: some View {
VStack(alignment: .center, spacing: 0) {
Text("Каунтер равен:\(counter)")
.font(.title)
.foregroundStyle(.teal)

ChildView(value: $counter)
}
.frame(width: 300, height: 500)
}
}

PlaygroundPage.current.setLiveView(ContentView())
#structured_concurrency #собеседование #middle

Нужно сказать на каком thread будет выполняться операция в контексте background или main thread, учитывая что Playground по-умолчанию равен background thread.


Task {
showCurrentThread() // 1 ?
await asyncOperation // 2 ?
showCurrentThread // 3 ?
}

Task { @MainActor in
showCurrentThread() // 4 ?
await asyncOperation // 5 ?
showCurrentThread // 6 ?
}


func asyncOperation(line: Int = #line) async {
showCurrentThread(line: line)
try? await Task.sleep(for: .seconds(1))
}

func showCurrentThread(line: Int = #line) {
print("Current line is: \(line) and thread is: \(Thread.current)")
}