Swift | Вопросы собесов
2.13K subscribers
28 photos
951 links
Download Telegram
🤔 Что означает принцип open closed?

Принцип OCP (Open-Closed Principle) гласит:
"Программные сущности должны быть открыты для расширения, но закрыты для модификации."
Это значит, что код должен позволять добавлять новый функционал без изменения существующего кода.

🚩Почему это важно?

Меньше багов – изменения не ломают старый код.
Лучшая поддержка – новый функционал добавляется без переписывания старого.
Гибкость – можно расширять систему без изменения её базовой логики.

🚩Пример нарушения принципа OCP

Допустим, у нас есть класс, который рисует фигуры:
class ShapeDrawer {
func draw(shape: String) {
if shape == "circle" {
print("Рисуем круг")
} else if shape == "square" {
print("Рисуем квадрат")
}
}
}


🚩Как исправить? Используем OCP!

Лучше использовать наследование или протоколы, чтобы расширять функциональность, не меняя существующий код:
protocol Drawable {
func draw()
}

class Circle: Drawable {
func draw() {
print("Рисуем круг")
}
}

class Square: Drawable {
func draw() {
print("Рисуем квадрат")
}
}

class ShapeDrawer {
func draw(shape: Drawable) {
shape.draw()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что значит value type принадлежит reference type?

Это ситуация, когда value type (например, struct) находится внутри reference type (например, класса), и тогда доступ к нему уже регулируется по ссылке через обёртку.


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

В Swift ключи в словаре (Dictionary) должны быть уникальными и сравниваться между собой. Поэтому, если вы хотите использовать*свою структуру в качестве ключа, она должна соответствовать протоколу Hashable.

🚩Что нужно?

Структура должна соответствовать Hashable
Должен быть реализован hash(into:) или использовать Equatable + Hashable автоматически
Свойства структуры должны быть Hashable (если String, Int, Double – все ок)

🚩Пример кастомной структуры в `Dictionary`

struct Person: Hashable {
let id: Int
let name: String
}

// Теперь можно использовать `Person` как ключ
var peopleAges: [Person: Int] = [
Person(id: 1, name: "Alice"): 25,
Person(id: 2, name: "Bob"): 30
]

// Доступ по ключу
let alice = Person(id: 1, name: "Alice")
print(peopleAges[alice] ?? "Не найдено") // 25


🚩Ручная реализация `hash(into:)`

Если нужно кастомное хеширование, можно реализовать hash(into:):
struct Person: Hashable {
let id: Int
let name: String

func hash(into hasher: inout Hasher) {
hasher.combine(id) // Используем только id для хеша
}
}


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

Unowned ссылки используются для предотвращения сильных циклов ссылок (retain cycles) между экземплярами классов, когда два объекта ссылаются друг на друга сильно. В отличие от `weak`, `unowned` не делает объект опциональным и не автоматически обнуляется, что делает его подходящим для случаев, когда другой объект гарантированно будет иметь тот же или более долгий срок жизни.

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

В основе Git Flow – несколько веток с разными ролями, чтобы команда могла параллельно разрабатывать новые фичи, исправлять баги и выпускать релизы.

🚩Как работает Git Flow?

🟠Основные ветки (`main` и `develop`)
- main – содержит только стабильные версии.
- developосновная ветка разработки, в неё вливаются все новые фичи.

🟠Разработка новых фич (feature branches)
- Каждая новая фича создаётся в отдельной ветке feature/*.
- После завершения сливается в develop.
git checkout develop
git checkout -b feature/new-cool-feature
# Разработка...
git checkout develop
git merge feature/new-cool-feature
git branch -d feature/new-cool-feature


🟠Подготовка к релизу (release branches)
- Когда код стабилен, создаётся ветка release/* от develop.
- Здесь можно тестировать и исправлять баги.
- После финального теста сливается в main и develop.
git checkout develop
git checkout -b release/1.0
# Фиксим баги, тестируем...
git checkout main
git merge release/1.0
git tag -a v1.0 -m "Release 1.0"
git checkout develop
git merge release/1.0
git branch -d release/1.0


🟠Горячие фиксы (hotfix branches)
- Если критический баг в main, создаём hotfix/*.
- После исправления вливаем в main и в develop.
git checkout main
git checkout -b hotfix/urgent-bugfix
# Фиксим баг...
git checkout main
git merge hotfix/urgent-bugfix
git checkout develop
git merge hotfix/urgent-bugfix
git branch -d hotfix/urgent-bugfix


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Когда мы создаем объект value type, что хранит переменная?

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


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

В Domain-Driven Design (DDD) управление зависимостями важно для создания четко разделенных и легко управляемых компонентов. Это достигается следующими методами:

🚩Методы управления зависимостями в DDD

🟠Инъекция зависимостей
Инъекция зависимостей позволяет передавать зависимости через конструкторы или сеттеры, снижая связанность и упрощая тестирование.
interface UserRepository {
findById(id: string): Promise<User | null>;
}

class UserService {
private userRepository: UserRepository;

constructor(userRepository: UserRepository) {
this.userRepository = userRepository;
}

public async getUserById(id: string): Promise<User | null> {
return await this.userRepository.findById(id);
}
}


🟠Сервисы домена
Сервисы домена содержат бизнес-логику, не принадлежащую конкретным сущностям или значимым объектам, и могут взаимодействовать с несколькими объектами или внешними сервисами.
class PaymentService {
private paymentGateway: PaymentGateway;

constructor(paymentGateway: PaymentGateway) {
this.paymentGateway = paymentGateway;
}

public async processPayment(order: Order): Promise<boolean> {
return await this.paymentGateway.charge(order.totalAmount);
}
}


🟠Анти-коррупционные слои
Анти-коррупционные слои защищают доменную модель от внешних влияний и преобразуют данные в понятные форматы.
class ExternalUserService {
public getUserData(id: string): ExternalUser {
// Получение данных от внешнего сервиса
}
}

class UserService {
private externalUserService: ExternalUserService;

constructor(externalUserService: ExternalUserService) {
this.externalUserService = externalUserService;
}

public getUser(id: string): User {
const externalUser = this.externalUserService.getUserData(id);
return new User(externalUser.id, externalUser.name, externalUser.email);
}
}


🟠Контейнеры зависимостей
Контейнеры управляют созданием и жизненным циклом зависимостей, упрощая конфигурацию и разрешение зависимостей.
import "reflect-metadata";
import { Container, injectable, inject } from "inversify";

@injectable()
class UserRepositoryImpl implements UserRepository {
public async findById(id: string): Promise<User | null> {
// Реализация метода
}
}

@injectable()
class UserService {
private userRepository: UserRepository;

constructor(@inject("UserRepository") userRepository: UserRepository) {
this.userRepository = userRepository;
}

public async getUserById(id: string): Promise<User | null> {
return await this.userRepository.findById(id);
}
}

// Конфигурация контейнера
const container = new Container();
container.bind<UserRepository>("UserRepository").to(UserRepositoryImpl);
container.bind<UserService>(UserService).toSelf();

// Разрешение зависимостей
const userService = container.get<UserService>(UserService);


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как поменять иконку приложения в зависимости от среды?

Для этого используют App Icon Sets, и в Info.plist можно указать набор иконок в зависимости от build-конфигурации. В Xcode можно также использовать xcconfig или Build Scheme, чтобы переключать иконки по окружению.


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

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

🚩Как работают push-уведомления

1⃣Регистрация устройства
Когда приложение устанавливается и запускается на устройстве, оно регистрируется для получения push-уведомлений. Для этого приложение отправляет запрос к Apple Push Notification Service (APNs) с запросом на получение уникального токена устройства (device token).

2⃣Получение токена устройства
APNs генерирует уникальный токен для устройства и отправляет его обратно приложению. Приложение затем передает этот токен на свой сервер.

3⃣Отправка уведомления на сервер
Когда необходимо отправить push-уведомление, сервер приложения формирует сообщение, включающее содержимое уведомления и токен устройства, и отправляет его на APNs.

4⃣Доставка уведомления
APNs принимает сообщение от сервера, определяет устройство по токену и отправляет уведомление на это устройство.

5⃣Получение уведомления на устройстве
Когда устройство получает уведомление, операционная система отображает его пользователю. Если пользователь взаимодействует с уведомлением, приложение может выполнить определенные действия, такие как открытие конкретного экрана.

🚩Пример кода для регистрации устройства

В AppDelegate
import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Запрос разрешения на отправку уведомлений
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
return true
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Преобразуем токен в строку
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")

// Отправляем токен на сервер
// serverAPI.registerDeviceToken(token)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие property wrapper знаешь (iOS 17) ?

Некоторые основные и нововведённые property wrappers:
-
@State
-
@Binding
-
@ObservedObject
-
@StateObject
-
@Environment
-
@EnvironmentObject

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

В Swift словарь (`Dictionary<Key, Value>`) – это неупорядоченная коллекция пар `ключ → значение`, где ключи уникальны.

🟠Сравнение двух словарей (`==`)
Swift позволяет сравнивать два словаря по их ключам и значениям, если и ключи, и значения соответствуют протоколу Equatable.
let dict1 = ["name": "Alice", "age": "25"]
let dict2 = ["name": "Alice", "age": "25"]
let dict3 = ["name": "Bob", "age": "30"]

print(dict1 == dict2) // true (значения одинаковые)
print(dict1 == dict3) // false (разные значения)


Ошибка при сравнении Dictionary без Equatable
Если значения не соответствуют `Equatable`, сравнение не сработает:
struct User {
let name: String
}

let dict1 = ["user": User(name: "Alice")]
let dict2 = ["user": User(name: "Alice")]

// Ошибка: Type 'User' does not conform to protocol 'Equatable'
// print(dict1 == dict2)


Решение: Сделать User Equatable:
struct User: Equatable {
let name: String
}

print(dict1 == dict2) // Теперь работает!


🟠Поиск и сравнение отдельных значений
Можно сравнивать отдельные элементы по ключу.
let dict = ["name": "Alice", "age": "25"]

if dict["name"] == "Alice" {
print("Имя совпадает") //
}


🟠Сравнение по ключам (`keys`) и значениям (`values`)
Можно сравнить только ключи или только значения.
let dict1 = ["name": "Alice", "age": "25"]
let dict2 = ["age": "30", "name": "Alice"]

print(dict1.keys == dict2.keys) // true (ключи одинаковые)


Сравнение значений
print(Set(dict1.values) == Set(dict2.values)) //  true (если порядок неважен)


🟠Сравнение словарей с разными типами (`Any`)
Если словарь хранит Any, то прямое сравнение не сработает, и нужно сравнивать элементы вручную.
let dict1: [String: Any] = ["name": "Alice", "age": 25]
let dict2: [String: Any] = ["name": "Alice", "age": 25]

// Функция сравнения словарей с `Any`
func areDictionariesEqual(_ dict1: [String: Any], _ dict2: [String: Any]) -> Bool {
return NSDictionary(dictionary: dict1).isEqual(to: dict2)
}

print(areDictionariesEqual(dict1, dict2)) // true


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

Да, если используется Notification Service Extension, можно перехватить и изменить UNNotificationRequest. Это должно быть сделано быстро, так как система ограничивает время на обработку.


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