Ответ
Работал со следующим стеком технологий для iOS:
UI & Фреймворки:
- UIKit: Создание интерфейсов через Storyboards, XIB и код (программные constraints).
- SwiftUI: Разработка декларативных интерфейсов, использование
@State,@ObservedObject,@EnvironmentObject. - Combine: Реактивное программирование для обработки асинхронных событий (
Publisher,Subscriber, операторы).
Архитектуры:
- MVC, MVVM, VIPER: Понимание их принципов, сильных и слабых сторон. Предпочтение MVVM+C для разделения ответственности и тестируемости.
Хранение данных:
- Core Data: Работа с managed object context, создание моделей, выполнение fetch requests.
- UserDefaults, Keychain: Для хранения простых настроек и защищенных данных соответственно.
- Файловая система: Работа с
FileManager.
Сетевое взаимодействие:
- URLSession: Для выполнения базовых сетевых запросов (
dataTask,uploadTask). - Alamofire: Для упрощения сложных сетевых операций и обработки ошибок.
Многопоточность:
- Grand Central Dispatch (GCD): Использование
DispatchQueue(main, global, custom) для управления задачами. - OperationQueue: Для зависимых и отменяемых операций.
Пример стека (MVVM + Combine + URLSession):
// Модель
struct User: Codable { let id: Int; let name: String }
// ViewModel
class UserViewModel: ObservableObject {
@Published var users: [User] = []
private var cancellables = Set<AnyCancellable>()
func fetchUsers() {
guard let url = URL(string: "https://api.example.com/users") else { return }
URLSession.shared.dataTaskPublisher(for: url)
.map( .data )
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: []) // Обработка ошибок
.receive(on: DispatchQueue.main) // Возврат в главный поток
.assign(to: .users, on: self)
.store(in: &cancellables)
}
}
// View (SwiftUI)
struct UserListView: View {
@StateObject var viewModel = UserViewModel()
var body: some View {
List(viewModel.users) { user in
Text(user.name)
}.onAppear { viewModel.fetchUsers() }
}
} Ответ 18+ 🔞
А, ну вот, классика жанра, подъехали! Смотри, какие у меня были инструменты для лепки этих ваших айфонских приложений. Сейчас разложу всё по полочкам, но не обессудь, если где-то матерок проскочит для пущей ясности, а то без него как-то сухо получается.
Вся эта ваша визуальная хуйня (UI & Фреймворки):
- UIKit: Это наш старый, добрый, местами упоротый дед. Интерфейсы клепал и через эти ваши сториборды с ксибами, где всё намазано, и чисто кодом, прописывая каждую констрейнту, как мантру. Сплошное
NSLayoutConstraint.activate, блядь. - SwiftUI: А это уже модная, молодая и дерзкая штука. Объявил вьюху, накидал модификаторов — и вроде как готово. Только вот эти
@State,@ObservedObject— смотри, чтобы не перепутать, а то приложение вздрочнется и сдохнет.@EnvironmentObject— это вообще как глобальная переменная, только чуть культурнее. - Combine: О, епта! Это чтобы не писать тонны колбэков и не отслеживать, кто кого обновил. Подписался на
Publisher, обработал через операторы (типаmap,flatMap,filter) — и сидишь, как падишах, данные сами текут. Красота, только мозг сначала надо настроить на реактивный лад.
Как мы архитектуру строили, чтобы не превратить проект в говногодзиллу:
- MVC, MVVM, VIPER: MVC — это как общежитие, где контроллер обрастает жиром и становится неподъёмным. MVVM — уже цивилизованнее, логику в ViewModel выносишь. А VIPER — это когда совсем охуели и хотите, чтобы каждый модуль жил своей жизнью, как монах в келье. Я больше склонялся к MVVM + Combine, чтоб всё было чётко и тестируемо.
Куда данные пихали (Хранение данных):
- Core Data: Мощная бандура, но иногда такая замысловатая, что хочется в рот себе чих-пых.
NSManagedObjectContext,NSPersistentContainer,NSFetchRequest— выучил как «Отче наш». Когда работает — красота, когда глючит — ищешь виноватого три дня. - UserDefaults, Keychain: Первое — для какой-нибудь хуеты типа «включить тёмную тему». Второе — для паролей и токенов, чтоб их никто не спиздил.
- Файловая система: Старый добрый
FileManager. Сохранил картинку, прочитал JSON — всё просто.
Как с миром общались (Сетевое взаимодействие):
- URLSession: Фундамент.
dataTask,uploadTask— всё руками собирал, как лего. Надо и заголовки добавить, и ошибки обработать. Работает, но писать много. - Alamofire: Пришёл, как кавалерия, когда заебало с URLSession возиться. Обёртка, которая кучу boilerplate-кода убирает. Красиво и мощно.
Как всё одновременно делали, не подвешивая интерфейс (Многопоточность):
- Grand Central Dispatch (GCD): Основа основ.
DispatchQueue.main— чтобы обновить интерфейс,DispatchQueue.global()— чтобы тяжёлую хуйню в фоне гонять. Главное — не наделать race condition, а то будет весело. - OperationQueue: Когда задачи зависят друг от друга (сначала скачай, потом распакуй) или их нужно отменять. Поумнее, чем просто GCD.
А вот живой пример, как это всё в одном флаконе может смотреться (MVVM + Combine + URLSession):
// Модель (просто структура, которая данные описывает)
struct User: Codable { let id: Int; let name: String }
// ViewModel (мозги нашей вьюхи)
class UserViewModel: ObservableObject {
@Published var users: [User] = [] // На это свойство вьюха подписывается
private var cancellables = Set<AnyCancellable>() // Сюда подписки складываем, чтобы не потерять
func fetchUsers() {
guard let url = URL(string: "https://api.example.com/users") else { return }
// Вся магия Combine и URLSession
URLSession.shared.dataTaskPublisher(for: url) // 1. Делаем сетевой запрос (Publisher)
.map( .data ) // 2. Вытаскиваем только данные из ответа
.decode(type: [User].self, decoder: JSONDecoder()) // 3. Декодируем JSON в массив наших User
.replaceError(with: []) // 4. Если ошибка (сеть отвалилась, JSON кривой) — возвращаем пустой массив
.receive(on: DispatchQueue.main) // 5. ВАЖНО! Возвращаем результат в главный поток
.assign(to: .users, on: self) // 6. Кладём полученный массив в свойство `users`
.store(in: &cancellables) // 7. Сохраняем подписку, чтобы она не сдохла раньше времени
}
}
// View (SwiftUI) — это уже лицо приложения
struct UserListView: View {
@StateObject var viewModel = UserViewModel() // Создаём или получаем ViewModel
var body: some View {
List(viewModel.users) { user in // Список, который зависит от users
Text(user.name)
}
.onAppear { viewModel.fetchUsers() } // При появлении экрана — дергаем метод загрузки
}
}
Вот и вся недолга, блядь. Выглядит компактно, а под капотом — и реактивщина, и сеть, и многопоточность. Главное — не запутаться в этих подписках, а то будет Cancellable болтаться, как манда с ушами, и память течь.