Comment gérer l'accessibilité de la connexion Internet dans Swift

Le plus souvent, les applications mobiles ont besoin d'une connexion Internet active pour fonctionner correctement. Il est cependant normal que la connexion Internet soit perdue. Dans de tels cas, il appartient au développeur de trouver des moyens de rendre l'expérience supportable, ou du moins d'en informer l'utilisateur.

Dans cet article, nous allons voir comment nous pouvons détecter les problèmes de connexion Internet dans Swift et comment les gérer.

Voici l'exemple d'application que nous allons créer et comment il gère différents scénarios de connectivité Internet:

Exigences

Pour que vous puissiez suivre cet article, vous aurez besoin des conditions suivantes:

  • Xcode installé sur votre machine.
  • Connaissance du langage de programmation Swift.
  • Cocoapods installés sur votre machine.

Lorsque vous avez les exigences ci-dessus, plongeons-nous.

Mise en place de notre espace de travail

Avant de commencer, nous allons créer une aire de jeux. C'est ici que nous écrirons tous nos cas d'utilisation et les traiterons.

Swift est livré avec sa propre implémentation d'accessibilité pour détecter les problèmes de connexion, mais nous utiliserons une bibliothèque tierce. Nous faisons cela parce que c'est plus facile et que l'API est plus expressive que celle intégrée.

Ouvrez Xcode et configurez un nouveau projet.

Ce projet sera un simple terrain de jeu que nous pourrons expérimenter.

Pour détecter quand la connexion se déconnecte, nous allons utiliser le package Reachability.swift ****. Il s'agit d'un «remplacement de l'accessibilité d'Apple réécrit en Swift avec des fermetures».

Ouvrez votre terminal et exécutez la commande ci-dessous:

$ pod init

Cela créera un nouveau Podfileoù nous pourrons déclarer les dépendances Cocoapods. Ouvrez le Podfileet remplacez le contenu par le code ci-dessous:

platform :ios, '9.0'
target 'project_name' do use_frameworks! pod 'ReachabilitySwift' pod 'Alamofire'end
Vous devez remplacer **project_name**par le nom de votre projet.

Enregistrez le fichier et exécutez la commande ci-dessous pour installer les pods dans votre projet:

$ pod install

Une fois l'installation terminée, ouvrez le *.xcworkspacefichier à la racine de votre projet. Cela lancera Xcode.

Création de notre gestionnaire d'accessibilité réseau

Créez une nouvelle NetworkManagerclasse. Cette classe stockera l'état du réseau et sera un simple proxy du Reachabilitypackage. Dans le fichier, collez le code ci-dessous:

import Foundationimport Reachability
class NetworkManager: NSObject {
 var reachability: Reachability!
 static let sharedInstance: NetworkManager = { return NetworkManager() }()
 override init() { super.init()
 // Initialise reachability reachability = Reachability()!
 // Register an observer for the network status NotificationCenter.default.addObserver( self, selector: #selector(networkStatusChanged(_:)), name: .reachabilityChanged, object: reachability )
 do { // Start the network status notifier try reachability.startNotifier() } catch { print("Unable to start notifier") } }
 @objc func networkStatusChanged(_ notification: Notification) { // Do something globally here! }
 static func stopNotifier() -> Void { do { // Stop the network status notifier try (NetworkManager.sharedInstance.reachability).startNotifier() } catch { print("Error stopping notifier") } }
 // Network is reachable static func isReachable(completed: @escaping (NetworkManager) -> Void) { if (NetworkManager.sharedInstance.reachability).connection != .none { completed(NetworkManager.sharedInstance) } }
 // Network is unreachable static func isUnreachable(completed: @escaping (NetworkManager) -> Void) { if (NetworkManager.sharedInstance.reachability).connection == .none { completed(NetworkManager.sharedInstance) } }
 // Network is reachable via WWAN/Cellular static func isReachableViaWWAN(completed: @escaping (NetworkManager) -> Void) { if (NetworkManager.sharedInstance.reachability).connection == .cellular { completed(NetworkManager.sharedInstance) } }
 // Network is reachable via WiFi static func isReachableViaWiFi(completed: @escaping (NetworkManager) -> Void) { if (NetworkManager.sharedInstance.reachability).connection == .wifi { completed(NetworkManager.sharedInstance) } }]

Dans la classe ci-dessus, nous avons défini quelques fonctions d'assistance qui nous aideront à démarrer avec la surveillance de l'état du réseau. Nous avons un sharedInstancequi est un singleton et nous pouvons l'appeler si nous ne voulons pas créer plusieurs instances de la NetworkManagerclasse.

Dans la initméthode, nous créons une instance de Reachability, puis nous enregistrons une notification à l'aide de la NotificationCenterclasse. Désormais, chaque fois que l'état du réseau change, le rappel spécifié par NotificationCenter(qui est networkStatusChanged) sera appelé. Nous pouvons l'utiliser pour faire quelque chose de global qui est activé lorsque le réseau est inaccessible.

Nous avons défini d'autres fonctions d'assistance qui rendront généralement l'exécution du code, en fonction de l'état de notre connexion Internet, un jeu d'enfant. Nous avons *isReachable*, *isUnreachable*, *isReachableViaWWAN*et *isReachableViaWiFi*.

L'utilisation de l'un de ces helpers ressemblera généralement à ceci:

NetworkManager.isReachable { networkManagerInstance in print("Network is available")}
NetworkManager.isUnreachable { networkManagerInstance in print("Network is Unavailable")}
Ce n'est pas un écouteur d'événements et ne s'exécutera qu'une seule fois. Pour utiliser un écouteur pour détecter les modifications du réseau en temps réel, vous devez utiliserNetworkManager.sharedInstance.reachability.whenReachable **. Nous montrerons un exemple plus loin dans l'article. **

Maintenant que nous avons une classe de gestionnaire, voyons comment nous pouvons l'utiliser dans une application.

Gestion de la disponibilité du réseau au lancement de l'application

Parfois, votre application repose fortement sur une connexion Internet et vous devez détecter l'état au lancement. Voyons comment nous pouvons gérer cela en utilisant la NetworkManagerclasse.

Créez un nouveau contrôleur appelé LaunchViewController. Nous traiterons la première vue du contrôleur sur le storyboard comme le contrôleur de lancement. Nous essaierons de détecter si l'appareil de l'utilisateur est en ligne et, sinon, nous créerons une page hors ligne pour gérer cela afin que l'utilisateur n'entre pas du tout dans l'application.

Dans le LaunchController, remplacez le contenu par le code suivant:

import UIKit
class LaunchViewController: UIViewController { let network: NetworkManager = NetworkManager.sharedInstance
 override func viewDidLoad() { super.viewDidLoad()
 NetworkManager.isUnreachable { _ in self.showOfflinePage() } }
 private func showOfflinePage() -> Void { DispatchQueue.main.async { self.performSegue( withIdentifier: "NetworkUnavailable", sender: self ) } }}

Dans cette classe, nous utilisons NetworkManagerla *isUnreachable*méthode de notre pour lancer la showOfflineméthode lorsque le réseau n'est pas disponible. Créons ce contrôleur de vue. Créez un nouveau contrôleur de vue appelé OfflineViewController.

Ouvrez le Main.storyboardfichier et définissez la classe personnalisée de la première vue sur LaunchViewController.

Next, create a new view controller in the storyboard. Set the OfflineViewController as the custom class for this new view controller. Now create a manual segue called NetworkUnavailable between the new view controller and the LaunchViewController. When you are done you should have something similar to this:

Now let’s run the application. Note, though, that before you run your application, your development machine should be offline as the iOS simulator uses the machine’s internet connection. When you run the application, you should get the offline page we created.

Now let us create a view controller that shows up when there is a connection.

Handling Events When the Device Comes Online

Now that we have created an Offline View Controller and it works when the device is offline, let us handle what happens when the device is back online.

Create a new navigation view controller on the storyboard below the Offline View Controller. We will create a controller that displays the latest Reddit posts. Create a new view controller class called PostsTableViewController. Now make this the custom class for the view controller attached to the Navigation View Controller.

Now create a manual segue called MainController from the Navigation View Controller to the Launch View Controller and the Offline View Controller. You should have something similar to this:

Now, open the LaunchViewController class and at the bottom of the viewDidLoad method add the following:

NetworkManager.isReachable { _ in self.showMainPage()}

Then add the method below to the controller:

private func showMainPage() -> Void { DispatchQueue.main.async { self.performSegue( withIdentifier: "MainController", sender: self ) }}

This will make sure that when the app is launched, it will check for connectivity and then, if the connection is available, it will present the PostsTableViewController. Otherwise it will present the OfflineViewController.

Great! But what happens when the user has hit the OfflineViewController and then the network comes back online? Let’s handle that scenario.

Open the OfflineViewController and replace the code with the code below:

import UIKit 
class OfflineViewController: UIViewController { let network = NetworkManager.sharedInstance
 override func viewDidLoad() { super.viewDidLoad()
 // If the network is reachable show the main controller network.reachability.whenReachable = { _ in self.showMainController() } }
 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated)
 navigationController?.setNavigationBarHidden(true, animated: animated) }
 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated)
 navigationController?.setNavigationBarHidden(false, animated: animated) }
 private func showMainController() -> Void { DispatchQueue.main.async { self.performSegue(withIdentifier: "MainController", sender: self) } }}

In the controller above, you can see, in the viewDidLoad method, that we set the whenReachable completion to show the main controller. This means that, as long as its offline, you watch for when the device comes back online. When it does, present the PostsTableViewController.

We also override the viewWillAppear and viewWillDisappear methods to ensure the navigation bar does not show on the Offline View Controller.

Fetching Posts from Reddit API in Swift

Now let us add the logic that’ll fetch data from Reddit and display on our PostsTableViewController. Open the file and replace the contents with the code below:

import UIKitimport Alamofire
struct RedditPost { let title: String! let subreddit: String!}
class PostsTableViewController: UITableViewController { var posts = [RedditPost]()
 let network = NetworkManager.sharedInstance
 override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Latest Posts"
 // Fetch the posts and then reload the table fetchPosts { posts in self.posts = posts self.tableView.reloadData() } }
 private func fetchPosts(completion: @escaping (_ posts: [RedditPost]) -> Void) -> Void { // Send a request to the Reddit API Alamofire.request("//api.reddit.com").validate().responseJSON { response in switch response.result { case .success(let JSON): let data = JSON as! [String:AnyObject] guard let children = data["data"]!["children"] as? [AnyObject] else { return } var posts = [RedditPost]()
 // Loop through the Reddit posts and then assign a post to the posts array for child in 0...children.count-1 { let post = children[child]["data"] as! [String: AnyObject]
 posts.append(RedditPost( title: post["title"] as! String, subreddit: "/r/" + (post["subreddit"] as! String) )) }
 DispatchQueue.main.async { completion(posts) } case .failure(let error): print(error) } } }
 override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() }
 // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { return 1 }
 // Return the number of posts available override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.posts.count }
 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) let post = posts[indexPath.row] as RedditPost cell.textLabel?.text = post.title cell.detailTextLabel?.text = post.subreddit return cell }}

In the fetchPosts method, we use Alamofire to send a GET request to the Reddit API. We then parse the response and add it to the RedditPost struct we created at the top of the file. This makes the data we are passing to the tableView consistent.

Handling Events when the Device Goes Offline

Now, let us handle one more scenario. Imagine while viewing the latest Reddit posts, you lose connectivity. What happens? Let’s show the offline page again when that happens.

As was previously done, create a manual segue called NetworkUnavailable from the PostsTableViewController to the OfflineViewController. Now add this code to the bottom of the viewDidLoad method:

network.reachability.whenUnreachable = { reachability in self.showOfflinePage()}

Now add the method below to the controller:

private func showOfflinePage() -> Void { DispatchQueue.main.async { self.performSegue(withIdentifier: "NetworkUnavailable", sender: self) }}

This will listen for when the device goes offline and, if that happens, it will showOfflinePage.

That’s all! We have been able to handle offline and online events using our NetworkManager in Swift.

Conclusion

In this article, we considered how to make sure your application can handle online and offline events when they happen. You can always implement this any way you wish. If you have any questions or feedback, leave them below in the comments.

The source code to this playground is available on GitHub.

This article was first published on Pusher.