NEW: Join my free 100 Days of SwiftUI challenge today! >>

Der letzte Schliff: didFinishLaunchingWithOptions

Bevor dieses Projekt abgeschlossen ist, machen wir noch zwei Änderungen. Zuerst fügen wir einen weiteren Tab zum UITabBarController hinzu, der beliebte Petitionen anzeigen wird, und dann machen wir unseren Lade-Code ein bisschen widerstandsfähiger, indem wir Fehlermeldungen hinzufügen.

Wie ich bereits sagte, können wir den zweiten Tab nicht einfach in unser Storyboard setzen, da beide Tabs einen ViewController beinhalten und das würde erfordern, dass die View Controller im Storyboard dupliziert werden müssten. Du kannst das machen, wenn du wirklich willst - aber bitte tu es nicht - es ist ein Wartungs-Albtraum!

Stattdessen lassen wir unsere Storyboard-Konfiguration in Ruhe und erzeugen den zweiten View Controller mit Hilfe von Code. So etwas hast du bisher noch nicht gemacht, aber es ist nicht schwierig und wir haben bereits den ersten Schritt getan, wie du sehen wirst.

Öffne die Datei AppDelegate.swift. Diese gab es schon in all unseren Projekten, aber bisher mussten wir noch nicht damit arbeiten. Suche nach der Methode didFinishLaunchingWithOptions, die ziemlich weit oben in der Datei stehen sollte. Diese wird von iOS aufgerufen, wenn die App bereit zum Ausführen ist, und wir werden sie kapern, um einen zweiten ViewController in unsere Tab Bar einzufügen.

Da sollte bereits einiges an vorgegebenem Apple-Code drin stehen, aber wir werden noch etwas mehr hinzufügen, und zwar direkt oberhalb der Zeile return true:

if let tabBarController = window?.rootViewController as? UITabBarController {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: "NavController")
    vc.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
    tabBarController.viewControllers?.append(vc)
}

Jede Zeile davon ist neu, also lass uns etwas tiefer graben:

  • Unser Storyboard erzeugt automatisch ein Fenster (Window), in dem all unsere View Controller angezeigt werden. Dieses Fenster muss wissen, welcher sein initialer View Controller ist, und dieser wird als seine rootViewController-Eigenschaft gesetzt. Das wird alles vom Storyboard erledigt.
  • In der Vorlage Single View App ist der ViewController der Root View Controller, aber wir haben diesen in einen Navigation Controller eingebettet, und dann diesen in einen Tab Bar Controller eingebettet. Daher ist für uns der Root View Controller ein UITabBarController.
  • Wir müssen einen neuen ViewController händisch erzeugen, was bedeutet, zunächst eine Referenz auf unsere Datei Main.storyboard zu erhalten. Das geht mit Hilfe der Klasse UIStoryboard, wie gezeigt. Du brauchst kein Paket ("Bundle") anzugeben, denn nil heißt so viel wie "verwende das aktuelle App-Paket".
  • Wir erzeugen unseren View Controller mit Hilfe der Methode instantiateViewController(), in dem wir die Storyboard-ID des View Controllers übergeben, den wir haben wollen. Wir haben bereits unserem Navigation Controller die ID "NavController" gegeben, also übergeben wir diese.
  • Wir erzeugen ein UITabBarItem-Objekt für den neuen View Controller und geben ihm das Symbol "Top Rated" ("Am besten bewertet") und das Etikett ("Tag") 1. Dieses Etikett wird gleich noch wichtig werden.
  • Wir fügen den neuen View Controller zum Array viewControllers unseres Tab Bar Controllers hinzu, wodurch er in der Tab Bar auftauchen wird.

Der Code erzeugt also einen duplizierten ViewController, der in einem Navigation Controller verpackt ist, gibt ihm ein neues Tab Bar Item, um ihn vom existierenden Tab zu unterscheiden, und fügt ihn dann zur Liste sichtbarer Tabs hinzu. Das erlaubt uns, dieselbe Klasse für beide Tabs zu verwenden, ohne irgendetwas im Storyboard duplizieren zu müssen.

Der Grund, warum wir dem neuen UITabBarItem das Etikett 1 gegeben haben, ist, dass dies eine einfache Methode zum Identifizieren ist. Wie du dich erinnerst, enthalten beide Tabs einen ViewController, was bedeutet, dass der selbe Code ausgeführt wird. Hier bedeutet das, dass beide den gleichen JSON-Kanal runterladen werden, was die Benutzung von zwei Tabs sinnlos macht. Aber wenn du den String urlString in der Methode viewDidLoad() in ViewController.swift durch diesen hier ersetzt, wird es viel besser funktionieren:

let urlString: String

if navigationController?.tabBarItem.tag == 0 {
    urlString = "https://api.whitehouse.gov/v1/petitions.json?limit=100"
} else {
    urlString = "https://api.whitehouse.gov/v1/petitions.json?signatureCountFloor=10000&limit=100"
}

Damit wird der Code so angepasst, dass die erste Instanz von ViewController die ursprüngliche JSON lädt, und die zweite nur Petitionen mit mindestens 10.000 Unterschriften.

Das Projekt ist beinahe fertig, aber wir machen noch eine letzte Änderung. Unser momentaner Code zum Laden ist nicht besonders widerstandsfähig: wir haben zwar ein paar if-Ausdrücke, um zu prüfen, dass alles korrekt abläuft, aber keine else-Ausdrücke, die Fehlermeldungen anzeigen, wenn es ein Problem gibt. Das erledigen wir einfach, indem wir eine neue Methode showError() hinzufügen, die einen UIAlertController erzeugt, der eine allgemeine Fehlermeldung anzeigt:

func showError() {
    let ac = UIAlertController(title: "Loading error", message: "There was a problem loading the feed; please check your connection and try again.", preferredStyle: .alert)
    ac.addAction(UIAlertAction(title: "OK", style: .default))
    present(ac, animated: true)
}

Du kannst nun den Code zum Runterladen und Parsen von JSON so anpassen, dass er diese Fehler-Methode aufruft, wann immer eine Bedingung fehlschlägt, so wie hier:

if let url = URL(string: urlString) {
    if let data = try? Data(contentsOf: url) {
        parse(json: data)
        return
    } else {
        showError()
    }
} else {
    showError()
}

Alternativ könnten wir das auch noch ein bisschen mehr aufräumen, indem wir ein return nach dem Aufruf von parse() einfügen. Das bedeutet, dass die Methode beendet wird, nachdem das Parsen erreicht wurde, daher kommen wir nur ans Ende der Methode, wenn das Parsen nicht erreicht wurde und können den Fehler anzeigen. Probier also mal das hier:

if let url = URL(string: urlString) {
    if let data = try? Data(contentsOf: url) {
        parse(json: data)
        return
    }
}

showError()

Beide Vorgehensweisen sind vollkommen zulässig - nimm einfach die, die du bevorzugst.

Egal, für welche du dich entscheidest, die App zeigt nun Fehlermeldungen an, wenn es Probleme gibt, und wir sind fertig - gute Arbeit!

LEARN SWIFTUI FOR FREE I have a massive, free SwiftUI video collection on YouTube teaching you how to build complete apps with SwiftUI – check it out!

MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!