Sådan håndteres tilgængeligheden af ​​internetforbindelsen i Swift

Oftere end ikke har mobilapplikationer brug for en aktiv internetforbindelse for at fungere korrekt. Det er dog normalt, at internetforbindelsen går tabt. I tilfælde som disse er det op til udvikleren at komme med måder at gøre oplevelsen tålelig, eller i det mindste underrette brugeren.

I denne artikel skal vi se, hvordan vi kan opdage internetforbindelsesproblemer i Swift, og nogle måder, vi kan håndtere det på.

Her er den applikationseksempel, vi skal opbygge, og hvordan den håndterer forskellige internetforbindelsesscenarier:

Krav

For at du kan følge med i denne artikel, skal du have følgende krav:

  • Xcode installeret på din maskine.
  • Kendskab til Swift programmeringssprog.
  • Cocoapods installeret på din maskine.

Når du har ovenstående krav, lad os dykke ind.

Opsætning af vores arbejdsområde

Før vi begynder, opretter vi en legeplads. Det er her, vi skriver alle vores brugssager og håndterer dem.

Swift kommer med sin egen Reachability-implementering til at opdage forbindelsesproblemer, men vi bruger et tredjepartsbibliotek. Vi gør dette, fordi det er lettere og API'en er mere udtryksfuld end den indbyggede.

Åbn Xcode og opret et nyt projekt.

Dette projekt vil være en simpel legeplads, som vi kan eksperimentere med.

For at opdage, hvornår forbindelsen går offline, bruger vi pakken Reachability.swift ****. Det er en "erstatning for Apples Reachability omskrevet i Swift med lukninger".

Åbn din terminal og kør kommandoen nedenfor:

$ pod init

Dette vil skabe et nyt, Podfilehvor vi kan erklære Cocoapods afhængigheder. Åbn Podfileog udskift indholdet med nedenstående kode:

platform :ios, '9.0'
target 'project_name' do use_frameworks! pod 'ReachabilitySwift' pod 'Alamofire'end
Du skal erstatte **project_name**med navnet på dit projekt.

Gem filen og kør kommandoen nedenfor for at installere Pods til dit projekt:

$ pod install

Når installationen er færdig, skal du åbne *.xcworkspacefilen i roden til dit projekt. Dette starter Xcode.

Oprettelse af vores Network Reachability Manager

Opret en ny NetworkManagerklasse. Denne klasse gemmer netværksstatus og er en simpel proxy til Reachabilitypakken. Indsæt koden nedenfor i filen:

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) } }]

I klassen ovenfor har vi defineret et par hjælperfunktioner, der hjælper os med at komme i gang med netværksstatusovervågning. Vi har en, sharedInstanceder er en singleton, og vi kan kalde det, hvis vi ikke vil oprette flere forekomster af NetworkManagerklassen.

I initmetoden opretter vi en forekomst af, Reachabilityog derefter registrerer vi en underretning ved hjælp af NotificationCenterklassen. Hver gang netværksstatus ændres, kaldes det tilbagekald, der er angivet af NotificationCenter(hvilket er networkStatusChanged). Vi kan bruge dette til at gøre noget globalt, der aktiveres, når netværket ikke kan nås.

Vi har defineret andre hjælperfunktioner, der generelt vil gøre kørende kode afhængigt af status på vores internetforbindelse til en leg. Vi har *isReachable*, *isUnreachable*, *isReachableViaWWAN*og *isReachableViaWiFi*.

Brugen af ​​en af ​​disse hjælpere vil generelt se sådan ud:

NetworkManager.isReachable { networkManagerInstance in print("Network is available")}
NetworkManager.isUnreachable { networkManagerInstance in print("Network is Unavailable")}
Dette er ikke en lytter til begivenheder og kører kun en gang. For at bruge en lytter til at hente netværksændringer i realtid skal du bruge NetworkManager.sharedInstance.reachability.whenReachable **. Vi viser et eksempel senere i artiklen. **

Nu hvor vi har en lederklasse, lad os se, hvordan vi kan bruge dette i en applikation.

Håndtering af netværkstilgængelighed ved applikationsstart

Nogle gange er din applikation stærkt afhængig af en internetforbindelse, og du skal registrere status ved lanceringen. Lad os se, hvordan vi kan håndtere dette ved hjælp af NetworkManagerklassen.

Create a new controller called LaunchViewController. We will treat the first controller view on the storyboard as the launch controller. We will try to detect if the user’s device is online and, if not, we will create an offline page to handle this so the user does not get into the application at all.

In the LaunchController, replace the contents with the following code:

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 ) } }}

In this class, we use our NetworkManager‘s *isUnreachable* method to fire the showOffline method when the network is unavailable. Let us create that view controller. Create a new view controller called OfflineViewController.

Open the Main.storyboard file and set the custom class of the first view to 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()}

Tilføj nu nedenstående metode til controlleren:

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

Dette vil lytte efter, når enheden går offline, og hvis det sker, vil den også showOfflinePage.

Det er alt! Vi har været i stand til at håndtere offline og online begivenheder ved hjælp af vores NetworkManager i Swift.

Konklusion

I denne artikel overvejede vi, hvordan vi kan sikre, at din applikation kan håndtere online- og offlinehændelser, når de sker. Du kan altid implementere dette som du ønsker. Hvis du har spørgsmål eller feedback, skal du lade dem være nedenfor i kommentarerne.

Kildekoden til denne legeplads er tilgængelig på GitHub.

Denne artikel blev første gang offentliggjort på Pusher.