Créer une application météo avec prévision - Intégration de l'API

Dans le premier article de cette série, nous avons jeté les bases du projet en le configurant et en créant la structure de l'application. Dans cet article, nous exploitons la bibliothèque AFNetworking pour interagir avec l'API Forecast..


introduction

Dans la première tranche de cette série, nous avons jeté les bases de notre application météo. Les utilisateurs peuvent ajouter leur emplacement actuel et basculer entre eux. Dans ce tutoriel, nous utiliserons la bibliothèque AFNetworking pour demander à l’API Forecast les données météorologiques de l’emplacement actuellement sélectionné..

Si vous souhaitez suivre, vous aurez besoin d'une clé de l'API Forecast. Vous pouvez obtenir une clé API en vous inscrivant en tant que développeur à Forecast. L'inscription est gratuite, je vous encourage donc à essayer le service de prévision météo. Vous pouvez trouver votre clé API au bas de votre tableau de bord (figure 1)..


Figure 1: Obtention de votre clé API

1. Sous-classement AFHTTPClient

Comme je l'ai écrit plus tôt dans cet article, nous utiliserons le AFNetworking bibliothèque pour communiquer avec l'API Forecast. AFNetworking offre plusieurs options, mais pour que notre application soit une preuve pour le futur, nous opterons pour la AFHTTPClient classe. Cette classe est conçue pour consommer des services Web, tels que l'API Forecast. Même si nous n’accéderons qu’à un seul point de terminaison d’API, il est toujours utile d’utiliser le AFHTTPClient comme vous allez apprendre dans quelques instants.

Il est recommandé de créer un AFHTTPClient sous-classe pour chaque service Web. Comme nous avons déjà ajouté AFNetworking à notre projet dans le didacticiel précédent, nous pouvons immédiatement commencer à sous-classer. AFHTTPClient.

Étape 1: Créer la classe

Créez une nouvelle classe Objective-C, nommez-la MTForecastClient, et en faire une sous-classe de AFHTTPClient (Figure 2).

Figure 2: Sous-classe AFHTTPClient

Étape 2: Création d'un objet Singleton

Nous adopterons le motif singleton pour faciliter l’utilisation du MTForecastClient classe dans notre projet. Cela signifie qu'une seule instance de la classe est active à la fois pendant la durée de vie de l'application. Il est fort probable que vous soyez déjà familiarisé avec le modèle singleton, car il s'agit d'un modèle courant dans de nombreux langages de programmation orientés objet. À première vue, le motif singleton semble très pratique, mais il y a un certain nombre de mises en garde à surveiller. Vous pouvez en apprendre plus sur les singletons en lisant cet excellent article de Matt Gallagher.

Créer un objet singleton est assez simple dans Objective-C. Commencez par déclarer une méthode de classe dans MTForecastClient.h pour fournir un accès public à l'objet singleton (voir ci-dessous).

 #import "AFHTTPClient.h" @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma mark client partagé + (MTForecastClient *) sharedClient; @fin

L'implémentation de sharedClient Cela peut paraître intimidant au début, mais ce n’est pas si difficile une fois que vous comprenez ce qui se passe. Nous déclarons d'abord deux variables statiques, (1) prédicat de type dispatch_once_t et (2) _sharedClient de type MTForecastClient. Comme son nom l'indique, prédicat est un prédicat que nous utilisons en combinaison avec le dispatch_once une fonction. Quand on travaille avec une variable de type dispatch_once_t, il est important qu'il soit déclaré de manière statique. La deuxième variable, _sharedClient, va stocker une référence à l'objet singleton.

le dispatch_once fonction prend un pointeur sur un dispatch_once_t la structure, le prédicat et un bloc. La beauté de dispatch_once est qu'il exécutera le bloc une fois pour la durée de vie de l'application, ce qui est exactement ce que nous voulons. le dispatch_once La fonction n'a pas beaucoup d'utilisations, mais c'est certainement l'un d'entre eux. Dans le bloc que nous passons à dispatch_once, nous créons l'objet singleton et stockons une référence dans _sharedClient. Il est plus sûr d'invoquer allouer et init séparément pour éviter une situation de concurrence susceptible de mener à une impasse. Euh… quoi? Vous pouvez en savoir plus sur les détails de Nitty Gritty sur Stack Overflow.

 + (MTForecastClient *) sharedClient statique predicate dispatch_once_t; static MTForecastClient * _sharedClient = nil; dispatch_once (& predicate, ^ _sharedClient = [autoall]; _sharedClient = [_sharedClient initWithBaseURL: [auto baseURL]];); return _sharedClient; 

La chose importante à comprendre sur la mise en œuvre de la sharedClient méthode de classe est que l'initialiseur, initWithBaseURL:, est invoqué qu'une seule fois. L'objet singleton est stocké dans le _sharedClient variable statique, qui est retournée par le sharedClient méthode de classe.

Étape 3: Configuration du client

Dans sharedClient, nous invoquons initWithBaseURL:, qui à son tour invoque baseURL, une autre méthode de classe. Dans initWithBaseURL:, nous définissons un en-tête par défaut, ce qui signifie que le client ajoute cet en-tête à chaque demande qu'il envoie. C’est l’un des avantages de travailler avec le AFHTTPClient classe. Dans initWithBaseURL:, nous enregistrons également une classe d'opération HTTP en appelant registerHTTPOperationClass:. La bibliothèque AFNetworking fournit un certain nombre de classes d'opérations spécialisées. Une de ces classes est la AFJSONRequestOperation classe, ce qui rend très facile l’interaction avec une API JSON. Comme l’API de prévision renvoie une réponse JSON, le AFJSONRequestOperation la classe est un bon choix. le registerHTTPOperationClass: méthode fonctionne de manière similaire à la registerClass: forCellReuseIdentifier: du UITableView travaux de classe. En indiquant au client quelle classe d'opération nous souhaitons utiliser pour interagir avec le service Web, il instanciera des instances de cette classe pour nous sous le capot. Pourquoi cela est utile deviendra clair dans quelques instants.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Accepter l'en-tête HTTP [self setDefaultHeader: @ "Accept", valeur: @ "application / json"]; // Enregistrer la classe d'opération HTTP [self registerHTTPOperationClass: [AFJSONRequestOperation class]];  retourner soi-même; 

L'implémentation de baseURL n’est rien de plus qu’une méthode pratique pour construire l’URL de base du client. L'URL de base est l'URL utilisée par le client pour atteindre le service Web. C'est l'URL sans nom de méthode ni paramètre. L'URL de base de l'API Forecast est https://api.forecast.io/forecast//. La clé API fait partie de l'URL, comme vous pouvez le voir. Cela peut sembler peu sûr et c'est le cas. Il n'est pas difficile pour quelqu'un de saisir la clé API. Il est donc conseillé de travailler avec un proxy pour masquer la clé API. Parce que cette approche est un peu plus complexe, je ne couvrirai pas cet aspect de cette série..

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

Vous avez peut-être remarqué dans la mise en œuvre de baseURL que j'ai utilisé une autre constante de chaîne pour stocker la clé API. Cela peut sembler inutile puisque nous n'utilisons la clé API qu'à un seul endroit. Cependant, il est recommandé de stocker les données d’application dans un emplacement ou dans une liste de propriétés..

 #pragma mark - #pragma mark API de prévision extern NSString * const MTForecastAPIKey;
 #pragma mark - #pragma mark API de prévision NSString * const MTForecastAPIKey = @ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

Étape 4: Ajout d'une méthode d'assistance

Avant de poursuivre, je voudrais prolonger la MTForecastClient classe en ajoutant un utilitaire ou une méthode pratique qui facilitera l’interrogation de l’API Forecast. Cette méthode pratique acceptera un emplacement et un bloc d’achèvement. Le bloc d'achèvement est exécuté à la fin de la demande. Pour faciliter l'utilisation des blocs, il est recommandé de déclarer un type de bloc personnalisé comme indiqué ci-dessous. Si vous ne vous sentez toujours pas à l'aise avec l'utilisation de blocs, je vous recommande de lire cet excellent article de Akiel Khan..

Le bloc prend deux arguments, (1) un booléen indiquant si la requête a abouti et (2) un dictionnaire avec la réponse de la requête. La méthode de la commodité, requestWeatherForCoordinate: complétion:, prend les coordonnées d'un lieu (CLLocationCoordinate2D) et un bloc d’achèvement. En utilisant un bloc d'achèvement, nous pouvons éviter de créer un protocole de délégué personnalisé ou de recourir à des notifications. Les blocs conviennent parfaitement à ce type de scénario.

 #import "AFHTTPClient.h" typedef void (^ MTForecastClientCompletionBlock) (succès BOOL, réponse NSDictionary *); @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma mark Client partagé + (MTForecastClient *) sharedClient; #pragma mark - #pragma mark Méthodes d'instance - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) achèvement des coordonnées: (MTForecastClientCompletionBlock) achèvement; @fin

Dans requestWeatherForCoordinate: complétion:, nous invoquons getPath: succès: échec:, une méthode déclarée dans AFHTTPClient. Le premier argument est le chemin ajouté à l'URL de base que nous avons créée précédemment. Les deuxième et troisième arguments sont des blocs qui sont exécutés lorsque la demande aboutit ou échoue, respectivement. Les blocs de réussite et d’échec sont assez simples. Si un bloc d'achèvement a été passé à requestWeatherForCoordinate: complétion:, nous exécutons le bloc et passons une valeur booléenne et le dictionnaire de réponse (ou néant dans le bloc d'échec). Dans le bloc d'échec, nous enregistrons l'erreur du bloc d'échec vers la console pour faciliter le débogage..

 - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) achèvement des coordonnées: (MTForecastClientCompletionBlock) achèvement NSString * path = [NSString chaîneWithFormat: @ "% f,% f", coordonnée.latitude, coordonnée.longitude]; [self getPath: paramètres de chemin: nul succès: ^ (opération AFHTTPRequestOperation *, réponse id) if (complétion) complétion (YES, réponse);  échec: ^ (opération AFHTTPRequestOperation *, erreur NSError *) if (complétion) complétion (NO, nil); NSLog (@ "Impossible d'extraire les données météo en raison de l'erreur% @ avec les informations utilisateur% @.", Error, error.userInfo); ]; 

Vous vous demandez peut-être ce que le réponse objet dans les blocs de succès est ou références. Même si l’API Forecast renvoie une réponse JSON, le réponse objet dans le bloc de succès est un NSDictionary exemple. L’avantage de travailler avec le AFJSONHTTPRequestOperation classe, dans laquelle nous nous sommes inscrits initWithBaseURL:, est-ce qu'il accepte la réponse JSON et crée automatiquement un objet à partir des données de réponse, un dictionnaire dans cet exemple.


2. Interrogation de l'API de prévision

Étape 1: Modifier setLocation:

Armé du MTForecastClient classe, il est temps d'interroger l'API Forecast et d'extraire les données météorologiques pour l'emplacement actuellement sélectionné. L’endroit le plus approprié pour faire cela est dans le setLocation: méthode du MTWeatherViewController classe. Modifier le setLocation: méthode comme indiqué ci-dessous. Comme vous pouvez le constater, tout ce que nous faisons est d’invoquer fetchWeatherData, une autre méthode d'assistance.

 - (void) setLocation: (NSDictionary *) location if (_location! = location) _location = location; // Mise à jour des paramètres utilisateur par défaut NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; [ud setObject: location forKey: MTRainUserDefaultsLocation]; [ud synchronize]; // Notification après notification NSNotification * notification1 = [objet notification NSNotification notificationWithName: MTRainLocationDidChangeNotification: self userInfo: location]; [[NSNotificationCenter defaultCenter] postNotification: notification1]; // Mise à jour de la vue [self updateView]; // Emplacement de la demande [self fetchWeatherData]; 

Vous êtes-vous déjà demandé pourquoi j'utilise autant de méthodes d'assistance dans mon code? La raison est simple. En incorporant la fonctionnalité dans des méthodes d'assistance, il est très facile de réutiliser du code à divers endroits d'un projet. Le principal avantage, cependant, est que cela permet de lutter contre la duplication de code. La duplication de code est quelque chose que vous devriez toujours essayer d'éviter autant que possible. L’utilisation des méthodes auxiliaires présente un autre avantage: elle rend votre code beaucoup plus lisible. En créant des méthodes qui font une chose et en fournissant un nom de méthode bien choisi, il est plus facile de lire et de traiter rapidement votre code..

Étape 2: Envoi de la demande

Il est temps de mettre le SVProgressHUD bibliothèque à utiliser. J'aime beaucoup cette bibliothèque car elle est si simple à utiliser sans encombrer la base de code du projet. Jetez un coup d’œil à la mise en œuvre de fetchWeatherData au dessous de. Nous commençons par montrer le progrès du HUD puis passons à une structure (CLLocationCoordinate2D) à la méthode de commodité que nous avons créée précédemment, requestWeatherForCoordinate: complétion:. Dans le bloc d'achèvement, nous masquons la palette de progression et enregistrons la réponse dans la console..

 - (void) fetchWeatherData // Afficher la palette de progression [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // API de prévision de requête double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; double lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) complétion: ^ (succès BOOL, réponse NSDictionary *) // Suppression du HUD de progression [SVProgressHUD licencié]; NSLog (@ "Response>% @", response); ]; 

Avant de générer et d’exécuter votre application, importez le fichier d’en-tête du fichier. MTForecastClient cours en MTWeatherViewController.m.

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

Que se passe-t-il lorsque l'appareil n'est pas connecté au Web? Avez-vous pensé à ce scénario? En termes d'expérience utilisateur, il est recommandé d'avertir l'utilisateur lorsque l'application est incapable de demander des données à l'API Forecast. Me laisser montrer comment faire ceci avec la bibliothèque AFNetworking.


3. Accessibilité

Un certain nombre de bibliothèques fournissent cette fonctionnalité, mais nous nous en tiendrons à AFNetworking. Apple fournit également un exemple de code, mais il est un peu obsolète et ne prend pas en charge ARC..

AFNetworking a vraiment adopté les blocs, ce qui est certainement l'une des raisons pour lesquelles cette bibliothèque est devenue si populaire. Surveiller les changements d’atteignabilité est aussi simple que de passer un bloc à setReachabilityStatusChangeBlock:, une autre méthode du AFHTTPClient classe. Le bloc est exécuté chaque fois que l'état d'accessibilité change et accepte un seul argument de type. AFNetworkReachabilityStatus. Jetez un coup d'oeil à la mise à jour initWithBaseURL: méthode du MTForecastClient classe.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Accepter l'en-tête HTTP [self setDefaultHeader: @ "Accept", valeur: @ "application / json"]; // Enregistrer la classe d'opération HTTP [self registerHTTPOperationClass: [AFJSONRequestOperation class]]; // Accessibilité __ type faible de (soi) faibleSoi = soi; [self setReachabilityStatusChangeBlock: ^ (état AFNetworkReachabilityStatus) [[[NSNotificationCenter defaultCenter]] postNotificationName: MTRainReachabilityStatusDidChangeNotification objet: faibleSelf]; ];  retourner soi-même; 

Pour éviter un cycle de conservation, nous passons une référence faible à l'objet singleton dans le bloc que nous passons à setReachabilityStatusChangeBlock:. Même si vous utilisez ARC dans vos projets, vous devez toujours être conscient des problèmes de mémoire subtils comme celui-ci. Le nom de la notification que nous publions est une autre constante de chaîne déclarée dans MTConstants.h / .m.

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

La raison pour laquelle vous enregistrez une notification dans le bloc de modification du statut d'accessibilité est de faciliter la mise à jour des autres parties de l'application lors de la modification de l'accessibilité du périphérique. Pour vous assurer que le MTWeatherViewController la classe est informée des changements d’atteignabilité, des instances de la classe sont ajoutées en tant qu’observateur des notifications envoyées par le client Forecast, comme indiqué ci-dessous.

 - (id) initWithNibName: (NSString *) bundle nibNameOrNil: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; if (self) // Initialise le gestionnaire d'emplacement self.locationManager = [[CLLocationManager alloc] init]; // Configurer le gestionnaire d'emplacement [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Ajouter l'observateur NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: sélecteur automatique: @selector (reachabilityStatusDidChange :) nom: MTRainReachabilityStatusDidChangeNotification object: nil];  retourner soi-même; 

Cela signifie également que nous devons supprimer l'instance en tant qu'observateur dans le dealloc méthode. C'est un détail qui est souvent négligé.

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

L'implémentation de reachabilityStatusDidChange: est assez basique pour le moment. Nous mettrons à jour son implémentation une fois que nous aurons créé l'interface utilisateur de l'application..

 - (void) reachabilityStatusDidChange: (NSNotification *) notification MTForecastClient * forecastClient = [objet de notification]; NSLog (@ "Statut d'accessibilité>% i", forecastClient.networkReachabilityStatus); 

4. Actualiser les données

Avant de terminer cet article, je souhaite ajouter deux fonctionnalités supplémentaires: (1) récupérer les données météorologiques chaque fois que l'application devient active et (2) ajouter la possibilité d'actualiser manuellement les données météorologiques. Nous pourrions implémenter une minuterie qui récupère de nouvelles données toutes les heures environ, mais cela n’est pas nécessaire pour une application météo à mon avis. La plupart des utilisateurs vont lancer l'application, consulter la météo et la mettre en arrière-plan. Il est donc uniquement nécessaire d'extraire de nouvelles données lorsque l'utilisateur lance l'application. Cela signifie que nous devons écouter pour UIApplicationDidBecomeActiveNotification notifications dans le MTWeatherViewController classe. Comme nous l’avons fait pour surveiller les modifications d’atteignabilité, nous ajoutons des instances de la classe en tant qu’observateurs des notifications de type. UIApplicationDidBecomeActiveNotification.

 - (id) initWithNibName: (NSString *) bundle nibNameOrNil: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; if (self) // Initialise le gestionnaire d'emplacement self.locationManager = [[CLLocationManager alloc] init]; // Configurer le gestionnaire d'emplacement [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Ajouter l'observateur NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: sélecteur automatique: @selector (applicationDidBecomeActive :) nom: UIApplicationDidBecomeActiveNotification object: nil]; [nc addObserver: sélecteur automatique: @selector (reachabilityStatusDidChange :) nom: MTRainReachabilityStatusDidChangeNotification object: nil];  retourner soi-même; 

Dans applicationDidBecomeActive:, on vérifie que emplacement est réglé (pas néant) parce que ce ne sera pas toujours vrai. Si l'emplacement est valide, nous récupérons les données météorologiques.

 - (void) applicationDidBecomeActive: (NSNotification *) notification if (self.location) [self fetchWeatherData]; 

J'ai aussi amendé fetchWeatherData interroger uniquement l'API Forecast si le périphérique est connecté au Web.

 - (void) fetchWeatherData if ([[MTForecastClient sharedClient] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) return; // Afficher la palette de progression [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // API de prévision de requête double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; double lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) complétion: ^ (succès BOOL, réponse NSDictionary *) // Suppression du HUD de progression [SVProgressHUD licencié]; // NSLog (@ "Response>% @", response); ]; 

Ajoutons un bouton au contrôleur de vue météo sur lequel l'utilisateur peut appuyer pour actualiser manuellement les données météo. Créer un point de vente dans MTWeatherViewController.h et créer un rafraîchir: action dans MTWeatherViewController.m.

 #importation  #import "MTLocationsViewController.h" @interface MTWeatherViewController: UIViewController  @property (faible, non atomique) IBOutlet UILabel * labelLocation; @property (faible, non atomique) IBOutlet UIButton * buttonRefresh; @fin
 - (IBAction) actualise: (id) expéditeur if (self.location) [self fetchWeatherData]; 

Ouvrir MTWeatherViewController.xib, ajouter un bouton à la vue du contrôleur de vue avec un titre de Rafraîchir, et connectez la prise et action avec le bouton (figure 3). La création d'un point de vente pour le bouton a pour but de pouvoir le désactiver lorsqu'aucune connexion réseau n'est disponible. Pour que cela fonctionne, nous devons mettre à jour le reachabilityStatusDidChange: méthode comme indiqué ci-dessous.

Figure 3: ajout d'un bouton d'actualisation
 - (void) reachabilityStatusDidChange: (NSNotification *) notification MTForecastClient * forecastClient = [objet de notification]; NSLog (@ "Statut d'accessibilité>% i", forecastClient.networkReachabilityStatus); // Bouton d'actualisation de la mise à jour self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus! = AFNetworkReachabilityStatusNotReachable); 

Il n'est pas nécessaire de désactiver temporairement le bouton d'actualisation lorsqu'une demande est en cours de traitement dans fetchWeatherData parce que la palette de progression ajoute une couche au-dessus de la vue du contrôleur de vue, ce qui empêche l'utilisateur d'appuyer plusieurs fois sur le bouton. Générez et exécutez l'application pour tout tester.


Bonus: supprimer des lieux

Un lecteur m'a demandé comment supprimer des emplacements de la liste afin que je l'inclue ici par souci d'exhaustivité. La première chose à faire est de dire à la vue tableau quelles sont les lignes modifiables en implémentant tableView: canEditRowAtIndexPath: du UITableViewDataSource protocole. Cette méthode retourne OUI si la rangée à indexPath est éditable et NON si ce n'est pas le cas. La mise en œuvre est simple, comme vous pouvez le voir ci-dessous. Chaque ligne est éditable sauf la première ligne et l'emplacement actuellement sélectionné.

 - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath if (indexPath.row == 0) return NO;  // Fetch Location NSDictionary * location = [self.locations objectAtIndex: (indexPath.row - 1)]; return! [self isCurrentLocation: location]; 

Pour vérifier si emplacement est l'emplacement actuel, nous utilisons une autre méthode d'assistance, isCurrentLocation:, dans lequel nous allons chercher l'emplacement actuel et comparer les coordonnées des emplacements. Il aurait été préférable (et plus simple) d’attribuer un identifiant unique à chaque emplacement stocké dans la base de données par défaut des utilisateurs. Cela faciliterait non seulement la comparaison des emplacements, mais nous permettrait également de stocker l'identifiant unique de l'emplacement actuel dans la base de données par défaut des utilisateurs et de le rechercher dans l'ensemble des emplacements. Le problème avec l'implémentation actuelle est que les emplacements avec exactement les mêmes coordonnées ne peuvent pas être distingués les uns des autres.

 - (BOOL) isCurrentLocation: (NSDictionary *) location // Récupérer l'emplacement actuel NSDictionary * currentLocation = [[NSUserDefaults standardUserDefaults] objectForKey: MTRainUserDefaultsLocation]; if ([emplacement [MTLocationKeyLatitude] doubleValue] == [currentLocation [MTLocationKeyLatitude] doubleValue] && [emplacement [MTLocationKeyLongitude] doubleValue] == [currentLocation [MTLocationKeyLongitude] doubleValue]) return YES;  return NO; 

Lorsque l'utilisateur appuie sur le bouton de suppression d'une ligne de la vue tableau, la source de données de la vue table reçoit un message. tableView: commitEditingStyle: forRowAtIndexPath: message. Dans cette méthode, nous devons (1) mettre à jour la source de données, (2) enregistrer les modifications dans la base de données des paramètres utilisateur par défaut et (3) mettre à jour la vue tabulaire. Si style de montage est égal à UITableViewCellEditingStyleDelete, on enlève l'emplacement du Emplacements array et stocke le tableau mis à jour dans la base de données par défaut de l'utilisateur. Nous supprimons également la ligne de la vue tableau pour refléter le changement de la source de données..

 - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) éditionStyle pourRowAtIndexPath: (N ° ; // Mise à jour des paramètres utilisateur par défaut [[NSUserDefaults standardUserDefaults] setObject: self.locations forKey: MTRainUserDefaultsLocations]; // Mise à jour de la vue de table [tableView deleteRowsAtIndexPaths: @ [indexPath] withRowAnimation: UITableViewRowAnimationTop]; 

Pour basculer le style d'édition de la vue tableau, nous devons ajouter un bouton d'édition à l'interface utilisateur. Créer un point de vente pour le bouton dans MTLocationsViewController.h et une action nommée editLocations: dans MTLocationsViewController.m. Dans editLocations:, nous basculons le style d'édition de la vue table.

 #importation  @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController: UIViewController  @propriété (faible, non atomique) id déléguer; @property (faible, non atomique) IBOutlet UITableView * tableView; @property (faible, non atomique) IBOutlet UIBarButtonItem * editButton; @end @protocol MTLocationsViewControllerDelegate  - (void) controllerShouldAddCurrentLocation: (MTLocationsViewController *) controller; - contrôleur (void): (MTLocationsViewController *) contrôleur didSelectLocation: (NSDictionary *) location; @fin
 - (IBAction) editLocations: (id) expéditeur [self.tableView setEditing:! [Self.tableView isEditing] animé: YES]; 

Ouvrir MTLocationsViewController.xib, ajoutez une barre de navigation à la vue du contrôleur de vue et ajoutez un bouton d'édition à la barre de navigation. Connectez le bouton d'édition à la sortie et à l'action que nous avons créées il y a un instant.


Figure 4: Ajout d'un bouton d'édition

Vous vous demandez peut-être pourquoi nous avons créé un point de vente pour le bouton Modifier. La raison en est que nous devons pouvoir modifier le titre du bouton Modifier de modifier à Terminé, et vice versa, chaque fois que le style d'édition de la vue tableau change. En outre, lorsque l'utilisateur supprime le dernier emplacement (à l'exception de l'emplacement actuel) dans la vue tableau, il serait intéressant de basculer automatiquement le style de modification de la vue tableau. Ces fonctionnalités ne sont pas difficiles à implémenter, c’est pourquoi je vous les laisse comme exercice. Si vous rencontrez des problèmes ou avez des questions, n'hésitez pas à laisser un commentaire dans les commentaires ci-dessous cet article.

Conclusion

Nous avons réussi à intégrer l'API Forecast dans notre application météo. Dans le prochain tutoriel, nous allons implémenter le focus sur l'interface utilisateur et la conception de l'application.