Der App Store ist kein nachsichtiger Ort. Allein im Jahr 2024 hat Apple 7,77 Millionen App-Einreichungen überprüft und fast 1,93 Millionen abgelehnt, etwa jede vierte. Performance-Probleme, Datenschutzverletzungen und Design-Inkonsistenzen waren die Hauptgründe für Ablehnungen, und viele dieser Probleme wären in einem guten Code-Review gefunden worden, bevor die Einreichung überhaupt eingereicht wurde.
Laut Business of Apps erreichten die Verbraucherausgaben auf iOS im Jahr 2025 117,6 Milliarden Dollar, und iOS-Nutzer geben pro App fast doppelt so viel aus wie Android-Nutzer. Dieses Ökosystem belohnt Apps, die nach hohem Standard entwickelt wurden, und bestraft diejenigen, die bei Qualität oder Sicherheit Abstriche machen.
Ein strukturiertes Code-Review ist eine der direktesten Investitionen, die ein iOS-Team vor dem Launch oder vor einer Finanzierungsrunde vornehmen kann. Es ist auch ein Muss, wenn Sie planen, Ihre Codebasis an einen neuen Entwicklungspartner zu übergeben. Es ist der Schritt, der eine funktionierende App in eine verwandelt, die funktioniert bleibt.
Heute teilen wir eine Checkliste mit Ihnen, die erfasst, was unsere Ingenieure in jedem Review markieren, strukturiert um die Probleme, die iOS-Projekte tatsächlich in der Produktion und in der App-Store-Review-Warteschlange versenken. Beachten Sie, dass Apple bei vibe-codierten Apps noch strenger ist. Wenn Sie also möchten, dass Ihre erfolgreich ist, könnte ein professionelles Audit der Schlüssel sein, damit sie vom App Store akzeptiert wird.
Vorbereitung vor dem Review
Ein produktives Code-Review beginnt, bevor eine einzige Zeile gelesen wird. Den Kontext von Anfang an richtig zu verstehen bedeutet, dass sich das Review selbst auf echte Probleme konzentriert statt auf Einrichtungsverwirrung.
Umfang und Ziele definieren:
- Feststellen, ob das Review die vollständige Codebasis, ein bestimmtes Feature-Modul, eine ausstehende App-Store-Einreichung oder die Vorbereitung für eine Investoren-Due-Diligence abdeckt
- Messbare Erfolgskriterien festlegen: weniger Abstürze, behobene Sicherheitsprobleme, verbesserte Testabdeckung oder Bereitschaft für eine bestimmte iOS-Version
- Die Zielgeräte der App und die minimale iOS-Version klären, da verschiedene Versionen unterschiedliche APIs, Berechtigungsanforderungen und Verhaltensbesonderheiten haben
- Feststellen, welche Extensions im Umfang enthalten sind: App Clips, WidgetKit-Extensions, Live Activities und Share-Extensions haben jeweils ihre eigene Review-Oberfläche
Umgebung und Tools überprüfen:
- Bestätigen, dass das Projekt in Xcode ohne Warnungen oder Fehler in Debug- und Release-Konfigurationen sauber erstellt wird
- Prüfen, ob die korrekte, stabile Version von Xcode verwendet wird. Apple erzwingt SDK-Anforderungen, und die Verwendung einer veralteten Version kann App-Store-Einreichungen vollständig verhindern
- Sicherstellen, dass alle Teammitglieder dieselben Swift- und Abhängigkeitsversionen verwenden, da Versionsabweichungen subtile Fehler einführen, die schwer zu verfolgen sind
- Das Projekt auf einem physischen Gerät ausführen, nicht nur im Simulator, da bestimmte Speicher-, Performance- und Hardware-Probleme nur auf echter Hardware auftreten
Abhängigkeiten und Paketverwaltung überprüfen:
- Bestätigen, dass alle Drittanbieter-Pakete konsistent über Swift Package Manager, CocoaPods oder Carthage verwaltet werden, nicht eine Mischung aller drei ohne dokumentierte Begründung
- Ein Abhängigkeits-Audit durchführen und verifizieren, dass keine Pakete bekannte Sicherheitslücken aufweisen
- Das letzte Veröffentlichungsdatum und die Wartungsaktivität jeder Abhängigkeit prüfen. Eine aufgegebene Bibliothek ist ein zukünftiges Kompatibilitäts- und Sicherheitsrisiko
- Bestätigen, dass Abhängigkeits-Lock-Dateien (Package.resolved, Podfile.lock) in die Versionskontrolle eingecheckt sind, damit alle Teammitglieder gegen identische Versionen bauen
- Prüfen, dass keine Abhängigkeit ein transitives Paket mit einer restriktiven oder inkompatiblen Lizenz einführt. Eine GPL-lizenzierte transitive Abhängigkeit in einer kommerziellen App-Store-App ist ein rechtliches Problem
- Verifizieren, dass Swift-Package-Manager-Abhängigkeiten in sicherheitssensiblen Projekten auf genaue Versionen oder Commit-Hashes gepinnt sind, nicht auf fließende Bereiche wie .upToNextMajor
Dokumentation und Projektverlauf prüfen:
- Sicherstellen, dass die README klar beschreibt, wie das Projekt auf einer sauberen Maschine eingerichtet, gebaut, ausgeführt und getestet wird
- Neuere Commits überprüfen, um zu verstehen, was sich geändert hat und warum, bevor der Code selbst überprüft wird
- Verifizieren, dass alle offenen Pull Requests auf den neuesten Main-Branch rebasiert sind
Code-Organisation und Architektur
Die Struktur eines iOS-Projekts sagt viel über das Team aus, das es gebaut hat. Ein gut organisiertes Projekt ist einfacher zu erweitern, einfacher zu testen und weit günstiger zu pflegen, wenn sich Anforderungen ändern.
Ordnerstruktur und Modultrennung:
- Verifizieren, dass das Projekt einer konsistenten Ordnerstruktur folgt: separate Verzeichnisse für Models, Views, View-Models oder Controller, Services, Utilities und Ressourcen
- Bestätigen, dass die App ein definiertes Architekturmuster konsistent in der gesamten Codebasis verwendet; MVVM, MVC, VIPER oder Clean Architecture sind alles gültige Optionen, aber welches Muster auch immer gewählt wird, sollte einheitlich angewendet werden, nicht auf halbem Weg aufgegeben
- Sicherstellen, dass Geschäftslogik nicht in UIViewController-Unterklassen enthalten ist. View-Controller sollten nur Navigation und Nutzerinteraktion behandeln und Datenverarbeitung sowie Geschäftsregeln an dedizierte Service- oder View-Model-Schichten delegieren
Schlechte Praxis:
// ViewController doing too much
class OrderViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let request = NSFetchRequest<Order>(entityName: "Order")
request.predicate = NSPredicate(format: "status == %0", "pending")
let orders = CoreDataStack.shared.context.fetch(request)
let total = orders.reduce(0) { $0 + $1.price * $1.quantity * taxRate }
sendConfirmationEmail(to: currentUser.email, total: total)
}
}
Gute Praxis:
// ViewController delegates to a service
class OrderViewController: UIViewController {
private let orderService: OrderService
override func viewDidLoad() {
super.viewDidLoad()
Task {
let summary = await orderService.fetchPendingSummary()
updateUI(with: summary)
}
}
}
Namenskonventionen:
- camelCase für Variablen, Funktionen, Eigenschaften und globale Konstanten mit let verwenden; PascalCase für Typen, Protokolle und Enumerationen
- UPPER_SNAKE_CASE ist eine Objective-C- und C-Konvention und ist kein idiomatisches Swift – in neuem Swift-Code vermeiden
- Sicherstellen, dass Dateinamen dem darin enthaltenen Typ entsprechen: eine Datei namens UserProfileViewModel.swift sollte den Typ UserProfileViewModel und nichts anderes enthalten
- Einbuchstabige Variablennamen außerhalb sehr kurzer Closures vermeiden. Zum Beispiel ist user immer klarer als u, und completion ist immer klarer als c
Protokoll- und Extension-Verwendung:
- Verifizieren, dass Protokolle verwendet werden, um gemeinsames Verhalten zu definieren, statt Vererbungsketten. Tiefe Klassenhierarchien in iOS-Code sind schwerer zu testen und zu ändern
- Prüfen, dass Extensions verwendet werden, um Funktionalität in logische Gruppen zu organisieren, statt Dateien zu erstellen, die Tausende von Zeilen lang sind
- Bestätigen, dass Extension-Dateien beschreibend benannt sind, wie String+Validation.swift oder UIColor+BrandColors.swift, damit ihr Zweck sofort klar ist
Swift-Sprache und Code-Qualität
Swift gibt Ihnen viele Werkzeuge, um sicheren, ausdrucksstarken Code zu schreiben. Ein Code-Review ist der Ort, um zu bestätigen, dass diese Werkzeuge gut eingesetzt werden und nicht umgangen werden.
Optionals und erzwungenes Unwrapping:
- Jedes Vorkommen des Force-Unwrap-Operators (!) überprüfen und bestätigen, dass jedes gerechtfertigt ist; das erzwungene Unwrapping eines nil-Werts lässt die App sofort abstürzen, und die meisten Verwendungen können durch sicherere Alternativen ersetzt werden
- Prüfen, dass guard let und if let verwendet werden, um Optionals sicher zu unwrappen, und dass die Fehlerpfade den fehlenden Wert tatsächlich behandeln, statt nur still zurückzukehren
Schlechte Praxis:
let user = userRepository.findUser(by: id)! // crashes if not found
let name = user.profile!.displayName! // crashes on nil profile or name
Gute Praxis:
guard let user = userRepository.findUser(by: id) else {
logger.warning("User not found for id: (id)")
showErrorState()
return
}
let name = user.profile?.displayName ?? "Anonymous"
Fehlerbehandlung:
- Verifizieren, dass Funktionen, die scheitern können, Swifts throws-Mechanismus verwenden, statt nil zurückzugeben oder sich auf Optionals zu stützen, um Fehler zu signalisieren. Aufrufer verdienen es zu wissen, was schiefgelaufen ist
- Prüfen, dass alle try-Aufrufe in do-catch-Blöcken spezifische catch-Klauseln für bekannte Fehlertypen haben, da ein einzelner Catch-All-Block, der Fehler ignoriert, keine Fehlerbehandlung ist, sondern eher „Fehler-Verstecken“
- Bestätigen, dass Fehler mit genügend Kontext protokolliert werden, damit ein Entwickler verstehen kann, was passiert ist, welche Eingabe es ausgelöst hat und wo in der Codebasis der Fehler aufgetreten ist
Werttypen vs. Referenztypen:
- Die Verwendung von struct vs. class überprüfen; Structs werden kopiert, was sie in nebenläufigem Code sicherer und einfacher zu verstehen macht; Klassen verwenden, wenn explizit Referenzsemantik oder Unterklassierung benötigt wird
- Prüfen, dass Structs Equatable und Hashable konformieren, wo diese Konformitäten nützlich sind, z. B. wenn der Typ in Sets gespeichert oder als Dictionary-Schlüssel verwendet wird
- Verifizieren, dass große Structs nicht durch Performance-sensitive Code-Pfade geleitet werden, wo Kopieren teuer wäre; zuerst profilieren, dann optimieren
Nebenläufigkeit:
- Bestätigen, dass die Codebasis Swifts modernes Nebenläufigkeitsmodell (async/await, actor, Task) verwendet, statt einer Mischung aus DispatchQueue, OperationQueue und Completion-Handlern, die unvorhersehbare Nebeneffekte produzieren
- Prüfen, dass alle UI-Updates im Haupt-Thread stattfinden; das Aktualisieren eines UILabel oder das Aufrufen von tableView.reloadData() aus einem Hintergrund-Thread erzeugt Abstürze, die intermittierend und notorisch schwer zu reproduzieren sind
Schlechte Praxis:
URLSession.shared.dataTask(with: url) { data, _, _ in
self.titleLabel.text = parseTitle(from: data) // UI update on background thread
}.resume()
Gute Praxis:
Task {
let title = try await fetchTitle(from: url) // runs off main thread
await MainActor.run {
self.titleLabel.text = title // UI update on main thread
}
}
- Die Verwendung von @MainActor überprüfen, um sicherzustellen, dass View-Models und UI-bezogener Code korrekt zum Haupt-Thread isoliert sind
Swift-6-Nebenläufigkeitsbereitschaft:
Swift 6 führt strenge Data-Race-Überprüfung ein. Projekte sollten jetzt auf Bereitschaft auditiert werden, auch wenn die Migration schrittweise erfolgt.
- Die SWIFT_STRICT_CONCURRENCY-Build-Einstellung des Projekts überprüfen. Ein Wert von complete aktiviert Swift-6-Level-Data-Race-Überprüfung und ist der Zielzustand für allen neuen Code. Projekte, die noch auf minimal oder targeted sind, sollten einen dokumentierten Migrationsplan haben
- Alle Typen, die über Nebenläufigkeitsgrenzen übergeben werden, auf Sendable-Konformität überprüfen. Der Compiler markiert Nicht-Sendable-Typen im strikten Modus, aber im lenient-Modus sind dies stille Data-Races, die unter Last auftreten
- Verwendungen von Task.detached auditieren. Im Gegensatz zu strukturierten Task { } erben detached Tasks den Actor-Kontext oder Task-lokale Werte des Aufrufers nicht. Jeder Task.detached-Aufruf sollte einen Kommentar haben, der erklärt, warum er absichtlich unstrukturiert ist
Deprecation-Audit:
Deprecation-Warnungen häufen sich im Laufe der Zeit an und blockieren die zukünftige SDK-Einführung. Es ist daher unbedingt erforderlich, diese Probleme zu beheben, bevor sie Ihren App-Entwicklungsfortschritt beeinträchtigen.
- Einen Build mit -warn-deprecated-declarations ausführen und alle Warnungen vor der Einreichung beheben
- Nach Verwendung von UIWebView (seit 2023 vollständig aus dem App Store verbannt), OpenGL ES (veraltet) und Objective-C-Bridging-Mustern suchen, die moderne Swift-Äquivalente haben
- Verifizieren, dass das minimale Deployment-Target absichtlich gesetzt ist und nicht auf einer alten iOS-Version belassen wurde, die die Verwendung veralteter APIs erzwingt
Speicherverwaltung
Speicherverwaltungsprobleme sind die häufigste Ursache für Produktionsabstürze in iOS-Apps. Swift verwendet Automatic Reference Counting (ARC) zur Speicherverwaltung, aber ARC erfordert von Entwicklern immer noch bewusste Entscheidungen darüber, wie Objekte aufeinander verweisen.
Retain-Zyklen:
- Nach Closures suchen, die self stark erfassen. Dies ist eine der häufigsten Quellen von Speicherlecks in iOS-Apps, insbesondere in Netzwerk-Callbacks, Timern und Animationsblöcken
Schlechte Praxis:
// Strong capture of self creates a retain cycle
networkClient.fetchData { response in
self.processResponse(response) // self retains networkClient, networkClient retains self
}
Gute Praxis:
networkClient.fetchData { [weak self] response in
guard let self else { return }
self.processResponse(response)
}
- Prüfen, dass Delegate-Eigenschaften als weak deklariert sind, da eine starke Delegate-Referenz fast immer ein Retain-Zyklus ist, der darauf wartet zu entstehen
- Verwendungen von unowned überprüfen, um zu bestätigen, dass das referenzierte Objekt immer länger lebt als das referenzierende Objekt. Bei jedem Zweifel stattdessen weak verwenden, da eine hängende unowned-Referenz die App abstürzen lässt
Speicherleck-Erkennung:
- Bestätigen, dass das Team Xcodes Memory Graph Debugger und das Instruments-Leaks-Tool als Teil des Entwicklungs- und Review-Prozesses verwendet
- Verifizieren, dass deinit-Methoden in Schlüsselklassen implementiert sind und wie erwartet ausgeführt werden; eine Klasse, deren deinit nie aufgerufen wird, ist eine Klasse, die leckt
- Prüfen, dass NotificationCenter-Observer entfernt werden, wenn ein View-Controller oder Objekt freigegeben wird, da unfreigegebene Observer das Objekt am Leben erhalten und auf freigegebenen Targets feuern
Ressourcenverwaltung:
- Bild- und Asset-Laden überprüfen, um zu bestätigen, dass große Bilder nicht länger als nötig im Speicher gehalten werden. UIImage(named:) für häufig wiederverwendete Bilder und UIImage(contentsOfFile:) für große Bilder verwenden, die nicht gecacht werden sollen
- Verifizieren, dass aus dem Netzwerk oder von der Festplatte geladene Daten nicht unbegrenzt im Speicher gehalten werden, wenn sie in einem Festplatten-Cache gespeichert oder bei Bedarf abgerufen werden könnten
Netzwerk und Datenverarbeitung
Netzwerkcode ist der Ort, wo viele iOS-Apps technische Schulden anhäufen. Schlecht strukturierte Netzwerkschichten werden mit wachsender API-Oberfläche teuer in der Wartung.
Netzwerkschicht-Organisation:
- Bestätigen, dass alle Netzwerkanfragen in einer dedizierten Netzwerkschicht zentralisiert sind, statt über View-Controller und View-Models verteilt zu sein
- Verifizieren, dass API-Endpunkte, Basis-URLs und Authentifizierungs-Tokens nicht in einzelnen Dateien fest kodiert sind, da diese über Umgebungskonfiguration verwaltet werden sollten
- Prüfen, dass Netzwerkanfragen URLSession oder eine gut gepflegte Bibliothek wie Alamofire mit einer konsistenten Fehlerbehandlungsstrategie verwenden
Antwort-Parsing und Validierung:
- Die Verwendung von Codable für JSON-Parsing überprüfen, da Models elegant dekodieren und fehlende oder unerwartete Felder ohne Abstürzen behandeln sollten
- Bestätigen, dass Server-Antworten vor der Verwendung validiert werden, und niemals annehmen, dass ein als optional markiertes Feld in der API-Dokumentation in der Praxis tatsächlich optional ist
- Prüfen, dass Paginierung, Timeout-Behandlung und Retry-Logik für alle Anfragen implementiert sind, die instabile Netzwerkbedingungen erleben könnten
SwiftData und Core Data:
Derzeit ist die empfohlene Persistenzschicht für Apps, die auf iOS 17 und später abzielen, SwiftData. Core Data wird jedoch für Apps, die es verwenden, weiterhin vollständig unterstützt. Sie haben daher die Freiheit, die Option zu wählen, die am besten zu Ihrem Fall passt, solange Sie es sicher tun.
- Wenn die App auf iOS 17 oder später abzielt, prüfen, ob SwiftData verwendet oder geplant ist. SwiftDatas @Model-Makro ersetzt NSManagedObject und integriert sich nativ mit Swift-Nebenläufigkeit
- Für Projekte mit Core Data bestätigen, dass Fetch-Requests NSFetchRequest mit parametrisierten NSPredicate-Format-Strings verwenden – niemals String-Interpolation – um Predicate-Injection zu verhindern
- Verifizieren, dass Core-Data-Managed-Object-Kontexte auf dem richtigen Thread verwendet werden. NSManagedObjectContext ist nicht thread-sicher; performAndWait oder Hintergrundkontexte für Off-Main-Thread-Arbeit verwenden
- Prüfen, dass SwiftData- oder Core-Data-Migrationsstrategien vor jedem Release, das das Modell ändert, dokumentiert und getestet sind
Sensible Daten in Übertragung:
- Bestätigen, dass App Transport Security (ATS) aktiviert ist und dass keine Ausnahmen ohne dokumentierten Grund zu Info.plist hinzugefügt wurden. ATS-Ausnahmen erlauben einfache HTTP-Verbindungen und sind ein direkter Weg zur Daten-Abfangung
- Für hochsichere Anwendungen wie Banking- oder Gesundheits-Apps in Betracht ziehen, SSL-Pinning zu implementieren, um sicherzustellen, dass die App nur mit vertrauenswürdigen Servern kommuniziert, selbst wenn eine Zertifizierungsstelle kompromittiert ist. Public-Key-Pinning (SPKI) über vollständiges Zertifikat-Pinning bevorzugen, da das Pinnen des vollständigen Zertifikats bricht, wenn der Server sein Zertifikat rotiert und ein Notfall-App-Update erfordert. Für die meisten Apps bietet ATS kombiniert mit modernem TLS ausreichenden Schutz ohne das Betriebsrisiko
Sicherheits-Best-Practices
iOS hat starke eingebaute Sicherheitsfähigkeiten, was ein deutlicher Vorteil ist. Sie helfen jedoch nur, wenn Sie sie korrekt implementieren. Apples Richtlinien stellen Datenschutz und Sicherheit an die Spitze ihrer Review-Kriterien. Den Beweis dafür sehen Sie im App-Store-Transparenzbericht 2024, der bestätigt, dass Datenschutzverletzungen zu den führenden Gründen für Ablehnung und Entfernung gehörten.
Keychain und sicherer Speicher:
- Bestätigen, dass sensible Daten wie Authentifizierungs-Tokens, Passwörter und kryptografische Schlüssel im iOS-Keychain gespeichert sind, nicht in UserDefaults, einfachen Dateien oder NSUserDefaults. Keychain-Elemente sind verschlüsselt und an die Identität der App gebunden
- Verifizieren, dass Keychain-Elemente geeignete Zugänglichkeitseinstellungen verwenden: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ist für die meisten sensiblen Daten geeignet und verhindert iCloud-Backup und Migration auf neue Geräte
- Jede Verwendung des Secure Enclave für kryptografische Operationen auf unterstützenden Geräten überprüfen. Secure Enclave führt kryptografische Arbeit in Hardware durch, ohne den Schlüssel jemals dem App-Speicher preiszugeben
Datenschutz:
- Bestätigen, dass Dateien mit sensiblen Daten mit der NSFileProtectionComplete-Datenschutzklasse erstellt werden, die sie verschlüsselt, wenn das Gerät gesperrt ist
- Verifizieren, dass sensible Daten so schnell wie möglich aus dem Speicher gelöscht werden, wenn sie nicht mehr benötigt werden; nach der Verwendung eines Passworts oder Tokens die Variable überschreiben und auf nil setzen, statt sie bis zur nächsten Garbage Collection im Speicher zu belassen
Eingabevalidierung und Injektionsprevention:
- Alle Punkte überprüfen, an denen vom Nutzer bereitgestellte Eingaben eine Datenbankabfrage, einen Dateipfad oder eine URL-Konstruktion erreichen, da dies potenzielle Injektionspunkte sind und vor der Verwendung validiert und bereinigt werden müssen
- Bestätigen, dass Web-Views kein JavaScript aus nicht vertrauenswürdigen Quellen ausführen. Wenn ein WKWebView externe Inhalte lädt, JavaScript deaktivieren, es sei denn, es ist erforderlich
Authentifizierung und Session-Management:
- Verifizieren, dass die App Sign in with Apple oder einen sicheren OAuth-2.0-Flow verwendet, statt eine benutzerdefinierte Authentifizierungsimplementierung zu erstellen
- Prüfen, dass Session-Tokens angemessen erneuert werden und dass abgelaufene Tokens aus dem Speicher gelöscht werden. Ein Token, das vor sechs Monaten gültig war, sollte heute keinen Zugriff mehr gewähren
- Bestätigen, dass biometrische Authentifizierung (Face ID, Touch ID) über LocalAuthentication mit einem Fallback auf Geräte-Passcode verwendet wird, nicht als Ersatz für serverseitige Authentifizierung
Performance-Optimierung
iOS-Nutzer erwarten, dass ihre Apps sofort reagieren. Daher ist eine langsame App eine gelöschte App. Die gute Nachricht ist, dass die meisten Performance-Probleme aus spezifischen, identifizierbaren Mustern entstehen, die ein Code-Review erkennen kann, bevor sie Nutzer erreichen.
Haupt-Thread-Performance:
- Jede Datei-I/O, Datenbanklesevorgänge, Netzwerkaufrufe oder schwere Berechnungen identifizieren, die im Haupt-Thread laufen. All diese sollten in Hintergrund-Threads verschoben werden, und die Ergebnisse zurück zum Haupt-Thread für UI-Updates gesendet werden
- Prüfen, dass viewDidLoad und viewWillAppear keine teuren synchronen Arbeiten enthalten. Diese Methoden laufen im Haupt-Thread, und jede Verzögerung blockiert direkt die UI
Tabellen- und Sammlungsansicht-Optimierung:
- Verifizieren, dass UITableView- und UICollectionView-Zell-Wiederverwendung korrekt implementiert ist und dass Zellen nach dem Enqueue keinen Zustand von vorherigen Elementen beibehalten
- Prüfen, dass in Zellen angezeigte Bilder asynchron geladen werden und dass ein Platzhalter angezeigt wird, während das Bild geladen wird, da synchrones Bildladen in einer Scroll-View sichtbares Stottern verursacht
- Bestätigen, dass Zellhöhen effizient berechnet werden; das Aufrufen von systemLayoutSizeFitting für jede Zelle in tableView(_:heightForRowAt:) ist eine häufige Ursache für Scroll-Jitter
Startzeit und Startup-Performance:
- Die Startsequenz der App überprüfen und bestätigen, dass nur wesentliche Arbeit stattfindet, bevor der erste Bildschirm erscheint. Verzögertes Laden und Lazy Initialization sollten für alles verwendet werden, was beim Start nicht benötigt wird
- Prüfen, dass große Assets und Ressourcen beim App-Start nicht synchron geladen werden, und Hintergrundwarteschlangen verwenden und einen Ladezustand anzeigen, wenn nötig
- Verifizieren, dass die App mit Xcodes Time-Profiler-Instrument profiliert wurde, da das Identifizieren der tatsächlichen Engpässe immer produktiver ist als zu raten
Batterie- und Ressourceneffizienz:
- Hintergrundaufgaben-Verwendung überprüfen und bestätigen, dass die in den Entitlements der App deklarierten Hintergrundmodi tatsächlich benötigt werden. Unnötige Hintergrundmodi entleeren den Akku und laden zu genauerer App-Store-Review-Prüfung ein
- Prüfen, dass Standortaktualisierungen die niedrigste Genauigkeitsstufe verwenden, die das Feature erfordert; kCLLocationAccuracyBest für ein Feature anzufordern, das nur die Stadt des Nutzers benötigt, ist eine erhebliche Akku-Entleerung
Xcode-Instrumente
- Time Profiler – CPU-Hotspots im Haupt-Thread identifizieren. Nach Arbeit suchen, die 16 ms übersteigt und einen Frame blockieren würde
- Allocations – Heap-Wachstum über eine typische Nutzersitzung verfolgen. Ein Diagramm, das sich bei normaler Nutzung nie einpegelt, signalisiert ein Leck oder einen unbegrenzten Cache
- Leaks – automatisierte Retain-Zyklus-Erkennung. Nach jedem wichtigen Feature ausführen, nicht nur vor der Einreichung
- Memory Graph Debugger – in Xcode integriert, zeigt den Live-Objektgraphen. Der schnellste Weg, zu beweisen, dass ein Retain-Zyklus existiert, und genau zu verfolgen, welche Referenz ihn hält
- Hangs – erkennt Haupt-Thread-Hänger über einem Schwellenwert. Apple verwendet dieselben Daten für die Hang-Rate-Metrik, die im Xcode-Organizer sichtbar ist
- Energy Log / CPU Usage – Akku-Auswirkungen von Hintergrundarbeit, Standortaktualisierungen und Bluetooth messen. Apps, die den Akku entleeren, werden gelöscht und schlecht bewertet
- Network-Instrument – schlechte Verbindungen (Edge, 3G, Paketverlust) simulieren, um Timeout-Behandlungslücken abzufangen, die nur in der echten Welt auftreten
- Core-Data-Template – Fetch-Request-Performance, Fault-Firing-Muster und Persistent-Store-I/O für Apps mit Core Data untersuchen
- SwiftUI-Instrument – Body-Re-Render-Anzahl pro View. Ein unsichtbarer Performance-Killer in SwiftUI-Apps, wo die View-Identität falsch konfiguriert ist
App-Größe und Binäranalyse
- Die unkomprimierte Binärgröße der App überprüfen und bestätigen, dass sie unter Apples Mobilfunk-Download-Warnungsschwelle bleibt (derzeit 200 MB)
- Xcodes App-Size-Report, der beim Archivieren generiert wird, verwenden, um zu identifizieren, welche Frameworks, Assets und Ressourcen am meisten zur Binärgröße beitragen
- Prüfen, dass ungenutzte Assets nicht gebundelt sind
- Verifizieren, dass große Medien-Assets On-Demand Resources verwenden, wenn sie beim ersten Start nicht benötigt werden
Build-Konfiguration und Code-Signierung
Build-Konfigurationsprobleme sind eine häufige Quelle von Produktionsfehlern und Ablehnungen aus dem App Store. Sie werden in Code-Reviews selten behandelt, sind aber konsistent schmerzhaft, wenn sie übersehen werden.
Debug- vs. Release-Build-Konfigurationen
- Bestätigen, dass Debug- und Release-Build-Konfigurationen sich bedeutsam unterscheiden: Assertionen in Debug aktiviert, in Release entfernt; Debug-Logging in Release deaktiviert
- Prüfen, dass das DEBUG-Flag konsistent verwendet wird und dass keine Debug-only-Code-Pfade – Testkonten, Logging-Endpunkte, Feature-Flags – ein Produktions-Binär erreichen können
- Verifizieren, dass Compiler-Optimierungen korrekt gesetzt sind: None [-O0] für Debug (schnellere Builds, besserer Debugger) und Optimize for Speed [-O] oder Optimize for Size [-Oz] für Release
- Prüfen, dass Dead-Code-Stripping für Release-Builds aktiviert ist, um die Binärgröße zu reduzieren und unerreichbare Code-Pfade zu entfernen
Code-Signierung und Provisioning
- Bestätigen, dass Provisioning-Profile über automatisches Signieren in Xcode oder ein Tool wie Fastlane Match verwaltet werden, nicht manuell heruntergeladene Profile, die ohne Warnung ablaufen und CI-Pipelines brechen
- Verifizieren, dass Entitlements in der .entitlements-Datei genau übereinstimmen, was in der App-Store-Connect-Capability-Konfiguration aktiviert ist. Abweichungen verursachen Archivierungsfehler oder stilles Capability-Versagen zur Laufzeit
- Prüfen, dass Push-Notification-Zertifikate oder Authentifizierungsschlüssel vor dem Ablauf rotiert werden und dass das Team einen dokumentierten Erneuerungsprozess hat
Tests und Code-Abdeckung
Tests sind der schriftliche Vertrag zwischen Ihrem Code und dem Verhalten, das er verspricht. Daher ist umfassendes Testen ein obligatorischer Schritt, wenn Sie möchten, dass Ihre App vom App Store genehmigt wird und eine Chance hat, Nutzer anzuziehen. Zudem trägt ohne wiederkehrende Tests jede Änderung versteckte Risiken.
Unit- und Integrationstests:
- Verifizieren, dass alle Geschäftslogik, Datentransformationsfunktionen und Service-Klassen Unit-Tests haben, die den Happy Path, Edge Cases und Fehlerbedingungen abdecken
- Bestätigen, dass Tests Dependency Injection oder protokollbasierte Abstraktionen verwenden, damit echte Netzwerkaufrufe, Datenbanken und Hardware durch Test-Doubles ersetzt werden
- Prüfen, dass Integrationstests kritische Nutzer-Flows von Anfang bis Ende abdecken, insbesondere Authentifizierung, Zahlungsabwicklung und Datenpersistenz
UI-Tests:
- Bestätigen, dass kritische Nutzer-Flows UI-Tests haben, die gegen echte oder simulierte Gerätezustände laufen, nicht nur Unit-Level-Assertionen
- Verifizieren, dass UI-Tests stabile, eindeutige Barrierefreiheits-Bezeichner verwenden statt positionsbasierter Abfragen wie „der dritte Button im zweiten Abschnitt“, die bei jeder Layout-Änderung brechen
Testqualität:
- Testnamen überprüfen, um zu bestätigen, dass sie das erwartete Verhalten beschreiben, zum Beispiel test_login_withExpiredToken_shouldRedirectToLoginScreen() statt testLogin()
- Bestätigen, dass keine Tests mit .skip oder XCTSkip permanent deaktiviert sind ohne einen dokumentierten und verfolgten Grund für den Ausschluss
- Verifizieren, dass die CI-Pipeline die vollständige Test-Suite bei jedem Pull Request ausführt und Merges bei Testfehlern blockiert
Absturz-Berichterstellung und Observability
Tests sagen Ihnen, was der Code in einer kontrollierten Umgebung tut. Sie benötigen jedoch Absturz-Berichterstellung und Observability, um zu erfahren, was er in der echten Welt, auf echten Geräten und unter echten Bedingungen tut.
Absturz-Berichterstellung:
- Bestätigen, dass ein Absturz-Berichterstellungs-SDK integriert ist (Firebase Crashlytics, Sentry oder ähnliches) und dass dSYM-Dateien automatisch für jeden Release-Build hochgeladen werden, damit Abstürze symbolisiert und lesbar sind
- Verifizieren, dass das Absturz-Berichterstellungs-SDK selbst keine Datenschutz-Manifest-Verletzungen einführt. Mehrere populäre SDKs haben in neueren Versionen erforderliche Reason APIs hinzugefügt, die in PrivacyInfo.xcprivacy deklariert werden müssen
- Prüfen, dass nicht-fatale Fehler und unerwartete Zustände als nicht-fatale Ereignisse protokolliert werden, nicht nur Abstürze. Ein stilles Versagen ist schwerer zu debuggen als ein Absturz
Xcode-Organizer und App-Store-Connect-Metriken:
- Die Absturzberichte der App im Xcode-Organizer vor jedem Review einer Live-App überprüfen. Diese aggregieren symbolisierte Absturzprotokolle von echten Nutzern und sind der schnellste Weg, um Top-Probleme in der Produktion zu identifizieren
- Die Hang-Rate und Scroll-Hitch-Rate der App im Organizer überprüfen. Apple markiert Apps in den schlechtesten 25 % ihrer Kategorie, und diese Metriken fließen direkt in die App-Store-Sichtbarkeit ein
- Disk-Writes-Diagnostik überprüfen. Exzessives Festplatten-I/O ist ein häufiges Problem in Core-Data-Apps, die bei jedem Tastendruck speichern
- Das Batterieverbrauch-Perzentil relativ zu Peer-Apps überprüfen. Dies ist die Grundlage für jedes Energie-Audit und ist verfügbar, ohne Instruments auszuführen
App-Store-Compliance und Datenschutz
Apple hat 2024 7,77 Millionen Einreichungen überprüft, und fast zwei Millionen davon wurden abgelehnt. Die Teams, die beim ersten Einreichen bestehen, behandeln die App-Store-Richtlinien als Engineering-Anforderungen, nicht als Nachgedanken.
Datenschutz-Manifest und Datenerklärungen:
- Bestätigen, dass eine Datenschutz-Manifest-Datei (PrivacyInfo.xcprivacy) existiert und alle verwendeten APIs, gesammelten Daten und die Gründe für ihre Verwendung genau deklariert. Apple erzwingt dies für alle in der App gebundelten Drittanbieter-SDKs sowie die App selbst
- Verifizieren, dass das App-Store-Connect-Datenschutz-Nutrition-Label das tatsächliche Datenerhebungsverhalten der App genau widerspiegelt, da Inkonsistenzen zwischen deklarierten und tatsächlichen Verhalten eine führende Ursache für Entfernung sind
- Alle Aufrufe von datenschutzsensiblen APIs überprüfen: Standort, Kamera, Mikrofon, Kontakte, Fotos, Gesundheit und Tracking. Jeder muss eine klare, nutzerorientierte Zweckzeichenkette in Info.plist haben, die in einfacher Sprache erklärt, warum die Berechtigung benötigt wird
Zeitpunkt der Berechtigungsanforderung:
- Den Zeitpunkt aller Berechtigungsanforderungsaufrufe überprüfen. Jede Berechtigung sollte in dem Moment angefordert werden, in dem der Nutzer eine Aktion ausführt, die sie erfordert, nicht beim Start
- Eine kurze In-App-Erklärung unmittelbar vor dem System-Prompt anzeigen, damit der Nutzer versteht, warum die Berechtigung benötigt wird, bevor der System-Dialog erscheint
- Eine Verwendungsbeschreibungszeichenkette in Info.plist ist erforderlich, ist aber kein Ersatz für gutes Anfragerequesting-Timing
In-App-Käufe und Entitlements:
- Bestätigen, dass alle digitalen Inhalte oder Features, die innerhalb der App freigeschaltet werden, Apples In-App-Purchase-System verwenden. Das Umgehen mit externen Zahlungslinks ist eine Richtlinienverletzung, die zu Ablehnung und potenzieller Entfernung führt
- Verifizieren, dass ein „Käufe wiederherstellen“-Button für Nutzer zugänglich ist und zuverlässig funktioniert, da Apple-Reviewer dies explizit testen
- Prüfen, dass in der App deklarierte Entitlements mit denen in der App-Store-Connect-Konfiguration aktivierten übereinstimmen, da Abweichungen zu Signierungsfehlern führen
Kontolöschung:
- Bestätigen, dass jede App, die Kontoerstellung anbietet, auch eine Möglichkeit bietet, dieses Konto direkt innerhalb der App zu löschen. Dies ist seit Juni 2023 eine harte Anforderung und wird bei jedem Review überprüft
Barrierefreiheit und Lokalisierung
Eine App, die von den meisten Menschen genutzt wird, wird von Anfang an mit Barrierefreiheit und Lokalisierung im Sinn gebaut, nicht als letzten Schritt vor der Auslieferung hinzugefügt.
Barrierefreiheit:
- Verifizieren, dass alle interaktiven Elemente aussagekräftige Barrierefreiheits-Labels haben. Ein Button mit einem Symbol und ohne Label ist für VoiceOver-Nutzer unsichtbar
- Prüfen, dass die App die Dynamic-Type-Einstellung des Systems respektiert und dass Text angemessen skaliert, ohne Layouts zu brechen
- Bestätigen, dass Farbe niemals das einzige Mittel zur Informationsübermittlung ist. Nutzer mit Farbenblindheit sollten alle Zustände und Nachrichten auch durch Labels oder Ikonographie verstehen können
- Verifizieren, dass minimale Touch-Zielgrößen Apples Human Interface Guidelines-Empfehlung von mindestens 44 mal 44 Punkten entsprechen
Lokalisierung:
- Bestätigen, dass alle nutzerorientierte Zeichenketten in NSLocalizedString eingebettet sind und keine direkt in den Quelldateien fest kodiert sind
- Die Localizable.strings-Datei auf Vollständigkeit überprüfen; fehlende Übersetzungen fallen still auf die Entwicklungssprache zurück und können verwirrende gemischtsprachige Bildschirme erzeugen
- Prüfen, dass Datums-, Zeit-, Währungs- und Zahlenformatierung locale-bewusste Formatter verwenden, statt fester Formatzeichenketten. Ein als „MM/DD/YYYY“ formatiertes Datum bedeutet in Europa etwas anderes als in den USA
Rechts-nach-links und Pseudo-Lokalisierung
- Verifizieren, dass die App Rechts-nach-links-Layouts korrekt für Märkte mit Arabisch, Hebräisch oder Persisch behandelt. NSTextAlignment.natural und leading/trailing Auto-Layout-Constraints statt left/right verwenden
- Prüfen, dass String-Formatierung String(format:locale:) statt nur String(format:) verwendet, wenn die Ausgabe nutzerorientiert ist
- Bestätigen, dass die App mit Pseudo-Lokalisierung getestet wurde, um abgeschnittene Labels und fest kodierte Layout-Annahmen abzufangen, bevor echte Übersetzungen geliefert werden. Xcode unterstützt dies nativ unter Scheme-Einstellungen
Warum iOS-Code-Review Redwerk anvertrauen
Die meisten iOS-Qualitätsprobleme sind keine rätselhaften Probleme, die aus dem Nichts erscheinen. Es sind dieselben Muster, die in verschiedenen Codebasen auftauchen:
- Retain-Zyklen, die niemand profiliert hat
- Keychain-Geheimnisse durch UserDefaults ersetzt
- UI-Updates, die außerhalb des Haupt-Threads ausgelöst werden
- App-Store-Einreichungen, die ohne Überprüfung des Datenschutz-Manifests gesendet wurden
Wir haben versucht, sie alle in dieser Checkliste abzudecken. Das Durcharbeiten davon bringt Sie jedoch nur so weit. Die eigentliche Frage ist, ob Ihr Team die Kapazität und den Abstand hat, es ehrlich durchzugehen, bevor die Einsätze hoch werden.
Ein Beispiel dafür, warum das wichtig ist, sehen Sie aus unserer eigenen Praxis. Schauen Sie sich Gooroo an, eine iOS-Nachhilfeplattform, die wir von Grund auf in Swift gebaut haben. Die App verband Tutoren und Schüler in New York und wurde offizieller Anbieter-Partner des NYC Department of Education. Sie erzielte auch eine 5-Sterne-Bewertung im App Store und belegte Platz drei auf Product Hunt.
Um dieses Niveau der Anerkennung zu erreichen, mussten wir jedoch sicherstellen, dass der Code auf jeder Schicht korrekt war:
- Saubere MVVM-Architektur
- Speicherverwaltung, die unter echter Nutzerlast standhielt
- Datenschutzhandhabung, die App-Store-Reviewer bei der ersten Einreichung überzeugte
- Test-Suite, die dem Team schnelle Iterationen ohne Regressionen ermöglichte
Dieses Ergebnis lässt sich nicht ohne vollständigen Einsatz und Liebe zum Detail erzielen. Es entsteht aus der Behandlung von Code-Qualität als Feature, nicht als eine Aufgabe, die bis nach dem Launch aufgeschoben wird.
Unser Team liefert und auditiert iOS-Anwendungen seit über 20 Jahren. Wir decken Architektur, Speicheranalyse, Sicherheits-Härtung, App-Store-Compliance und Test-Abdeckung ab. Sie erhalten einen Bericht, der spezifisch, priorisiert und sowohl für Entscheidungsträger als auch für Entwickler geschrieben ist. Wir liefern niemals vage Beobachtungen oder generische Empfehlungen.
Wenn Sie ein klares Bild davon möchten, wo Ihre iOS-Codebasis tatsächlich steht, ist unser Code-Review-Service der richtige Ausgangspunkt. Kontaktieren Sie uns über das, was Sie gebaut haben, und wir werden Ihnen genau mitteilen, was wir finden.
Sehen Sie echte Code-Review-Ergebnisse: 80+ identifizierte Verbesserungen und Sicherheitsrisiken für einen mobilen Marktplatz