День из жизни Swift-разработчика: от чашки кофе до деплоя в App Store

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как выглядит типичный рабочий день Swift-разработчика в 2025 году

Когда я только начинал изучать Swift, мне казалось, что iOS-разработчики — это такие волшебники, которые с утра до вечера пишут идеальный код под чашечку латте в стильном коворкинге. Реальность, как вы догадываетесь, оказалась прозаичнее, но не менее интересной.

В этой статье я проведу вас через типичный рабочий день Swift разработчика — от утреннего кофе до вечернего пуша в App Store. Вы увидите не только расписание, но и реальные фрагменты кода, с которыми мы сталкиваемся каждый день. Поехали!

09:00 — Утро: Daily Stand-up и планирование

День начинается с короткого стендапа в Zoom или Slack. Мы — команда из 5 iOS-разработчиков, двух бэкендеров и продакта. Каждый говорит по 1-2 минуты: что сделано вчера, что планируется сегодня, есть ли блокеры.

Типичный мой ответ звучит так:

  • Вчера: Закончил рефакторинг модуля авторизации на Combine.
  • Сегодня: Интегрирую новый экран профиля с SwiftUI, нужно сделать анимацию перехода.
  • Блокер: Жду от дизайнера финальные мокапы для тёмной темы.

После стендапа — 15 минут на разбор сообщений в Jira и GitHub. Смотрю, какие PR требуют моего ревью, и читаю комментарии к своим пул-реквестам.

09:45 — Первая глубокая работа: Новый функционал

Утро — лучшее время для сложных задач. Сегодня я пишу модуль Push-уведомлений с deep linking. Вот как выглядит типичная структура класса-обработчика:

import UIKit
import UserNotifications

final class PushNotificationHandler: NSObject, UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void
    ) {
        let userInfo = response.notification.request.content.userInfo
        
        guard let deepLink = DeepLinkParser.parse(userInfo) else {
            completionHandler()
            return
        }
        
        Task { @MainActor in
            Coordinator.shared.handle(deepLink)
        }
        
        completionHandler()
    }
    
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
    ) {
        completionHandler([.banner, .sound, .badge])
    }
}

Обратите внимание на Task { @MainActor in } — это современный способ форсировать выполнение на главном потоке без старомодного DispatchQueue.main.async. iOS разработчик сейчас активно использует Concurrency из Swift 5.5+.

11:00 — Code Review: Взгляд со стороны

Приходит уведомление от коллеги: «Посмотри PR, пожалуйста». Открываю GitHub, вижу изменения в модели данных. Вот пример того, что я часто комментирую:

// ❌ Плохо: force unwrap
let user = users.first!

// ✅ Хорошо: безопасное извлечение
guard let user = users.first else {
    assertionFailure("Список users не должен быть пустым на этом этапе")
    return
}

На code review мы обсуждаем не только корректность, но и архитектуру. Сегодня коллега использовал SwiftUI с @ObservableObject, но по нашему код-стайлу мы переходим на @Observable (iOS 17+). Делаю мягкое замечание:

// Рекомендуем переписать на @Observable
@Observable
final class ProfileViewModel {
    var name: String = ""
    var age: Int = 0
}

12:30 — Обед и переключение контекста

Обед — святое. Выхожу на улицу или готовлю сам. Стараюсь не смотреть в экран — глаза отдыхают. За обедом часто читаю телеграм-каналы по iOS разработке: «Swift by Sundell», «iOS Goodies», «Российский Swift». Это помогает быть в курсе новых фич.

Кстати, сегодня наткнулся на интересную статью про Swift Testing — новый фреймворк, который Apple представила как альтернативу XCTest. Записал себе в заметки попробовать на pet-проекте.

14:00 — Работа с базой данных: Core Data и SwiftData

Вторая половина дня — время для более рутинных, но важных задач. Сегодня нужно добавить кэширование в наш новостной дайджест. Мы используем CoreData с легковесной миграцией, но постепенно переходим на SwiftData (Swift 6).

Вот как выглядит типичный запрос на сохранение:

import CoreData

class NewsCacheManager {
    private let context: NSManagedObjectContext
    
    init(context: NSManagedObjectContext) {
        self.context = context
    }
    
    func saveArticles(_ articles: [ArticleDTO]) throws {
        for article in articles {
            let cached = CachedArticle(context: context)
            cached.id = article.id
            cached.title = article.title
            cached.url = article.url
            cached.cachedAt = Date()
        }
        
        if context.hasChanges {
            try context.save()
        }
    }
    
    func fetchCachedArticles() -> [CachedArticle] {
        let request = CachedArticle.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(key: "cachedAt", ascending: false)]
        request.fetchLimit = 50
        
        do {
            return try context.fetch(request)
        } catch {
            print("Ошибка чтения кэша: \(error)")
            return []
        }
    }
}

Обратите внимание: я всегда передаю NSManagedObjectContext через инициализатор, а не создаю внутри. Это позволяет легко тестировать код и использовать viewContext для UI и backgroundContext для тяжелых операций.

15:30 — Тестирование и отладка

Написал код — напиши тесты. Это правило я выучил после того, как однажды забыл проверить граничные случаи, и приложение упало у пяти тысяч пользователей. Больше не повторяю.

Пишу unit-тест для нашего кэша:

import XCTest
@testable import MyNewsApp

final class NewsCacheManagerTests: XCTestCase {
    
    var sut: NewsCacheManager!
    var container: NSPersistentContainer!
    
    override func setUp() {
        super.setUp()
        container = NSPersistentContainer(name: "NewsModel")
        // Используем InMemoryStore для тестов
        let description = NSPersistentStoreDescription()
        description.type = NSInMemoryStoreType
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores { _, error in
            XCTAssertNil(error)
        }
        sut = NewsCacheManager(context: container.viewContext)
    }
    
    func testSaveAndFetchArticles() throws {
        let articles = [ArticleDTO(id: "1", title: "Тест", url: "https://example.com")]
        try sut.saveArticles(articles)
        
        let cached = sut.fetchCachedArticles()
        XCTAssertEqual(cached.count, 1)
        XCTAssertEqual(cached.first?.title, "Тест")
    }
}

Запускаю тесты — зелёные. Чувствую себя уверенно. Теперь можно делать коммит.

17:00 — Ком

Блоги

Рекомендаций по книгам