Kom igång med IOS-utveckling, del 3

Du kanske har en idé, och det kliar i fingrarna att förverkliga den. I del 3 av artikelserien påbörjar vi ett nytt projekt som i slutändan ska bli en färdig app.

Att komma igång med ett nytt projekt kan te sig lite överväldigande för den som inte är bekant med #Xcode och den terminologi som hör till. I den här och resterande delar av artikelserien kommer jag att redovisa de viktigaste stegen i #apputvecklingen.

Jag kommer även att visa hur man använder viktiga tekniker som delegation-mönstret, asynkrona HTTP-anrop, apptillägg, och mycket annat.

Om projektet

Jag ska utveckla en app som visar SL:s realtidsinformation för utvalda hållplatser i en widget i Idag-vyn i Notiscentret. I själva huvudappen ska man kunna välja ut ett antal stationer eller hållplatser som ska visas i widgeten.

Data kommer att hämtas från det API som finns tillgängligt via trafiklab.se.

Skapa nytt projekt

I mitt fall börjar jag med att skapa ett Workspace där jag kan samla flera projekt. Planen är att ha biblioteket för SL:s API som ett separat projekt, så ett Workspace gör det enklare att organisera det hela.

  1. Starta Xcode och välj File > New > Workspace och välj ett informativt namn och en plats att spara på. Det här namnet är bara för din skull och är inget som kommer att synas utåt.

  2. Välj File > New > Project för att skapa själva projektet.

  3. Välj en lämplig mall för ditt projekt. Olika mallar ger dig olika mycket så kallad boiler plate-kod, en grundplåt med kod som du kan använda rakt av eller modifiera utifrån dina behov. I mitt fall passar ”Master-Detail Application” bäst, så jag väljer den.

  4. Välj ett namn på projektet och en Organization Identifier. De här är viktiga om du planerar att publicera din app, då de avgör appens ”Bundle ID” som är publikt. Organization Name sätts med fördel till ditt eller ditt företags namn.

  5. Kryssa i ”Use Core Data” om du behöver behandla större datamängder i en bestående databas. För det här projektet behöver jag det inte, så jag lämnar den okryssad.

När du har skapat projektet ser du de filer som Xcode har skapat åt dig i rutan till vänster.

AppDelegate.swift – I den här filen definieras funktioner som anropas av själva operativsystemet, så att du kan anpassa appens beteende om exempelvis användaren avslutar appen eller om ett telefonsamtal kommer in. Enkla appar behöver sällan göra så mycket här.

MasterViewController.swift – Det här är är en så kallad View Controller. Den motsvarar Controller-delen i MVC-mönstret som jag tog upp i del 1. En View Controller styr beteendet hos en specifik View.

Just den här styr Master-listan som är den första att visas när man startar appen. Den har en grundplåt med kod för att behandla en UITableView, det vill säga det grafiska element som i princip alla listor i IOS-appar består av.

DetailViewController.swift – En till View Controller, som styr detaljvyn. Den här visas när man väljer ett element i Master-listan.

Main.storyboard – I en Storyboard kan du på ett grafiskt sätt definiera utseendet på olika views, och även flödet mellan dem. Här ser du en Navigation Controller, en Master Scene och en Detail Scene som är sammanlänkade med så kallade Segues (uttalas ”Segways”).

En Segue definierar bland annat hur innehållet ska presenteras när man växlar mellan View Controllers.

Testkör

Redan från början har vi en tillräcklig grundplåt för att köra appen utan att skriva en enda rad egen kod. Klicka på Play-knappen uppe till vänster för att starta upp appen i simulatorn.

Du kommer att märka att den inte gör så mycket i nuläget. Det går i princip bara att lägga till tidstämplar i huvudlistan och klicka på dem för att komma till detaljvyn. Men poängen här är att du redan har en struktur på en app som troligtvis ser bekant ut för alla IOS-användare.

Lägga till en View Controller

I Master-listan vill jag kunna lägga till hållplatser och stationer istället för tidstämplar. Då behöver jag först och främst en vy för att söka efter hållplatser.

  1. Välj File > New > File…

  2. Välj IOS > Source i listan till vänster och Cocoa Touch Class i rutan till höger.
    Döp filen till exempelvis SearchViewController och välj UITableViewController som subclass.

  3. Markera Main.storyboard och dra en Table View Controller från listan nere till höger till en tom del av storyboarden.

  4. Med den nya vyn markerad, gå till Identity Inspector i verktygsfältet till höger och välj vår nya SearchViewController som Custom Class.

Nu har vi en ny View Controller kopplad till en vy. Men för att komma åt den inuti appen måste vi skapa en segue och aktivera den när användaren trycker på plusknappen i appen.

  1. I vår storyboard, markera View Controllern som heter Master.

  2. Håll ner ctrl-tangenten medan du klickar och drar från Master till vår nya Search View Controller.

  3. Välj ”Present modally” i menyn som dyker upp.

  4. Markera den nya seguen och sätt en identifier på den i fliken ”Attributes inspector” i verktygsfältet till höger. Döp den till exempelvis showSearch.

Dags att skriva kod! I MasterViewController.swift finns en fördefinierad funktion som heter viewDidLoad(). Den anropas så snart en View Controller har skapats och laddats in i minnet. Här kan du se att plusknappen läggs till programmatiskt istället för i Storyboarden, och att den associeras till funktionen insertNewObject.

Ta bort den förskrivna koden i insertNewObject-funktionen och lägg till raden:

self.performSegueWithIdentifier("showSearch", sender: self)

Testkör appen. Nu ser vi att det ploppar upp en ny vy med en tom lista när man trycker på plusknappen. Men det går inte att göra något här, inte ens hoppa tillbaka till huvudvyn.

Lägg till funktionalitet i nya View Controllern

Först och främst behöver vi ett sökfält där användaren kan skriva in namnet på en hållplats att söka efter. Det löser vi med en UISearchController som vi lägger till i kod.

Lägg till en instansvariabel som heter resultSearchController i klassen SearchViewController. Vidare behöver SearchViewController implementera ett par delegate-protokoll för att kunna vara delegate åt vår UISearchController.

Nu ska klassdefinitionen se ut som följer:

class SearchViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController = UISearchController()

	// … massa mer kod …
}

I viewDidLoad initierar vi vår UISearchController med följande kod:

self.resultSearchController = ({
            let controller = UISearchController(searchResultsController: nil)
            controller.searchResultsUpdater = self
            controller.dimsBackgroundDuringPresentation = false
            controller.searchBar.sizeToFit()
            
            self.tableView.tableHeaderView = controller.searchBar
            
            return controller
        })()
self.resultSearchController.searchBar.delegate = self

Nu klagar Xcode på att SearchViewController-klassen inte implementerar alla obligatoriska funktioner i protokollen vi lade till. Det fixar vi genom att lägga till en tom definition av den enda obligatoriska funktionen i UISearchResultUpdating längst ner i SearchViewController-klassen:

func updateSearchResultsForSearchController(searchController: UISearchController) {
}

Om vi testkör appen nu ser vi att den mycket riktigt har lagt till ett sökfält i vår nya vy. Själva sökfunktionen med SL:s API får vänta till en senare del i serien, men nu vill vi åtminstone kunna avbryta och backa till första vyn.

Det åstadkommer vi enkelt genom att implementera en delegate-funktion i SearchViewController-klassen:

func searchBarCancelButtonClicked(searchBar: UISearchBar) {
        self.dismissViewControllerAnimated(true, completion: nil)
}

dismissViewControllerAnimated gör att vår nya sökvy avvisar sig själv och appen hoppar tillbaka till huvudvyn. Kalas!

En sak återstår för en fulländad användarupplevelse; vi vill att textmarkören ska hamna i sökfältet direkt när vyn dyker upp, så man slipper trycka där först. Sökfältet behöver bli så kallad ”First responder” genom att anropa funktionen becomeFirstResponder() på searchBar-objektet.

becomeFirstResponder behöver köras i huvudtråden för att fungera, så vi måste använda dispatch_async. Implementera viewDidAppear med följande kod i SearchViewController:

override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        dispatch_async(dispatch_get_main_queue(), {
            self.resultSearchController.searchBar.becomeFirstResponder()
            return
        })
    }

Avslutning

Även om appen inte gör så mycket än så har vi sett hur lite jobb det egentligen behövs för att få till en grundläggande struktur med navigation mellan olika vyer. I kommande artiklar ska jag visa hur man hämtar data från ett API på internet och presenterar den i appen.