โดย Neo Ighodaro
Mobile Applications บ่อยครั้งต้องการ Active Internet Connection เพื่อทำงานอย่างถูกต้อง เป็นเรื่องปกติที่ Internet Connection จะ Lost ในกรณีเหล่านี้ Developer ต้องหาวิธีทำให้ Experience บearable หรืออย่างน้อย Notify User
ใน Article นี้ เราจะดูวิธี Detect Internet Connection Issues ใน Swift และวิธี Handle บางอย่าง
นี่คือ Sample Application ที่เราจะสร้าง และวิธีมัน Handle Internet Connectivity Scenarios ต่างๆ:

Requirements
เพื่อ Follow Along ใน Article นี้ คุณต้องการ:
- Xcode Installed บน Machine
- Knowledge ของ Swift Programming Language
- Cocoapods Installed บน Machine
พร้อม Requirements ข้างต้นแล้ว เริ่มกันเลย
Setting up Workspace
ก่อนเริ่ม สร้าง Playground นี่คือที่เราจะเขียน Use Cases และ Handle พวกเขา
Swift มี Reachability Implementation ของตัวเองสำหรับ Detect Connection Issues แต่เราจะใช้ Third-party Library เพราะง่ายกว่าและ API Expressive กว่า Built-in
เปิด Xcode สร้าง Project ใหม่

Project นี้จะเป็น Simple Playground สำหรับ Experiment
Detect Connection Offline ใช้ Reachability.swift ** Package เป็น "Replacement for Apple's Reachability Re-written in Swift with Closures"
เปิด Terminal รัน Command:
$ pod init
สร้าง Podfile ใหม่สำหรับ Declare Cocoapods Dependencies เปิด Podfile แทนที่ Contents ด้วย Code:
platform :ios, '9.0'
target 'project_name' do
use_frameworks!
pod 'ReachabilitySwift'
pod 'Alamofire'
end
ต้อง Replace **project_name** ด้วยชื่อ Project ของคุณ
Save File รัน Command Install Pods:
$ pod install
Install Complete เปิด *.xcworkspace File ใน Root ของ Project จะ Launch Xcode
Creating Network Reachability Manager
สร้าง NetworkManager Class Class นี้ Store Network Status และเป็น Simple Proxy ไป Reachability Package ใน File Paste Code:
import Foundation
import Reachability
class NetworkManager: NSObject {
var reachability: Reachability!
static let sharedInstance: NetworkManager = { return NetworkManager() }()
override init() {
super.init()
// Initialise reachability
reachability = Reachability()!
// Register Observer สำหรับ Network Status
NotificationCenter.default.addObserver(
self,
selector: #selector(networkStatusChanged(_:)),
name: .reachabilityChanged,
object: reachability
)
do {
// Start 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 {
try (NetworkManager.sharedInstance.reachability).startNotifier()
} catch {
print("Error stopping notifier")
}
}
// Network Reachable
static func isReachable(completed: @escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection != .none {
completed(NetworkManager.sharedInstance)
}
}
// Network Unreachable
static func isUnreachable(completed: @escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection == .none {
completed(NetworkManager.sharedInstance)
}
}
// Network Reachable via WWAN/Cellular
static func isReachableViaWWAN(completed: @escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection == .cellular {
completed(NetworkManager.sharedInstance)
}
}
// Network Reachable via WiFi
static func isReachableViaWiFi(completed: @escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection == .wifi {
completed(NetworkManager.sharedInstance)
}
}
}
ใน Class ข้างต้น เรา Define Helper Functions สำหรับ Network Status Monitoring มี sharedInstance เป็น Singleton เรียกได้ถ้าไม่สร้าง Multiple Instances ของ NetworkManager
ใน init Method สร้าง Instance ของ Reachability Register Notification ด้วย NotificationCenter Class ตอนนี้ทุกครั้ง Network Status เปลี่ยน Callback ที่ NotificationCenter ระบุ (คือ networkStatusChanged) จะถูก Call ใช้ทำ Global Action เมื่อ Network Unreachable
Define Helper Functions อื่นๆ ทำให้รัน Code ขึ้นอยู่กับ Internet Connection Status ง่ายขึ้น มี isReachable, isUnreachable, isReachableViaWWAN และ isReachableViaWiFi
Usage ของ Helper จะประมาณนี้:
NetworkManager.isReachable { networkManagerInstance in
print("Network is available")
}
NetworkManager.isUnreachable { networkManagerInstance in
print("Network is Unavailable")
}
นี่ไม่ใช่ Event Listener จะ Run แค่ครั้งเดียว ถ้าอยาก Listener Pick Up Network Changes Real-time ต้องใช้ NetworkManager.sharedInstance.reachability.whenReachable จะแสดงตัวอย่างใน Article ต่อไป
ตอนนี้มี Manager Class แล้ว ดูวิธีใช้ใน Application
Handling Network Availability on Application Launch
บางครั้ง Application ขึ้นอยู่กับ Internet Connection มาก ต้อง Detect Status on Launch ดูวิธี Handle ด้วย NetworkManager Class
สร้าง LaunchViewController ใหม่ จะ Treat First Controller View บน Storyboard เป็น Launch Controller จะ Detectว่า Device Online ถ้าไม่ Online สร้าง Offline Page Handle เพื่อ User ไม่เข้า App เลย
ใน LaunchController แทนที่ Contents ด้วย 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
)
}
}
}
ใน Class นี้ ใช้ NetworkManager isUnreachable Method Fire showOffline Method เมื่อ Network Unavailable สร้าง View Controller นั้น สร้าง OfflineViewController ใหม่
เปิด Main.storyboard Set Custom Class ของ First View เป็น LaunchViewController
สร้าง View Controller ใหม่ใน Storyboard Set OfflineViewController เป็น Custom Class สร้าง Manual Segue ชื่อ NetworkUnavailable ระหว่าง New View Controller และ LaunchViewController เสร็จแล้วควรประมาณนี้:

Run Application Note: Development Machine ควร Offline เพราะ iOS Simulator ใช้ Internet ของ Machine Run แล้วควรได้ Offline Page ที่สร้าง
สร้าง View Controller ที่แสดงเมื่อมี Connection
Handling Events When Device Comes Online
ตอนนี้มี Offline View Controller ทำงานเมื่อ Offline มา Handle เมื่อ Device Online กลับมา
สร้าง Navigation View Controller ใหม่ใน Storyboard ใต้ Offline View Controller สร้าง Controller แสดง Latest Reddit Posts สร้าง PostsTableViewController Class ใหม่ Set เป็น Custom Class สำหรับ View Controller ที่ Attached กับ Navigation View Controller
สร้าง Manual Segue ชื่อ MainController จาก Navigation View Controller ไป Launch View Controller และ Offline View Controller ควรประมาณนี้:

เปิด LaunchViewController Class Bottom ของ viewDidLoad Method เพิ่ม:
NetworkManager.isReachable { _ in self.showMainPage() }
เพิ่ม Method ด้านล่างใน Controller:
private func showMainPage() -> Void {
DispatchQueue.main.async {
self.performSegue(
withIdentifier: "MainController",
sender: self
)
}
}
นี่ทำให้ App Launch ตรวจสอบ Connectivity ถ้า Available Present PostsTableViewController ไม่ใช่ Present OfflineViewController
แต่ถ้า User Hit OfflineViewController แล้ว Network Online กลับมา? Handle Scenario นี้
เปิด OfflineViewController แทนที่ Code ด้วย:
import UIKit
class OfflineViewController: UIViewController {
let network = NetworkManager.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
// If Network Reachable Show 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)
}
}
}
ใน Controller ข้างบน ใน viewDidLoad Set whenReachable Completion Show Main Controller หมายถึงตราบใดที่ Offline Watch เมื่อ Device Online กลับมา เมื่อเกิด Present PostsTableViewController
Override viewWillAppear และ viewWillDisappear ให้แน่ใจว่า Navigation Bar ไม่แสดงบน Offline View Controller
Fetching Posts from Reddit API ใน Swift
เพิ่ม Logic Fetch Data จาก Reddit แสดงบน PostsTableViewController เปิด File แทนที่ Contents:
import UIKit
import 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 Posts แล้ว Reload Table
fetchPosts { posts in
self.posts = posts
self.tableView.reloadData()
}
}
private func fetchPosts(completion: @escaping (_ posts: [RedditPost]) -> Void) -> Void {
// Send Request ไป Reddit API
Alamofire.request("https://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 Reddit Posts Assign Post ไป 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
}
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
}
}
ใน fetchPosts Method ใช้ Alamofire ส่ง GET Request ไป Reddit API Parse Response เพิ่มใน RedditPost Struct ที่สร้าง Top of File ทำให้ Data ที่ Pass ไป tableView Consistent
Handling Events When Device Goes Offline
Handle Scenario อีกอย่าง: ระหว่าง View Reddit Posts Lost Connectivity ทำไง? แสดง Offline Page อีกครั้ง
เหมือนเดิม สร้าง Manual Segue NetworkUnavailable จาก PostsTableViewController ไป OfflineViewController เพิ่ม Code Bottom ของ viewDidLoad:
network.reachability.whenUnreachable = { reachability in
self.showOfflinePage()
}
เพิ่ม Method ด้านล่างใน Controller:
private func showOfflinePage() -> Void {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "NetworkUnavailable", sender: self)
}
}
นี่ Listen เมื่อ Device Offline ถ้าเกิดจะ showOfflinePage
That's all! เรา Handle Offline และ Online Events ด้วย NetworkManager ใน Swift ได้แล้ว
Conclusion
ใน Article นี้ เราพิจารณาวิธีทำให้ Application Handle Online/Offline Events เมื่อเกิดขึ้น สามารถ Implement แบบไหนก็ได้ หากมี Questions หรือ Feedback Comment ได้ด้านล่าง
Source Code ของ Playground นี้อยู่ที่ GitHub
Article นี้เผยแพร่ครั้งแรกบน Pusher
เรียนรู้เขียนโค้ดฟรี หลักสูตรโอเพนซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานเป็นนักพัฒนา เริ่มต้นได้เลย