Swift | Вопросы собесов
2.13K subscribers
28 photos
949 links
Download Telegram
🤔 Что такое ассоциированный тип (associated type)?

Ассоциированный тип (associated type) в Swift используется в протоколах для определения типа, который должен быть конкретизирован при реализации протокола. Это позволяет создавать протоколы, которые могут работать с разными типами данных, сохраняя при этом гибкость. Ассоциированные типы играют важную роль при создании обобщённых протоколов. Это позволяет писать код, который может адаптироваться под различные типы, не привязываясь к конкретной реализации.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
🤔 Зачем делить код?

🟠Улучшение читаемости и поддерживаемости кода
Разделение кода на модули позволяет разработчикам легче понимать и навигировать по коду. Когда код структурирован и разбит на логически связанные части, его проще читать и поддерживать.

🚩Пример

🟠Разделение функций в отдельные файлы и классы
View: Отвечает за отображение данных и взаимодействие с пользователем.
Model: Содержит бизнес-логику и данные.
Controller/Presenter/Interactor: Управляет взаимодействием между View и Model.

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

🟠Облегчение тестирования
Разделение кода на модули упрощает написание и проведение тестов. Модульный код легче тестировать изолированно, что позволяет находить и исправлять ошибки быстрее и эффективнее.

Модульный код
class DataService {
func fetchData() -> [String] {
// Логика получения данных
}
}

class ViewModel {
let dataService: DataService

init(dataService: DataService) {
self.dataService = dataService
}

func getData() -> [String] {
return dataService.fetchData()
}
}


Тест
func testFetchData() {
let dataService = MockDataService()
let viewModel = ViewModel(dataService: dataService)
let data = viewModel.getData()
XCTAssertEqual(data, ["MockData1", "MockData2"])
}


🟠Упрощение работы в команде
Когда код структурирован и разбит на модули, разные члены команды могут работать над разными частями проекта одновременно, не мешая друг другу. Это способствует параллельной разработке и уменьшает конфликты слияния (merge conflicts).

🟠Снижение сложности
Разделение кода на более мелкие, управляемые части помогает снизить общую сложность системы. Каждый модуль решает одну конкретную задачу, что делает систему более понятной и управляемой.

🟠Принципы SOLID
Разделение кода помогает следовать принципам SOLID:
S ingle Responsibility Principle (Принцип единственной ответственности): Каждый модуль отвечает за одну конкретную задачу.
O pen/Closed Principle (Принцип открытости/закрытости): Модули открыты для расширения, но закрыты для модификации.
L iskov Substitution Principle (Принцип подстановки Лисков): Модули можно заменить другими, не нарушая работу системы.
I nterface Segregation Principle (Принцип разделения интерфейсов): Избегайте создания "толстых" интерфейсов; предпочтительно иметь несколько специализированных интерфейсов.
D ependency Inversion Principle (Принцип инверсии зависимостей): Модули зависят от абстракций, а не от конкретных реализаций.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое "Опционалы"?

Опционалы в Swift — это тип данных, который позволяет переменной содержать либо значение, либо `nil`, указывающее на отсутствие значения. Опционалы объявляются с использованием знака вопроса (`?`), и для безопасного извлечения значения из опционала нужно его "развернуть" с помощью оператора `!` или безопасно использовать через условное приведение (`if let` или `guard let`). Опционалы помогают избежать ошибок, связанных с доступом к переменным, которые не содержат значений, и делают код безопаснее.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Какую проблему решает реактивное программирование?

🟠Управление асинхронностью
Реактивное программирование упрощает координацию асинхронных операций, избегая "callback hell".
До:
fetchData { data in
process(data) { processedData in
save(processedData) { success in
print("Data saved: \(success)")
}
}
}


После:
fetchData()
.flatMap { process($0) }
.flatMap { save($0) }
.subscribe(onNext: { success in
print("Data saved: \(success)")
})


🟠Обработка событий и управление состоянием
Обеспечивает простое и автоматическое обновление UI при изменении данных.
data.asObservable()
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
cell.textLabel?.text = model
}
.disposed(by: disposeBag)


🟠Композиция и повторное использование кода
Позволяет легко объединять данные из разных источников.
Observable.zip(loadUser(), loadPosts())
.subscribe(onNext: { user, posts in
display(posts)
})


🟠Управление ошибками
Централизованная обработка ошибок в асинхронных цепочках.
fetchData()
.flatMap { process($0) }
.flatMap { save($0) }
.subscribe(onNext: { success in
print("Data saved: \(success)")
}, onError: { error in
print("Error: \(error)")
})


🟠Реактивные пользовательские интерфейсы
Автоматическая привязка данных к элементам UI.
usernameObservable
.bind(to: usernameLabel.rx.text)
.disposed(by: disposeBag)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что такое многопоточность?

Многопоточность — это возможность выполнения нескольких задач одновременно в разных потоках, что позволяет программам эффективно использовать ресурсы процессора. В Swift многопоточность реализуется с помощью GCD (Grand Central Dispatch) и NSOperationQueue, которые позволяют управлять параллельным выполнением задач. Многопоточность используется для разделения вычислительно затратных операций на несколько потоков, чтобы не блокировать основной поток интерфейса пользователя. Это улучшает производительность и отзывчивость приложений.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Почему плохо загружать главный поток и какие есть способы этого не делать?

Главный поток отвечает за обновление пользовательского интерфейса (UI) и обработку взаимодействия с пользователем. Если этот поток блокируется, приложение может стать неотзывчивым, что вызывает задержки в интерфейсе или даже его полную заморозку.

🚩Проблемы при загрузке

🟠Замораживание интерфейса
Если главный поток занят выполнением длительных операций, UI не будет обновляться, что приведет к замораживанию или задержке интерфейса.
🟠Плохой пользовательский опыт
Задержки и лаги при взаимодействии с приложением вызывают неудовлетворенность пользователей и могут привести к негативным отзывам.
🟠Возможные аварийные завершения
Длительная блокировка главного потока может привести к тому, что система iOS решит завершить приложение, считая его неотзывчивым.

🚩Способы предотвращения загрузки

🟠Использование Grand Central Dispatch (GCD)
GCD предоставляет простой способ выполнения асинхронных задач в фоновом режиме.
DispatchQueue.global(qos: .background).async {
// Длительная задача
let result = performHeavyComputation()

DispatchQueue.main.async {
// Обновление UI с результатом
updateUI(with: result)
}
}


🟠OperationQueue
OperationQueue предлагает более высокоуровневый интерфейс для управления асинхронными задачами с возможностью задания приоритетов и зависимостей.
let backgroundQueue = OperationQueue()
backgroundQueue.addOperation {
// Длительная задача
let result = performHeavyComputation()

OperationQueue.main.addOperation {
// Обновление UI с результатом
updateUI(with: result)
}
}


🟠URLSession для сетевых запросов
URLSession автоматически выполняет сетевые запросы в фоновом режиме.
let url = URL(string: "https://example.com/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
// Обработка данных

DispatchQueue.main.async {
// Обновление UI
updateUI(with: data)
}
}
task.resume()


🟠Core Data
Для выполнения запросов к базе данных можно использовать фоновые контексты (background contexts).
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.perform {
let fetchRequest: NSFetchRequest<Entity> = Entity.fetchRequest()
let results = try? backgroundContext.fetch(fetchRequest)

DispatchQueue.main.async {
// Обновление UI с результатами
updateUI(with: results)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как решить проблему race condition?

Race condition — это ошибка, возникающая при одновременном доступе нескольких потоков к одним и тем же данным без должной синхронизации. Для решения проблемы race condition в Swift используются механизмы синхронизации, такие как блокировки (locks), семафоры или серийные очереди GCD. Эти инструменты позволяют ограничить доступ к общим ресурсам, чтобы только один поток мог их изменять в любой момент времени. Это предотвращает непредсказуемое поведение программы и ошибки при многопоточном доступе к данным.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Почему нельзя записать пример ячейки?

Может быть сложным для реализации из-за необходимости создания множества компонентов даже для простых задач.

🚩Пример реализации ячейки

Создание пользовательской ячейки
import UIKit

class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!

func configure(with text: String) {
titleLabel.text = text
}
}


View
import UIKit

protocol TableViewProtocol: AnyObject {
func updateTableView()
}

class TableViewController: UIViewController, TableViewProtocol {
@IBOutlet weak var tableView: UITableView!
var presenter: TableViewPresenterProtocol?

override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
presenter?.viewDidLoad()
}

func updateTableView() {
tableView.reloadData()
}
}

extension TableViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter?.numberOfRows() ?? 0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
if let text = presenter?.textForRow(at: indexPath) {
cell.configure(with: text)
}
return cell
}
}


Presenter
protocol TableViewPresenterProtocol: AnyObject {
func viewDidLoad()
func numberOfRows() -> Int
func textForRow(at indexPath: IndexPath) -> String
}

class TableViewPresenter: TableViewPresenterProtocol {
weak var view: TableViewProtocol?
var interactor: TableViewInteractorProtocol?

func viewDidLoad() {
interactor?.fetchData()
}

func numberOfRows() -> Int {
return interactor?.data.count ?? 0
}

func textForRow(at indexPath: IndexPath) -> String {
return interactor?.data[indexPath.row] ?? ""
}
}


Interactor
protocol TableViewInteractorProtocol: AnyObject {
var data: [String] { get }
func fetchData()
}

class TableViewInteractor: TableViewInteractorProtocol {
var data: [String] = []

func fetchData() {
data = ["Item 1", "Item 2", "Item 3"]
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔3
🤔 Расскажи про RC?

RC (Reference Counting) в Swift — это механизм управления памятью, который автоматически отслеживает количество ссылок на объекты в памяти. Когда количество ссылок на объект достигает нуля, объект освобождается, и его память становится доступной для повторного использования. ARC (Automatic Reference Counting) управляет ссылками на объекты в фоновом режиме, что позволяет избежать утечек памяти. Однако при работе с циклическими ссылками может потребоваться использовать слабые (`weak`) или необладания (`unowned`) ссылки, чтобы избежать утечек памяти.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Есть ли в функциях моменты, когда используется свойство класса?

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

🚩Примеры использования

🟠Методы класса
Могут использовать свойства класса для чтения или изменения данных. В этом примере метод celebrateBirthday использует свойства name и age для обновления возраста и печати сообщения.
class Person {
var name: String
var age: Int

init(name: String, age: Int) {
self.name = name
self.age = age
}

func celebrateBirthday() {
age += 1
print("Happy Birthday, \(name)! You are now \(age) years old.")
}
}

let person = Person(name: "Alice", age: 30)
person.celebrateBirthday()
// Output: "Happy Birthday, Alice! You are now 31 years old."


🟠Инициализаторы
Используют свойства класса для установки начальных значений при создании экземпляра класса.
class Car {
var model: String
var year: Int

init(model: String, year: Int) {
self.model = model
self.year = year
}

func displayInfo() {
print("Car model: \(model), year: \(year)")
}
}

let car = Car(model: "Toyota", year: 2022)
car.displayInfo()
// Output: "Car model: Toyota, year: 2022"


🟠Замыкания (closures)
Могут захватывать и использовать свойства класса. Это часто используется в асинхронных операциях, таких как сетевые запросы.
class Downloader {
var url: String

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

func download(completion: @escaping () -> Void) {
print("Downloading from \(url)...")
DispatchQueue.global().async {
// Симуляция загрузки
sleep(2)
DispatchQueue.main.async {
completion()
}
}
}
}

let downloader = Downloader(url: "https://example.com/file")
downloader.download {
print("Download completed.")
}
// Output:
// "Downloading from https://example.com/file..."
// (2 секунды спустя)
// "Download completed."


🟠Статические свойства и методы
Используются для данных или функций, которые относятся ко всему классу, а не к конкретному экземпляру. В этом примере статическое свойство count используется для отслеживания количества созданных экземпляров класса.
class Counter {
static var count = 0

init() {
Counter.count += 1
}

static func displayCount() {
print("Number of instances: \(count)")
}
}

let counter1 = Counter()
let counter2 = Counter()
Counter.displayCount()
// Output: "Number of instances: 2"


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое Stack и куча?

Стек (Stack) — это область памяти, которая используется для хранения локальных переменных и вызовов функций. Он организован по принципу LIFO (Last In, First Out), и данные в стеке автоматически освобождаются при завершении вызова функции. Куча (Heap) — это область памяти, используемая для динамического выделения памяти, где объекты хранятся до тех пор, пока на них существуют ссылки. В Swift объекты классов размещаются в куче, а структуры и примитивные типы — в стеке, что влияет на производительность и управление памятью.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍1
🤔 Может ли быть в языке Swift, что существует сильная ссылка на объект и при этом его не существует?

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

🚩Unowned References

Неустранимые ссылки (unowned) не увеличивают счетчик ссылок на объект, к которому они относятся. В отличие от слабых ссылок (weak), unowned ссылки не обнуляются автоматически, когда объект освобождается. Это означает, что если вы попытаетесь обратиться к объекту через unowned ссылку после его освобождения, это приведет к аварийному завершению программы (runtime crash).Если бы вы попытались обратиться к owner после освобождения john, это привело бы к аварийному завершению программы, так как unowned ссылка не обнуляется и остается указателем на освобожденный объект.
class Person {
var name: String
var creditCard: CreditCard?

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

deinit {
print("\(name) is being deinitialized")
}
}

class CreditCard {
var number: Int
unowned var owner: Person

init(number: Int, owner: Person) {
self.number = number
self.owner = owner
}

deinit {
print("CreditCard #\(number) is being deinitialized")
}
}

var john: Person? = Person(name: "John")
john!.creditCard = CreditCard(number: 1234, owner: john!)

john = nil
// Output:
// John is being deinitialized
// CreditCard #1234 is being deinitialized


1⃣Создается объект Person с именем "John" и сильной ссылкой john.
2⃣Создается объект CreditCard, который имеет неустранимую ссылку owner на john.
3⃣Когда john освобождается (при установке john в nil), также освобождается и объект CreditCard.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 В чем разница между синхронными и асинхронными запросами?

Синхронные запросы блокируют выполнение программы до получения ответа, что может замедлить работу приложения, особенно если запрос занимает длительное время. Асинхронные запросы, напротив, не блокируют основной поток и позволяют программе продолжать выполнение других задач, пока ожидается ответ от сервера или другой операции. Асинхронные запросы часто используются для работы с сетью или файловой системой, чтобы улучшить отзывчивость и производительность программ. В Swift асинхронные операции реализуются через GCD или `async/await`.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Расскажи про проблемы работы с Self'ом?

Может вызывать проблемы, особенно в контексте замыканий, циклических ссылок и асинхронных операций.

🚩Проблемы

🟠Циклические ссылки (Retain Cycles)
Возникают, когда два объекта удерживают друг друга сильными ссылками. Решение: Использовать weak или unowned ссылки.
class Person {
var name: String
var apartment: Apartment?

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

class Apartment {
var unit: String
weak var tenant: Person? // Используем weak

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


🟠Захват `self` в замыканиях
Может привести к циклическим ссылкам и утечкам памяти. Решение: Использовать списки захвата (capture lists) с weak или unowned.
class MyViewController: UIViewController {
func loadData() {
someAsyncMethod { [weak self] in
guard let self = self else { return }
print(self.name)
}
}
}


🟠Асинхронные операции
Могут завершиться после освобождения объекта, вызвавшего их. Решение: Использовать weak или unowned ссылки внутри замыканий.
class MyViewController: UIViewController {
func fetchData() {
DispatchQueue.global().async { [weak self] in
sleep(2)
DispatchQueue.main.async {
self?.updateUI()
}
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Что лучше NSOperationQueue или GCD?

NSOperationQueue и GCD оба являются инструментами для управления многозадачностью в Swift, но они имеют разные преимущества. GCD (Grand Central Dispatch) более низкоуровневый и предоставляет простой синтаксис для выполнения задач асинхронно, с меньшими накладными расходами, что делает его более производительным для простых задач. NSOperationQueue, с другой стороны, предлагает более высокоуровневый интерфейс с возможностью управления зависимостями между операциями, приоритетами и отменой задач. NSOperationQueue лучше подходит для сложных сценариев, требующих большей гибкости и контроля над выполнением задач, тогда как GCD предпочтителен для простоты и скорости.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что делает Unowned?

Это ключевое слово, которое используется для создания неустранимых ссылок. Неустранимая ссылка (unowned) не увеличивает счётчик ссылок на объект, к которому она относится, и, в отличие от слабых ссылок (weak), не обнуляется автоматически, когда объект освобождается из памяти. Использование unowned ссылок полезно в тех случаях, когда вы уверены, что объект, на который ссылается unowned ссылка, будет существовать на протяжении всей жизни ссылки.

🚩Особенности

🟠Не увеличивает счётчик ссылок
Unowned ссылки не удерживают объект в памяти, что предотвращает циклические ссылки.
🟠Не обнуляется автоматически
Если объект, на который ссылается unowned ссылка, освобождается из памяти, попытка доступа к этой ссылке приведёт к ошибке времени выполнения (runtime crash).
🟠Использование в ситуациях гарантированного существования
Unowned ссылки следует использовать только тогда, когда вы уверены, что объект будет существовать на протяжении всей жизни ссылки.

🚩Пример использования

class Person {
var name: String
var creditCard: CreditCard?

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

deinit {
print("\(name) is being deinitialized")
}
}

class CreditCard {
var number: Int
unowned var owner: Person

init(number: Int, owner: Person) {
self.number = number
self.owner = owner
}

deinit {
print("CreditCard #\(number) is being deinitialized")
}
}

var john: Person? = Person(name: "John")
john!.creditCard = CreditCard(number: 1234, owner: john!)

john = nil
// Output:
// John is being deinitialized
// CreditCard #1234 is being deinitialized


1⃣Создаётся объект Person с именем "John".
2⃣Создаётся объект CreditCard, который имеет unowned ссылку owner на john.
3⃣Когда объект john освобождается (при присвоении nil), также освобождается и объект CreditCard. Случаи использования unowned

🚩Когда использовать

🟠Гарантированное существование объекта
Вы уверены, что объект будет существовать на протяжении всей жизни ссылки.
🟠Избежание ненужной опциональности: Если объект гарантированно существует, использование unowned позволяет избежать использования опционалов (Optional), что упрощает код.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
🤔 Чем отличается frame от bounds?

Frame определяет положение и размер представления (view) относительно его родительского координатного пространства в iOS. Bounds определяет внутренний прямоугольник представления, используемый для рисования и взаимодействия, и всегда задается относительно собственных координат представления (его левый верхний угол всегда (0,0)).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
🤔 Как работает UITableView?

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

🚩Компоненты

🟠Data Source (Источник данных)
tableView(_:numberOfRowsInSection:): Количество строк.
tableView(_:cellForRowAt:): Конфигурация ячеек.
🟠Delegate (Делегат)
tableView(_:didSelectRowAt:): Обработка выбора строки.
tableView(_:heightForRowAt:): Настройка высоты строки.
🟠Cells (Ячейки)
Ячейки (UITableViewCell) отображают контент.

🚩Настройка `UITableView`

1⃣Создание проекта и добавление таблицы
Добавьте UITableView в ViewController через Interface Builder или программно. Подключите UITableView как IBOutlet.
2⃣Настройка Data Source и Delegate
Назначьте ViewController источником данных и делегатом таблицы. Реализуйте методы протоколов.
import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1000 // Количество строк
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected row: \(indexPath.row)")
}
}


🟠Повторное использование ячеек
Используйте dequeueReusableCell(withIdentifier:for:) для повторного использования ячеек.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 Жизненный цикл UIViewController

Жизненный цикл UIViewController включает несколько ключевых этапов:
1. `loadView` создает представление, которое контроллер управляет.
2. `viewDidLoad` вызывается после загрузки представления контроллера в память.
3. `viewWillAppear` выполняется перед тем, как представление станет видимым.
4. `viewDidAppear` вызывается после того, как представление появилось на экране.
5. `viewWillDisappear` и `viewDidDisappear` вызываются перед и после того, как представление было удалено с экрана.
6. `deinit` вызывается перед освобождением экземпляра контроллера из памяти.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Как сделано VVM?

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

🚩Структура проекта

- Models
- User.swift
- Views
- UserView.swift
- ViewModels
- UserViewModel.swift


🚩Полный пример реализации

🟠Model
Модель представляет данные и бизнес-логику. Например, это может быть структура User.
struct User {
let name: String
let age: Int
}


🟠ViewModel
ViewModel связывает модель и представление, управляя состоянием и логикой представления. Он использует @Published для обновления представления при изменении данных.
import Combine

class UserViewModel: ObservableObject {
@Published var user: User?

func fetchUser() {
// Логика получения данных пользователя (например, из сети или базы данных)
self.user = User(name: "John Doe", age: 30)
}
}


🟠View
Представление отвечает за отображение данных и взаимодействие с пользователем. В SwiftUI представление использует @StateObject или @ObservedObject для наблюдения за изменениями данных в ViewModel.
import SwiftUI

struct UserView: View {
@StateObject var viewModel = UserViewModel()

var body: some View {
VStack {
if let user = viewModel.user {
Text("Name: \(user.name)")
Text("Age: \(user.age)")
} else {
Text("Loading...")
}
}
.onAppear {
viewModel.fetchUser()
}
}
}

struct UserView_Previews: PreviewProvider {
static var previews: some View {
UserView()
}
}


🚩Полный пример

User.swift
struct User {
let name: String
let age: Int
}


UserViewModel.swift
import Combine

class UserViewModel: ObservableObject {
@Published var user: User?

func fetchUser() {
// Логика получения данных пользователя (например, из сети или базы данных)
self.user = User(name: "John Doe", age: 30)
}
}


UserView.swift
import SwiftUI

struct UserView: View {
@StateObject var viewModel = UserViewModel()

var body: some View {
VStack {
if let user = viewModel.user {
Text("Name: \(user.name)")
Text("Age: \(user.age)")
} else {
Text("Loading...")
}
}
.onAppear {
viewModel.fetchUser()
}
}
}

struct UserView_Previews: PreviewProvider {
static var previews: some View {
UserView()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1