Native iOS App Development in Swift
Swift is not just a replacement for Objective-C. It's a different way of thinking about architecture: strict typing, value semantics, async/await instead of callback hell, SwiftUI declarativeness instead of imperative UIKit. An app written with these principles in mind is simpler to maintain and scale.
Architecture: What We Choose and Why
For most product applications, we use Clean Architecture with MVVM at the presentation layer. Separation into layers — Domain, Data, Presentation — allows testing business logic without UI and without dependence on specific data framework.
ViewModel on Swift Concurrency: @MainActor for publishing state to UI thread, Task for background work. No manual DispatchQueue.main.async — compiler checks thread safety via Sendable and actor isolation.
Navigation. UIKit: Coordinator Pattern — AppCoordinator manages UINavigationController, child coordinators handle flows (AuthFlow, MainFlow, OnboardingFlow). This isolates navigation logic from ViewControllers. SwiftUI: NavigationStack with NavigationPath for programmatic navigation, Router object as EnvironmentObject.
Dependencies. Swift Package Manager instead of CocoaPods where possible. SPM is native tool, doesn't require pod install and doesn't break workspace. For packages not yet migrated to SPM (rare in 2024), use CocoaPods selectively.
Typical stack: Alamofire or native URLSession for networking, Combine or async/await for reactivity, Kingfisher for image caching, swift-composable-architecture (TCA) for complex state machines.
Where Time Gets Lost at Start
Cold start. App launches slowly if too much synchronous initialization happens in application(_:didFinishLaunchingWithOptions:): SDK analytics, Core Data stack, Firebase config. Solution: lazy initialization of non-critical services, heavy operations on background queue, MetricKit for monitoring launch time in production.
Memory leaks in closures. Classic: forgot [weak self] in closure passed to NotificationCenter or Timer. Instruments → Leaks + Memory Graph Debugger — mandatory before release. Xcode 15 added compiler warnings for some cases, but not all.
UITableView and UICollectionView with heavy cells. JPEG decoding on main thread in cellForRowAt — FPS drops on fast scroll. Move decoding to background via ImageIO with explicit kCGImageSourceShouldCacheImmediately: true, fill cell with ready CGImage. On iPhone SE 2nd gen difference between correct and incorrect approach is 8 FPS vs 60 FPS on list scroll.
SwiftUI vs UIKit: How We Decide
| Criterion | UIKit | SwiftUI |
|---|---|---|
| iOS minimum | iOS 13+ normally, iOS 12- | iOS 14+ for stable work |
| Custom animations | Full control via Core Animation | Limited but expanding each version |
| List performance | UICollectionView Compositional Layout — best in class | List enough for most; LazyVStack for custom |
| Team | UIKit known to all iOS developers | SwiftUI requires rethinking patterns |
| Complex gestures | UIGestureRecognizer — maximum control |
gesture modifier + GestureState — enough in most cases |
For new projects with iOS 16+ minimum — SwiftUI as foundation, UIKit for components where SwiftUI lags (custom keyboard accessories, some gesture interactions). For iOS 14 support — hybrid where UIKit backbone and SwiftUI screens via UIHostingController.
Development Process
Project start: requirements analysis → technical architecture design → agreement → sprint development. Each module accompanied by unit tests on business logic (XCTest), critical UI flows — UI tests (XCUITest).
CI/CD via Fastlane: fastlane test on each PR, fastlane beta for TestFlight, fastlane release for App Store. Test builds — automatically on merge to develop.
Firebase Crashlytics — connect at project start, not before release. Crashes in TestFlight caught early.
What Affects Timeline
- Number of screens and complexity of navigation flow
- Integrations: payments (StoreKit 2), authorization (Sign in with Apple, OAuth), maps (MapKit), camera/photo (AVFoundation, PhotosUI)
- iPad / Mac Catalyst support requirement
- Ready design and API availability
Straightforward app (auth, feed, profile, details): 3–4 weeks. Product with complex business logic, custom components, integrations: 2–3 months. Cost is calculated individually after ТЗ evaluation.







