Maak een weer-app met voorspelling - API-integratie

In het eerste artikel van deze serie hebben we de basis gelegd voor het project door het opzetten van het project en het creëren van de structuur van de applicatie. In dit artikel gebruiken we de AFNetworking-bibliotheek om te communiceren met de prognose-API.


Invoering

In de eerste aflevering van deze serie hebben we de basis gelegd voor onze weerapplicatie. Gebruikers kunnen hun huidige locatie toevoegen en schakelen tussen locaties. In deze zelfstudie gebruiken we de AFNetworking-bibliotheek om de Forecast API te vragen naar de weergegevens van de momenteel geselecteerde locatie.

Als je mee wilt, heb je een Forecast API-sleutel nodig. U kunt een API-sleutel verkrijgen door u bij Trend te registreren als ontwikkelaar. Registratie is gratis, dus ik moedig je aan om de weersvoorspelling voor Forecast uit te proberen. U vindt uw API-sleutel onderaan in uw dashboard (figuur 1).


Figuur 1: Uw API-sleutel verkrijgen

1. Subclassering AFHTTPClient

Zoals ik eerder in dit artikel schreef, zullen we de AFNetworking bibliotheek om te communiceren met de Forecast API. Er zijn verschillende opties bij het werken met AFNetworking, maar om onze applicatie toekomstbestendig te maken, zullen we kiezen voor de AFHTTPClient klasse. Deze klasse is bedoeld voor het gebruik van webservices, zoals de prognose-API. Hoewel we slechts één API-eindpunt gebruiken, is het nog steeds nuttig om gebruik te maken van de AFHTTPClient zoals je in een paar momenten zult leren.

Het wordt aanbevolen om een AFHTTPClient subklasse voor elke webservice. Omdat we in de vorige zelfstudie al AFNetworking aan ons project hebben toegevoegd, kunnen we meteen beginnen met subclasseren AFHTTPClient.

Stap 1: Maak de klas

Maak een nieuwe Objective-C-klasse, noem deze MTForecastClient, en maak er een subklasse van AFHTTPClient (Figuur 2).

Figuur 2: Subklasse van AFHTTPClient

Stap 2: Een Singleton-object maken

We zullen het singleton-patroon gebruiken om het gemakkelijker te maken om het te gebruiken MTForecastClient klasse in ons project. Dit betekent dat slechts één instantie van de klasse op elk moment leeft gedurende de levensduur van de toepassing. De kans is groot dat je al bekend bent met singleton-patronen, omdat dit een veel voorkomend patroon is in veel objectgerichte programmeertalen. Op het eerste gezicht lijkt het singleton-patroon erg handig, maar er zijn een aantal kanttekeningen waar je op moet letten. Je kunt meer over singletons leren door dit uitstekende artikel van Matt Gallagher te lezen.

Het creëren van een singleton-object is vrij eenvoudig in Objective-C. Begin met het declareren van een klassemethode in MTForecastClient.h om openbare toegang tot het singleton-object te bieden (zie hieronder).

 #import "AFHTTPClient.h" @interface MTForecastClient: AFHTTPClient #pragma-markering - #pragma-markering Shared Client + (MTForecastClient *) sharedClient; @einde

De implementatie van sharedClient kan aanvankelijk ontmoedigend lijken, maar het is niet zo moeilijk als je eenmaal begrijpt wat er aan de hand is. We verklaren eerst twee statische variabelen, (1) gezegde van type dispatch_once_t en (2) _sharedClient van type MTForecastClient. Zoals de naam al aangeeft, gezegde is een predikaat dat we gebruiken in combinatie met de dispatch_once functie. Bij het werken met een variabele van het type dispatch_once_t, het is belangrijk dat het statisch wordt verklaard. De tweede variabele, _sharedClient, zal een verwijzing naar het singleton-object opslaan.

De dispatch_once functie neemt een pointer naar a dispatch_once_t structuur, het predicaat en een blok. De schoonheid van dispatch_once is dat het het blok een keer zal uitvoeren gedurende de levensduur van de applicatie, wat precies is wat we willen. De dispatch_once functie heeft niet veel functies, maar dit is zeker een van hen. In het blok waar we doorheen gaan dispatch_once, we maken het singleton-object en slaan hierin een referentie op _sharedClient. Het is veiliger om aan te roepen alloc en in het afzonderlijk om een ​​race-situatie te voorkomen die mogelijk tot een impasse leidt. Euh ... wat? U kunt meer lezen over de gedetailleerde informatie over Stack Overflow.

 + (MTForecastClient *) sharedClient static dispatch_once_t predicate; static MTForecastClient * _sharedClient = nihil; dispatch_once (& predicate, ^ _sharedClient = [self allocation]; _sharedClient = [_sharedClient initWithBaseURL: [self baseURL]];); return _sharedClient; 

Het belangrijkste om te begrijpen over de implementatie van de sharedClient klasse methode is dat de initializer, initWithBaseURL:, wordt slechts één keer aangeroepen. Het singleton-object wordt opgeslagen in de _sharedClient statische variabele, die wordt geretourneerd door de sharedClient klassemethode.

Stap 3: Configuratie van de client

In sharedClient, we roepen aan initWithBaseURL:, die op zijn beurt roept baseURL, een andere klassemethode. In initWithBaseURL:, we stellen een standaard header in, wat betekent dat de client deze header toevoegt aan elk verzoek dat wordt verzonden. Dit is een van de voordelen van het werken met de AFHTTPClient klasse. In initWithBaseURL:, we registreren ook een HTTP-bewerkingsklasse door aan te roepen registerHTTPOperationClass:. De AFNetworking-bibliotheek biedt een aantal gespecialiseerde operatieklassen. Een van deze klassen is de AFJSONRequestOperation klasse, wat de interactie met een JSON API erg gemakkelijk maakt. Omdat de prognose-API een JSON-antwoord retourneert, is de AFJSONRequestOperation klas is een goede keuze. De registerHTTPOperationClass: methode werkt vergelijkbaar met hoe de registerClass: forCellReuseIdentifier: van de UITableView klasse werkt. Door de klant te vertellen welke operatieklasse we willen gebruiken voor de interactie met de webservice, worden instanties van die klasse voor ons onder de motorkap geplaatst. Waarom dit nuttig is zal in enkele ogenblikken duidelijk worden.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Accept HTTP-header [self setDefaultHeader: @ "Accepteer" waarde: @ "application / json"]; // HTTP-bedieningsklasse registreren [zelfregisterHTTPOperationClass: [AFJSONRequestOperation class]];  terugkeer zelf; 

De implementatie van baseURL is niets meer dan een gemakkelijke methode voor het samenstellen van de basis-URL van de klant. De basis-URL is de URL die de client gebruikt om de webservice te bereiken. Het is de URL zonder namen of parameters van de methode. De basis-URL voor de Forecast API is https://api.forecast.io/forecast//. De API-sleutel maakt deel uit van de URL zoals u kunt zien. Dit lijkt misschien onveilig en dat is het ook. Het is niet moeilijk voor iemand om de API-sleutel te bemachtigen, dus is het raadzaam om met een proxy te werken om de API-sleutel te maskeren. Omdat deze benadering een beetje meer betrokken is, zal ik dit aspect in deze serie niet behandelen.

 + (NSURL *) baseURL return [NSURL URLWithString: [NSString stringWithFormat: @ "https://api.forecast.io/forecast/%@/", MTForecastAPIKey]]; 

Je hebt misschien gemerkt in de implementatie van baseURL dat ik een andere reeksconstante heb gebruikt voor het opslaan van de API-sleutel. Dit lijkt misschien niet nodig omdat we de API-sleutel alleen op één locatie gebruiken. Het is echter een goede gewoonte om toepassingsgegevens op te slaan op één locatie of in een eigenschappenlijst.

 #pragma mark - #pragma mark Voorspelling API extern NSString * const MTForecastAPIKey;
 #pragma mark - #pragma mark Forecast API NSString * const MTForecastAPIKey = @ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

Stap 4: Een helpermethode toevoegen

Voordat we verder gaan, wil ik de MTForecastClient class door een helper- of gemaksmethode toe te voegen die het gemakkelijker maakt om de Forecast API te ondervragen. Deze gemaksmethode accepteert een locatie en een voltooiingsblok. Het voltooiingsblok wordt uitgevoerd wanneer het verzoek is voltooid. Om het werken met blokken gemakkelijker te maken, wordt het aangeraden om een ​​aangepast bloktype aan te duiden, zoals hieronder getoond. Als je je nog steeds ongemakkelijk voelt door blokken te gebruiken, raad ik aan dit geweldige artikel van Akiel Khan te lezen.

Het blok neemt twee argumenten, (1) een boolean die aangeeft of de query succesvol was en (2) een woordenboek met het antwoord van de query. De gemaksmethode, requestWeatherForCoordinate: oplevering:, neemt de coördinaten van een locatie (CLLocationCoordinate2D) en een voltooiingsblok. Door een voltooiingsblok te gebruiken, kunnen we voorkomen dat er een aangepast deelnemersprotocol wordt gemaakt of dat we terugvallen op het gebruik van meldingen. Blokken passen perfect in dit type scenario.

 #import "AFHTTPClient.h" typedef void (^ MTForecastClientCompletionBlock) (BOOL-succes, NSDictionary * -antwoord); @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma mark Shared Client + (MTForecastClient *) sharedClient; #pragma mark - #pragma mark Instance Methods - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) coordinate completion: (MTForecastClientCompletionBlock) completion; @einde

In requestWeatherForCoordinate: oplevering:, we roepen aan getPath: succes: mislukking:, een methode gedeclareerd in AFHTTPClient. Het eerste argument is het pad dat is toegevoegd aan de basis-URL die we eerder hebben gemaakt. De tweede en derde argumenten zijn blokken die worden uitgevoerd wanneer het verzoek slaagt en respectievelijk mislukt. De blokken voor succes en falen zijn vrij eenvoudig. Als een voltooiingsblok is doorgegeven aan requestWeatherForCoordinate: oplevering:, we voeren het blok uit en geven een Booleaanse waarde en het antwoordwoordenboek door (of nul in het foutblok). In het foutblok registreren we de fout van het foutenblok naar de console om het opsporen van fouten te vergemakkelijken.

 - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) coordinate completion: (MTForecastClientCompletionBlock) completion NSString * path = [NSString stringWithFormat: @ "% f,% f", coordinate.latitude, coordinate.longitude]; [self getPath: padparameters: nul succes: ^ (AFHTTPRequestOperation * bewerking, id-reactie) if (completion) completion (YES, response);  fout: ^ (AFHTTPRequestOperation * bewerking, NSError * fout) if (completion) completion (NO, nil); NSLog (@ "Kan geen weergegevens ophalen vanwege fout% @ met gebruikersinformatie% @.", Error, error.userInfo); ]; 

Je vraagt ​​je misschien af ​​wat de antwoord object in de succesblokken is of referenties. Hoewel de prognose-API een JSON-antwoord retourneert, zorgt de antwoord object in het succesblok is een NSDictionary aanleg. Het voordeel van werken met de AFJSONHTTPRequestOperation klas, die we hebben geregistreerd in initWithBaseURL:, is dat het de JSON-reactie accepteert en automatisch een object uit de responsgegevens maakt, een woordenboek in dit voorbeeld.


2. De prognose-API opvragen

Stap 1: Wijzigen setLocation:

Gewapend met de MTForecastClient Klasse, het is tijd om de Forecast API te vragen en de weergegevens op te halen voor de momenteel geselecteerde locatie. De meest geschikte plaats om dit te doen is in de setLocation: methode van de MTWeatherViewController klasse. Wijzig de setLocation: methode zoals hieronder getoond. Zoals je kunt zien, is alles wat we doen aanroepen fetchWeatherData, een andere helper-methode.

 - (void) setLocation: (NSDictionary *) location if (_location! = location) _location = location; // Update User Defaults NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; [ud setObject: location forKey: MTRainUserDefaultsLocation]; [ud synchroniseren]; // Berichtgeving NSNotification * notification1 = [NSNotification notificationWithName: MTRainLocationDidChangeNotification object: self userInfo: location]; [[NSNotificationCenter defaultCenter] postNotification: notification1]; // Updateweergave [self updateView]; // Verzoeklocatie [self fetchWeatherData]; 

Heb je je ooit afgevraagd waarom ik zoveel hulpmethoden in mijn code gebruik? De reden is simpel. Door functionaliteit in helper-methoden in te pakken, is het heel eenvoudig om code opnieuw te gebruiken op verschillende plaatsen in een project. Het grootste voordeel is echter dat het de duplicatie van gevechts-code helpt. Codeduplicatie is iets dat je altijd zo veel mogelijk moet proberen te vermijden. Een ander voordeel van het gebruik van hulpmethoden is dat het uw code leesbaarder maakt. Door methoden te maken die één ding doen en een goed gekozen naam voor de methode te bieden, is het eenvoudiger om uw code snel te lezen en te verwerken.

Stap 2: het verzoek verzenden

Het is tijd om de SVProgressHUD bibliotheek om te gebruiken. Ik vind deze bibliotheek erg leuk omdat hij zo eenvoudig te gebruiken is zonder de codebasis van het project te overbelasten. Bekijk de implementatie van fetchWeatherData hieronder. We beginnen met het tonen van de voortgang HUD en geven vervolgens een structuur door (CLLocationCoordinate2D) naar de gemaksmethode die we eerder hebben gemaakt, requestWeatherForCoordinate: oplevering:. In het voltooiingsblok verbergen we de voortgang HUD en loggen we het antwoord op de console in.

 - (void) fetchWeatherData // Show Progress HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Queryvoorspelling API double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; double lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) completion: ^ (BOOL-succes, NSDictionary * -reactie) // Hitting afwijzen HUD [SVProgressHUD sluiten]; NSLog (@ "Response>% @", antwoord); ]; 

Voordat u uw toepassing bouwt en uitvoert, importeert u het header-bestand van de MTForecastClient klasse in MTWeatherViewController.m.

 #import "MTWeatherViewController.h" #import "MTForecastClient.h" @interface MTWeatherViewController ()  BOOL _locationFound;  @property (strong, nonatomic) NSDictionary * locatie; @property (strong, nonatomic) CLLocationManager * locationManager; @einde

Wat gebeurt er wanneer het apparaat niet is verbonden met internet? Heb je nagedacht over dat scenario? In termen van gebruikerservaring is het een goede gewoonte om de gebruiker op de hoogte te stellen wanneer de toepassing geen gegevens kan opvragen bij de prognose-API. Laat me laten zien hoe dit te doen met de AFNetworking-bibliotheek.


3. Bereikbaarheid

Er zijn een aantal bibliotheken die deze functionaliteit bieden, maar we houden het bij AFNetworking. Apple biedt ook voorbeeldcode, maar het is een beetje verouderd en ondersteunt ARC niet.

AFNetworking heeft blokken echt omarmd, wat zeker een van de redenen is waarom deze bibliotheek zo populair is geworden. Bewaken van wijzigingen in bereikbaarheid is net zo eenvoudig als een blok doorgeven aan setReachabilityStatusChangeBlock:, een andere methode van de AFHTTPClient klasse. Het blok wordt elke keer uitgevoerd als de bereikbaarheidstatus verandert en het accepteert een enkel argument van het type AFNetworkReachabilityStatus. Bekijk de bijgewerkte versie initWithBaseURL: methode van de MTForecastClient klasse.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Accept HTTP-header [self setDefaultHeader: @ "Accepteer" waarde: @ "application / json"]; // HTTP-bedieningsklasse registreren [zelfregisterHTTPOperationClass: [AFJSONRequestOperation class]]; // Bereikbaarheid __ zwak type van (zelf) zwakzelf = zelf; [self setReachabilityStatusChangeBlock: ^ (AFNetworkReachabilityStatus-status) [[NSNotificationCenter defaultCenter] postNotificationName: MTRainReachabilityStatusDidChangeNotification-object: weakSelf]; ];  terugkeer zelf; 

Om een ​​behouden cyclus te vermijden, geven we een zwakke verwijzing door naar het singleton-object in het blok waar we doorheen gaan setReachabilityStatusChangeBlock:. Zelfs als u ARC in uw projecten gebruikt, moet u zich nog steeds bewust zijn van subtiele geheugenproblemen zoals deze. De naam van de melding die we plaatsen is een andere stringconstante die is gedeclareerd MTConstants.h / .m.

 extern NSString * const MTRainReachabilityStatusDidChangeNotification;
 NSString * const MTRainReachabilityStatusDidChangeNotification = @ "com.mobileTuts.MTRainReachabilityStatusDidChangeNotification";

De reden voor het plaatsen van een melding in het wijzigingsblok voor de bereikbaarheidstatus is om het voor andere delen van de toepassing gemakkelijker te maken om bij te werken wanneer de bereikbaarheid van het apparaat verandert. Om ervoor te zorgen dat de MTWeatherViewController klasse wordt op de hoogte gebracht van wijzigingen in bereikbaarheid, instanties van de klasse worden toegevoegd als waarnemer voor de meldingen verzonden door de klant van de voorspelling, zoals hieronder weergegeven.

 - (id) initWithNibName: (NSString *) nibNameOrNil-bundel: (NSBundle *) nibBundleOrNil self = [super initWithBibName: nibNameOrNil-bundel: nibBundleOrNil]; if (self) // Location Manager initialiseren self.locationManager = [[CLLocationManager alloc] init]; // Configureer Locatiemanager [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Add Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: self selector: @selector (reachabilityStatusDidChange :) naam: MTRainReachabilityStatusDidChangeNotification-object: nihil];  terugkeer zelf; 

Dit betekent ook dat we de instantie moeten verwijderen als een waarnemer in de dealloc methode. Dit is een detail dat vaak over het hoofd wordt gezien.

 - (void) dealloc // Observer verwijderen [[NSNotificationCenter defaultCenter] removeObserver: self]; 

De implementatie van reachabilityStatusDidChange: is vrij eenvoudig op dit moment. We zullen de implementatie updaten zodra we de gebruikersinterface van de applicatie hebben gemaakt.

 - (ongeldig) bereikbaarheidStatusDidChange: (NSNotification *) notificatie MTForecastClient * forecastClient = [notification-object]; NSLog (@ "Bereikbaarheidsstatus>% i", forecastClient.networkReachabilityStatus); 

4. Gegevens vernieuwen

Voordat we dit bericht inpakken, wil ik twee extra functies toevoegen, (1) weergegevens ophalen wanneer de toepassing actief wordt en (2) de mogelijkheid toevoegen om weergegevens handmatig te vernieuwen. We zouden een timer kunnen implementeren die om het uur of zo nieuwe gegevens ophaalt, maar dit is naar mijn mening niet nodig voor een weerapplicatie. De meeste gebruikers starten de applicatie, bekijken het weer en plaatsen de applicatie op de achtergrond. Het is daarom alleen nodig om nieuwe gegevens op te halen wanneer de gebruiker de toepassing start. Dit betekent dat we moeten luisteren UIApplicationDidBecomeActiveNotification meldingen in de MTWeatherViewController klasse. Zoals we deden voor het controleren van wijzigingen in de bereikbaarheid, voegen we instanties van de klasse toe als waarnemers van meldingen van het type UIApplicationDidBecomeActiveNotification.

 - (id) initWithNibName: (NSString *) nibNameOrNil-bundel: (NSBundle *) nibBundleOrNil self = [super initWithBibName: nibNameOrNil-bundel: nibBundleOrNil]; if (self) // Location Manager initialiseren self.locationManager = [[CLLocationManager alloc] init]; // Configureer Locatiemanager [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Add Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: self selector: @selector (applicationDidBecomeActive :) name: UIApplicationDidBecomeActiveNotification object: nil]; [nc addObserver: self selector: @selector (reachabilityStatusDidChange :) naam: MTRainReachabilityStatusDidChangeNotification-object: nihil];  terugkeer zelf; 

In applicationDidBecomeActive:, we verifiëren dat plaats is ingesteld (niet nul) omdat dit niet altijd waar zal zijn. Als de locatie geldig is, halen we de weergegevens op.

 - (ongeldig) applicationDidBecomeActive: (NSNotification *) notificatie if (self.location) [self fetchWeatherData]; 

Ik heb ook gewijzigd fetchWeatherData om alleen de Forecast-API te ondervragen als het apparaat is verbonden met internet.

 - (void) fetchWeatherData if ([[MTForecastClient sharedClient] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) retour; // Show Progress HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Queryvoorspelling API double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; double lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) completion: ^ (BOOL-succes, NSDictionary * -reactie) // Hitting afwijzen HUD [SVProgressHUD sluiten]; // NSLog (@ "Response>% @", antwoord); ]; 

Laten we een knop toevoegen aan de weerview-controller die de gebruiker kan gebruiken om de weergegevens handmatig te verversen. Maak een outlet in MTWeatherViewController.h en maak een frissen: actie in MTWeatherViewController.m.

 #importeren  #import "MTLocationsViewController.h" @interface MTWeatherViewController: UIViewController  @property (weak, nonatomic) IBOutlet UILabel * labelLocation; @property (weak, nonatomic) IBOutlet UIButton * buttonRefresh; @einde
 - (IBAction) refresh: (id) afzender if (self.location) [self fetchWeatherData]; 

Open MTWeatherViewController.xib, voeg een knop toe aan de weergave van de view controller met een titel van verversen, en verbind het stopcontact en de actie met de knop (figuur 3). De reden voor het maken van een outlet voor de knop is om deze te kunnen uitschakelen wanneer er geen netwerkverbinding beschikbaar is. Om dit te laten werken, moeten we het reachabilityStatusDidChange: methode zoals hieronder getoond.

Figuur 3: Een knop Vernieuwen toevoegen
 - (ongeldig) bereikbaarheidStatusDidChange: (NSNotification *) notificatie MTForecastClient * forecastClient = [notification-object]; NSLog (@ "Bereikbaarheidsstatus>% i", forecastClient.networkReachabilityStatus); // Update Refresh Button self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus! = AFNetworkReachabilityStatusNotReachable); 

Het is niet nodig om de vernieuwingsknop tijdelijk uit te schakelen wanneer een aanvraag wordt verwerkt fetchWeatherData omdat de voortgang HUD een laag bovenop de weergave van de weergavecontroller toevoegt die voorkomt dat de gebruiker de knop meer dan eens aanraakt. Bouw en voer de applicatie uit om alles te testen.


Bonus: locaties verwijderen

Een lezer vroeg me hoe ik locaties uit de lijst kon verwijderen, dus ik neem het hier voor de volledigheid op. Het eerste dat we moeten doen is het tabeloverzicht vertellen welke rijen door implementatie kunnen worden bewerkt tableView: canEditRowAtIndexPath: van de UITableViewDataSource protocol. Deze methode retourneert JA als de rij om indexPath is bewerkbaar en NEE als het niet is. De implementatie is eenvoudig, zoals u hieronder kunt zien. Elke rij kan worden bewerkt met uitzondering van de eerste rij en de momenteel geselecteerde locatie.

 - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath if (indexPath.row == 0) return NO;  // Locatie ophalen NSDictionary * location = [objectlocaties self.locationsAtIndex: (indexPath.row - 1)]; retourneren! [self isCurrentLocation: location]; 

Om te controleren of plaats is de huidige locatie, gebruiken we een andere hulpmethode, isCurrentLocation:, waarin we de huidige locatie ophalen en de coördinaten van locaties vergelijken. Het zou beter (en gemakkelijker) zijn geweest als we een unieke identifier hadden toegewezen aan elke locatie die was opgeslagen in de database met gebruikersstandaarden. Het zou het niet alleen gemakkelijker maken om locaties te vergelijken, maar het zou ons ook in staat stellen om de unieke identificatie van de huidige locatie op te slaan in de database met gebruikersstandaarden en deze op te zoeken in de reeks locaties. Het probleem met de huidige implementatie is dat locaties met exact dezelfde coördinaten niet van elkaar te onderscheiden zijn.

 - (BOOL) isCurrentLocation: (NSDictionary *) locatie // Huidige locatie ophalen NSDictionary * currentLocation = [[NSUserDefaults standardUserDefaults] objectForKey: MTRainUserDefaultsLocation]; if ([location [MTLocationKeyLatitude] doubleValue] == [currentLocation [MTLocationKeyLatitude] doubleValue] && [location [MTLocationKeyLongitude] doubleValue] == [currentLocation [MTLocationKeyLongitude] doubleValue]) return YES;  return NO; 

Wanneer de gebruiker op de verwijderknop van een tabelweergave rij tikt, wordt de gegevensbron van de tabelweergave verzonden tableView: commitEditingStyle: forRowAtIndexPath: bericht. In deze methode moeten we (1) de gegevensbron bijwerken, (2) de wijzigingen opslaan in de database met gebruikersstandaarden en (3) de tabelweergave bijwerken. Als editingStyle is gelijk aan UITableViewCellEditingStyleDelete, we verwijderen de locatie van de locaties array en sla de bijgewerkte array op in de standaard gebruikersdatabase. We verwijderen ook de rij uit de tabelweergave om de wijziging in de gegevensbron weer te geven.

 - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle == UITableViewCellEditingStyleDelete) // Update Locations [self.locations removeObjectAtIndex: (indexPath.row - 1)] ; // Update User Defaults [[NSUserDefaults standardUserDefaults] setObject: self.locations forKey: MTRainUserDefaultsLocations]; // Tabelweergave bijwerken [tableView deleteRowsAtIndexPaths: @ [indexPath] withRowAnimation: UITableViewRowAnimationTop]; 

Om de bewerkingsstijl van de tabelweergave te wijzigen, moeten we een bewerkingsknop aan de gebruikersinterface toevoegen. Maak een outlet voor de knop in MTLocationsViewController.h en een actie genaamd editLocations: in MTLocationsViewController.m. In editLocations:, we schakelen de bewerkingsstijl van de tabelweergave in.

 #importeren  @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController: UIViewController  @property (weak, nonatomic) id delegeren; @property (weak, nonatomic) IBOutlet UITableView * tableView; @property (weak, nonatomic) IBOutlet UIBarButtonItem * editButton; @ end @protocol MTLocationsViewControllerDelegate  - (void) controllerShouldAddCurrentLocation: (MTLocationsViewController *) controller; - (void) controller: (MTLocationsViewController *) controller didSelectLocation: (NSDictionary *) locatie; @einde
 - (IBAction) editLocations: (id) afzender [self.tableView setEditing:! [Self.tableView isEditing] animated: YES]; 

Open MTLocationsViewController.xib, voeg een navigatiebalk toe aan de weergave van de weergavecontroller en voeg een bewerkknop toe aan de navigatiebalk. Verbind de knop Bewerken met het stopcontact en de actie die we zojuist hebben gemaakt.


Figuur 4: Een knop Bewerken toevoegen

U vraagt ​​zich misschien af ​​waarom we een outlet hebben gemaakt voor de knop Bewerken. De reden is dat we de titel van de bewerkknop van moeten kunnen wijzigen Bewerk naar Gedaan, en vice versa, telkens als de bewerkingsstijl van de tabelweergave verandert. Wanneer de gebruiker de laatste locatie (behalve de huidige locatie) in de tabelweergave verwijdert, zou het bovendien prettig zijn om automatisch de bewerkingsstijl van de tabelweergave te wijzigen. Deze functies zijn niet moeilijk te implementeren en daarom laat ik ze aan jou over als een oefening. Als je problemen tegenkomt of vragen hebt, kun je een reactie achterlaten in de reacties onder dit artikel.

Conclusie

We hebben de Forecast API met succes geïntegreerd in onze weertoepassing. In de volgende tutorial zullen we focussen op de gebruikersinterface en het ontwerp van de applicatie.