MFormations
Modern Mobile Engineering

Chapitre 5

05 - iOS Natif

05 - iOS Natif

05 - iOS Natif : Cours complet

Table des matières

  1. Xcode : projet, schemes, signing
  2. Swift Package Manager et CocoaPods
  3. App lifecycle : scene et UIApplicationDelegate
  4. UIKit fundamentals
  5. Auto Layout et constraints
  6. GCD et OperationQueue
  7. Delegation et KVO
  8. Combine
  9. App Store : TestFlight et review
  10. Résumé et checklist

1. Xcode : projet, schemes, signing

1.1 Xcode

Xcode est l'IDE officiel d'Apple (macOS uniquement). Il comprend l'éditeur, le simulateur, les instruments (profiling), le debugger et le générateur d'UI (SwiftUI/Storyboard).

Composants clés :

  • Projet (.xcodeproj) : contient targets, build settings, schemes.
  • Workspace (.xcworkspace) : contient le projet + les packages pods.
  • Target : une cible de build (app, extension, tests…).
  • Scheme : définit quelles actions (Run/Test/Profile/Analyze/Archive) et quelle configuration utiliser.

1.2 Structure d'un projet

MyApp/
├── MyApp.xcodeproj
├── MyApp/
│   ├── MyAppApp.swift        # @main, App entry (SwiftUI)
│   ├── ContentView.swift
│   ├── Assets.xcassets
│   ├── Info.plist
│   └── Preview Content/
├── MyAppTests/
├── MyAppUITests/
└── (SwiftPackage via File > Add Package Dependencies)

1.3 Schemes

Diagramme en cours de génération...
  • Un scheme lie une target à des configurations (Debug/Release).
  • Configs partagées dans le fichier .xcscheme (à commiter).
  • On peut ajouter des actions de build pré/post (scripts).

1.4 Signing et provisioning

  • Signing : signature de l'app avec un certificat (Developer ID, Apple Development, Distribution).
  • Provisioning profile : associe l'app (Bundle ID) aux devices autorisés + entitlements (push, App Groups, etc.).
  • Automatic signing : Xcode gère la plupart (nécessite un compte Apple ID/Developer).
  • Manual signing : nécessaire pour les cas complexes (CI, app groups multiples).

Entitlements courants :

<!-- App.entitlements -->
<dict>
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>applinks:example.com</string>
    </array>
    <key>aps-environment</key>
    <string>development</string>
</dict>

1.5 Capabilities

On active les capacités dans Signing & Capabilities : Push, Background Modes, App Groups, Keychain Sharing, Associated Domains, In-App Purchase…


2. Swift Package Manager et CocoaPods

2.1 Swift Package Manager (SPM)

Le gestionnaire natif d'Apple, intégré à Xcode.

// Package.swift
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [.iOS(.v17)],
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.10.0"),
    ],
    targets: [
        .executableTarget(
            name: "MyApp",
            dependencies: ["Alamofire"]
        )
    ]
)

Ajout dans Xcode : File → Add Package Dependencies… → URL ou search.

2.2 CocoaPods

Ancien standard, toujours utilisé par beaucoup d'équipes.

# Podfile
platform :ios, '17.0'
use_frameworks!

target 'MyApp' do
  pod 'Alamofire', '~> 5.10'
end
pod install
open MyApp.xcworkspace

2.3 SPM vs CocoaPods

CritèreSPMCocoaPods
IntégrationNative XcodeWorkspace généré
VitesseRapideMoyenne
ÉcosystèmeJeune, en croissanceMature
Apps dyn/staticPackagePods
Recommandation 2026Par défautHéritage/maintenance

3. App lifecycle : scene et UIApplicationDelegate

3.1 Architecture des scènes (iOS 13+)

  • UIApplicationDelegate : cycle de vie de l'app (lancement, liens, push).
  • UISceneDelegate (ex. UIWindowSceneDelegate) : cycle de vie d'une scène (fenêtre).
Diagramme en cours de génération...

3.2 Étapes

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        return true
    }
}

SwiftUI s'appuie sur AppDelegate seulement pour les intégrations (push, analytics). La vie de l'UI est gérée par la App :

@main
struct MyAppApp: App {
    @Environment(\.scenePhase) private var scenePhase

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { _, phase in
            switch phase {
            case .active: // reprendre
            case .inactive: // transition
            case .background: // sauvegarder
            @unknown default: break
            }
        }
    }
}

3.3 Règles

  • Sauvegarder dans scenePhase == .background.
  • Les tâches longues à la mise en arrière-plan : beginBackgroundTask ou BGTaskScheduler.
  • Ne pas mettre à jour l'UI en .background.

4. UIKit fundamentals

4.1 UIViewController

final class HomeViewController: UIViewController {
    private let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        label.text = "Bonjour"
        label.font = .preferredFont(forTextStyle: .headline)
        view.addSubview(label)
    }
}

Cycle : loadView → viewDidLoad → viewWillAppear → viewDidAppear → viewWillDisappear → viewDidDisappear → viewDidLoad (pop) → deinit.

4.2 UIView

  • Toute hiérarchie d'UI : UIView, UILabel, UIButton, UIImageView, UITableView, UICollectionView, UIScrollView
  • Propriétés : frame, bounds, center, transform, alpha, isHidden, tintColor.

4.3 Construire une UI programmatiquement

final class ProfileViewController: UIViewController {
    private let avatar = UIImageView()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
    }

    private func setupViews() {
        view.addSubview(avatar)
        avatar.translatesAutoresizingMaskIntoConstraints = false
        avatar.layer.cornerRadius = 32
        avatar.layer.masksToBounds = true

        NSLayoutConstraint.activate([
            avatar.widthAnchor.constraint(equalToConstant: 64),
            avatar.heightAnchor.constraint(equalToConstant: 64),
            avatar.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            avatar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24),
        ])
    }
}

4.4 Présentation de contrôleurs

let detail = DetailViewController()
detail.product = product
navigationController?.pushViewController(detail, animated: true)   // empiler
present(detail, animated: true)                                     // modal

5. Auto Layout et constraints

5.1 Principe

Auto Layout résout un système d'équations entre les vues, indépendant de la taille d'écran.

NSLayoutConstraint.activate([
    titleLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
    titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
    titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
])

5.2 Composantes

  • Anchors : leading/trailing/top/bottom, width/height, centerX/centerY.
  • Priorités : required (1000), defaultHigh (750), defaultLow (250).
  • Contraintes intrinsèques : labels/boutons ont une taille par contenu (contentHuggingPriority, contentCompressionResistancePriority).

5.3 Stack views

let stack = UIStackView(arrangedSubviews: [title, subtitle, button])
stack.axis = .vertical
stack.spacing = 12
stack.alignment = .fill
stack.distribution = .fill

5.4 Storyboards vs programmatique

CritèreStoryboardProgrammatique
VisuelOuiNon
Merge/diffDifficileFacile
ReuseMoyenneForte
Recommandation 2026MaintenanceNeuf + SwiftUI

6. GCD et OperationQueue

6.1 GCD (Grand Central Dispatch)

// file background, retour sur le main
DispatchQueue.global(qos: .userInitiated).async {
    let data = heavyCompute()
    DispatchQueue.main.async {
        self.updateUI(data)
    }
}

QoS (Quality of Service) : .userInteractive, .userInitiated, .utility, .background. Plus la QoS est élevée, plus la priorité et la consommation énergie sont hautes.

6.2 DispatchQueue modernes

// async/await (recommandé pour le neuf)
Task {
    let data = try await repository.fetch()
    updateUI(data)   // déjà sur MainActor
}

6.3 OperationQueue

Couche au-dessus de GCD : Operation + dépendances + annulation.

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2

let download = BlockOperation { /* télécharger */ }
let process = BlockOperation { /* traiter */ }
process.addDependency(download)
queue.addOperations([download, process], waitUntilFinished: false)

6.4 Règles

  • Ne jamais toucher l'UI hors du main thread.
  • QoS adaptée au travail (userInitiated pour ce que l'utilisateur attend).
  • Annuler proprement les opérations ; vérifier isCancelled.

7. Delegation et KVO

7.1 Delegation

Pattern central de UIKit : l'objet (data source/delegate) délégué reçoit des appels.

final class FeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    private var items: [String] = []

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }

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

Protocoles de delegation : UITableViewDataSource, UITableViewDelegate, UICollectionViewDataSource

7.2 KVO (Key-Value Observing)

Observation du changement d'une propriété Objective-C.

@objc dynamic var progress: Double = 0

// observateur
observe(\DownloadTask.progress, options: [.new]) { task, change in
    print(change.newValue ?? 0)
}

KVO est peu utilisé en Swift moderne : Combine et Observation l'ont en grande partie remplacé.

7.3 Protocols & weak delegates

protocol PurchaseDelegate: AnyObject {
    func didPurchase(product: Product)
}

final class PurchaseFlow {
    weak var delegate: PurchaseDelegate?   // weak pour éviter le cycle
}

8. Combine

8.1 Principe

Combine est le framework de programmation réactive d'Apple (iOS 13+) : Publisher, Subscriber, Operator.

import Combine

final class SearchViewModel: ObservableObject {
    @Published var query = ""
    @Published var results: [Product] = []

    private var cancellables = Set<AnyCancellable>()

    init(search: @escaping (String) async throws -> [Product]) {
        $query
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink { [weak self] q in
                Task {
                    self?.results = try await search(q)
                }
            }
            .store(in: &cancellables)
    }
}

8.2 Opérateurs

map, filter, flatMap, combineLatest, zip, debounce, throttle, removeDuplicates, catch, receive(on:).

8.3 Sources

  • @Published sur un ObservableObject.
  • URLSession.dataTaskPublisher.
  • NotificationCenter.default.publisher.
  • Timer.publish.

8.4 Combine vs async/await

CritèreCombineasync/await
StyleRéactif (flux)Séquentiel
API publiques modernesMoinsPlus (SwiftUI, URLSession)
ComplexitéÉlevéeFaible
CasFlux complexes, backpressureLa majorité du code

Règle 2026 : async/await pour la logique, Combine (ou Observation) pour l'état observable, en conservant la compatibilité.


9. App Store : TestFlight et review

9.1 Le cycle de distribution

Diagramme en cours de génération...

9.2 TestFlight

  • Jusqu'à 10 000 testeurs (90 jours par build).
  • Interne : jusqu'à 100 membres de l'équipe (pas de review).
  • Externe : jusqu'à 10 000 testeurs (review Basic pour la 1re version externe).
  • Exporte les métadonnées (installations, sessions, crashs).

9.3 Review

  • Guidelines 5.x (design), 3.x (business)… : à lire avant de soumettre.
  • Délai typique 24–48 h ; litiges possibles.
  • L'app doit être complète : pas de placeholders, pas d'accès révoqué.
  • App Privacy : le nutrition label doit correspondre à ce que l'app collecte réellement.

9.4 Checklist de soumission

  • Version et build incrementés
  • Tests verts (unitaires + UI) sur device réel
  • Privacy manifest + nutrition label à jour
  • Screenshots pour toutes les tailles requises
  • Vidéo d'aperçu (App Preview) si pertinent
  • Compte démo/fonctionnalité de connexion pour le reviewer si besoin
  • Rollout : manual or phased release

10. Résumé et checklist

10.1 Points clés

  • Xcode : projets, targets, schemes, signing (automatique par défaut).
  • SPM par défaut, CocoaPods en héritage.
  • Scene lifecycle : ScenePhase (SwiftUI) ou sceneDelegate (UIKit).
  • UIKit : UIViewController + UIView + Auto Layout.
  • GCD/OperationQueue : concurrence ; async/await recommandé pour le neuf.
  • Delegation partout en UIKit ; KVO remplacé par Observation/Combine.
  • Combine : Publisher/Subscriber pour les flux.
  • Distribution : Archive → TestFlight → Review → App Store.

10.2 Checklist

  • Je crée un projet Xcode et je comprends targets/schemes
  • Je configure le signing (automatique)
  • J'ajoute une dépendance SPM
  • Je gère le scenePhase de mon app
  • Je construis une UI UIKit programmatique + Auto Layout
  • J'exécute du travail en background avec GCD (QoS correcte)
  • J'implémente un delegate weak
  • Je crée un @Published + Combine pipeline
  • Je soumets un build TestFlight

Prochain chapitre

06-Swift : langage Swift moderne, async/await, actors, Codable, Swift 6.