GUIDE

Kom igång med IOS-utveckling, del 5

Nu när vi har en datakälla är det dags att sätta den i arbete. I del 5 av artikelserien visar 99mac hur man använder ett fristående framework och presenterar data i en UITableView.

I förra delen av vår #Kom igång-serie skrev vi en klient som hämtar data från ett publikt API. För att få den att fungera i appen behöver vi länka in vårt framework och berätta för UITableView hur den ska presentera data.

Länka in Framework

Det första vi behöver göra är att länka in vårt framework i huvudprojektet. Om du har organiserat både huvudprojektet och ditt framework i samma workspace så är det väldigt enkelt.

  1. Markera huvudprojektet i fillistan till vänster

  2. Välj "General"-fliken

  3. Klicka på plustecknet nere under "Linked Frameworks and libraries"

  4. Välj ditt framework i listan

  5. Lägg till ditt framework även under "Embedded binaries" på motsvarande sätt

Om du inte har dem i samma workspace så får du klicka på "Add other…" i steg 4 och 5 och leta reda på ditt framework i filsystemet istället. Utan steg 5 fungerar appen i simulatorn, men inte på en fysisk IOS-enhet.

Implementera protokoll

Nu är det dags att återgå till klasserna vi skapade i del 3, närmare bestämt SearchViewController. Det första vi behöver göra är att importera vårt inlänkade framework. Längst upp i SearchViewController.swift-filen finns redan en import av UIKit. Under den raden lägger vi till "import SLAPI".

Nu behöver vi implementera SLAPIDelegate-protokollet i SearchViewController så att API-klienten kan skicka tillbaka asynkrona sökresultat. Vi behöver även skapa en instansvariabel för API-klienten. Klassdefinitionen för SearchViewController ska nu se ut så här:

class SearchViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate, SLAPIDelegate {

    var api : SLAPI? = nil
	// Mer kod..
}

Om du inte gjorde protokollmetoderna optional så klagar #Xcode på att de inte är implementerade. Vi löser det genom att lägga in tomma definitioner av dem i SearchViewController:

func siteLookupComplete(sites: NSArray) {
        NSLog("Yay!")
    }
    
    func siteLookupFailed(error: NSError) {
        NSLog("Doh!")
    }
    
    func getDeparturesComplete(departures: NSDictionary) {
        // Not used
    }
    
    func getDeparturesFailed(error: NSError) {
        // Not used
    }

Anropa sökfunktionen

Innan vi kan använda API-klienten måste vi skapa ett SLAPI-objekt, eftersom variabeln sattes till nil ovan. I del 4 skrev vi en init-funktion som tar ett delegate-objekt som argument. Eftersom vi har implementerat delegate-protokollet i den anropande klassen (SearchViewController) så anger vi self som delegate.

SLAPI-objektet skapas med fördel i funktionen viewDidLoad.

override func viewDidLoad() {
	self.api = SLAPI(delegate: self)
	// Mer kod..
    }

Så, när ska vi anropa searchSite i SLAPI? Det gör vi lämpligtvis när användaren skriver något i sökrutan. I del 3 lade vi in en tom definition av delegate-funktionen updateSearchResultsForSearchController. Den anropas varje gång innehållet i sökfältet ändras, vilket är precis vad vi vill åt.

func updateSearchResultsForSearchController(searchController: UISearchController) {
        // Ta hand om söksträng
        self.api?.searchSite(searchController.searchBar.text)
    }

Här anropas searchSite med den textsträng som har skrivits in i sökfältet. Nu sker all asynkron magi via NSURLSession, och när all data är hämtad anropas vår delegate-funktion siteLookupComplete.

Fyll TableView med data

När siteLookupComplete-funktionen anropas får vi en NSArray med sökresultat som argument. I min implementation har jag en hjälpklass som heter SLSite, som i princip bara är en container för data för att bunta ihop det snyggt. Arrayen med sökresultat innehåller alltså SLSite-objekt, som i sin tur innehåller siteID och namn.

Vi behöver en mer bestående referens för sökresultatet, så vi skapar en ny instansvariabel av typen NSArray i SearchViewController.

var dataSource : NSArray = NSArray()

Nu kan vi spara undan sökresultaten i den nya variabeln och beordra den inbäddade UITableView att ladda om sin data. Som vanligt bör UI-uppdateringar ske i huvudtråden, så vi använder funktionen dispatch_async.

func siteLookupComplete(sites: NSArray) {
        NSLog("Yay!")
        dataSource = sites
        dispatch_async(dispatch_get_main_queue(), {
            self.tableView.reloadData()
            return
        })
    }

Men, än så länge vet inte vår UITableView var den ska hämta data när vi laddar om den. SearchViewController är dess delegate, men vi måste implementera delegate-funktionerna, närmare bestämt numberOfSectionsInTableView, numberOfRowsInSection och cellForRowAtIndexPath.

numberOfSectionsInTableView anger antalet sektioner i en UITableView. Man kan exempelvis ha en sektion för varje bokstav i en alfabetiskt sorterad lista. Men för vårt syfte här behövs bara en sektion, så den här funktionen blir enkel.

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // Return the number of sections.
        return 1
    }

numberOfRowsInSection anger hur många rader/objekt som finns i en given sektion. Eftersom vi bara har en sektion så returnerar vi bara antal objekt i datakällan.

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return the number of rows in the section.
        if section == 0 {
            return dataSource.count
        }
        return 0
    }

I cellForRowAtIndexPath sker den mest relevanta logiken. Denna funktion ska returnera en UITableViewCell för en given så kallad indexPath som anger vilken sektion och vilken rad som efterfrågas.

cellForRowAtIndexPath anropas en gång för varje synlig rad i tabellen. När man skrollar anropas den i takt med att nya rader blir synliga. Av prestandaskäl skapas ett antal UITableViewCells som sedan återanvänds i takt med att de försvinner ur bild. Till detta anges en "Reuse identifier" som definieras för varje sorts cell i Storyboards.

Funktionen dequeueReusableCellWithIdentifier hämtar en ledig cell, eller skapar en ny om inga lediga finns. Därefter kan vi konfigurera cellen genom att sätta dess textLabel till namnet på siten.

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("searchTableCell", forIndexPath: indexPath) as! UITableViewCell

        // Configure the cell...
        let site : SLSite = dataSource.objectAtIndex(indexPath.row) as! SLSite
        cell.textLabel?.text = site.getName()

        return cell
   }

Så! Nu ska vår lista fyllas med sökresultat om man skriver några tecken. I kommande delar ska jag bland annat visa hur man kan spara favoriter med hjälp av NSUserDefaults.

Flex 2 är en av Fitbits budgetmodeller och den första som är så vattentålig at det går att simma med den. 99mac beger sig till badhuset för att testa!

Med den modulära datorn Kano kan vem som helst lära sig att skruva ihop sin egen hårdvara för ett flertal olika användningsområden. Med Kano Code går det också att lära sig grundläggande programmering. 99mac har pratat med Tommy Säl, som arbetar på Kickstarter-succén.

Skjut dig fram genom rymden i den senaste delen av action-spelet Galaxy on Fire, som släpps idag till Iphone, Ipad och Ipod Touch.

Snart kommer det kanske gå att att hyra filmer i Itunes så kort som två veckor efter att filmerna haft premiär. Men är det tillräckligt för alla som vill se nyare film hemma?

Under julhelgen kan det lätt bli stressigt och det är mycket att göra, ett sätt att lugna ner hjärnan är att använda digitala att-göra-listor som stillar planeringsstressen.

Det vankas både tävling och specialpris runt kylaren Silent Loop när Inet och Be Quiet intar dagens lucka. Rulla igång rimfabriken och bege dig till jukalendern!