Wednesday, October 4, 2017

Kiểm tra kết nối mạng Swift

Bạn có ứng dụng yêu cầu kết nối mạng, để trước khi vào phần dùng mạng khỏi bị lỗi, ta sẽ kiểm tra xem đã có kết nối mạng chưa, nếu có mới cho sang.
Tạo một class có tên Reachability, copy toàn bộ code dưới đây vào.
/*Copyright (c) 2014, Ashley Mills
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 1. Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */
import Foundation
import SystemConfiguration
//import Foundation
publicenum ReachabilityError: ErrorType {
case FailedToCreateWithAddress(sockaddr_in)
case FailedToCreateWithHostname(String)
case UnableToSetCallback
case UnableToSetDispatchQueue
}

publiclet ReachabilityChangedNotification = "ReachabilityChangedNotification"

func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
let reachability = Unmanaged<Reachability>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()

dispatch_async(dispatch_get_main_queue()) {
        reachability.reachabilityChanged(flags)
    }
}

publicclass Reachability: NSObject {
publictypealias NetworkReachable = (Reachability) -> ()
publictypealias NetworkUnreachable = (Reachability) -> ()

publicenum NetworkStatus: CustomStringConvertible {

case NotReachable, ReachableViaWiFi, ReachableViaWWAN

publicvar description: String {
switchself {
case .ReachableViaWWAN:
return"Cellular"
case .ReachableViaWiFi:
return"WiFi"
case .NotReachable:
return"No Connection"
            }
        }
    }

// MARK: - *** Public properties ***
publicvar whenReachable: NetworkReachable?
publicvar whenUnreachable: NetworkUnreachable?
publicvar reachableOnWWAN: Bool
publicvar notificationCenter = NSNotificationCenter.defaultCenter()

publicvar currentReachabilityStatus: NetworkStatus {
ifisReachable() {
ifisReachableViaWiFi() {
return .ReachableViaWiFi
            }
ifisRunningOnDevice {
return .ReachableViaWWAN
            }
        }
return .NotReachable
    }

publicvar currentReachabilityString: String {
return"\(currentReachabilityStatus)"
    }

privatevar previousFlags: SCNetworkReachabilityFlags?

// MARK: - *** Initialisation methods ***

requiredpublicinit(reachabilityRef: SCNetworkReachability) {
reachableOnWWAN = true
self.reachabilityRef = reachabilityRef
    }

publicconvenienceinit(hostname: String) throws {

let nodename = (hostname asNSString).UTF8String
guardlet ref = SCNetworkReachabilityCreateWithName(nil, nodename) else { throwReachabilityError.FailedToCreateWithHostname(hostname) }

self.init(reachabilityRef: ref)
    }

publicclassfunc reachabilityForInternetConnection() throws ->Reachability {

var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

guardlet ref = withUnsafePointer(&zeroAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else { throwReachabilityError.FailedToCreateWithAddress(zeroAddress) }

returnReachability(reachabilityRef: ref)
    }

publicclassfunc reachabilityForLocalWiFi() throws ->Reachability {

var localWifiAddress: sockaddr_in = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
        localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress))
        localWifiAddress.sin_family = sa_family_t(AF_INET)

// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
let address: UInt32 = 0xA9FE0000
        localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian)

guardlet ref = withUnsafePointer(&localWifiAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else { throwReachabilityError.FailedToCreateWithAddress(localWifiAddress) }

returnReachability(reachabilityRef: ref)
    }

// MARK: - *** Notifier methods ***
publicfunc startNotifier() throws {

guard !notifierRunningelse { return }

var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())

if !SCNetworkReachabilitySetCallback(reachabilityRef!, callback, &context) {
stopNotifier()
throwReachabilityError.UnableToSetCallback
        }

if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) {
stopNotifier()
throwReachabilityError.UnableToSetDispatchQueue
        }

// Perform an intial check
dispatch_async(reachabilitySerialQueue) { () ->Voidin
let flags = self.reachabilityFlags
self.reachabilityChanged(flags)
        }

notifierRunning = true
    }

publicfunc stopNotifier() {
defer { notifierRunning = false }
guardlet reachabilityRef = reachabilityRefelse { return }

SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
    }

// MARK: - *** Connection test methods ***
publicfunc isReachable() ->Bool {
let flags = reachabilityFlags
returnisReachableWithFlags(flags)
    }

publicfunc isReachableViaWWAN() ->Bool {

let flags = reachabilityFlags

// Check we're not on the simulator, we're REACHABLE and check we're on WWAN
returnisRunningOnDevice&&isReachable(flags) &&isOnWWAN(flags)
    }

publicfunc isReachableViaWiFi() ->Bool {

let flags = reachabilityFlags

// Check we're reachable
if !isReachable(flags) {
returnfalse
        }

// Must be on WiFi if reachable but not on an iOS device (i.e. simulator)
if !isRunningOnDevice {
returntrue
        }

// Check we're NOT on WWAN
return !isOnWWAN(flags)
    }

// MARK: - *** Private methods ***
privatevar isRunningOnDevice: Bool = {
#if (arch(i386) || arch(x86_64)) &&os(iOS)
returnfalse
#else
returntrue
#endif
    }()

privatevar notifierRunning = false
privatevar reachabilityRef: SCNetworkReachability?
privatelet reachabilitySerialQueue = dispatch_queue_create("uk.co.ashleymills.reachability", DISPATCH_QUEUE_SERIAL)

privatefunc reachabilityChanged(flags: SCNetworkReachabilityFlags) {

guardpreviousFlags != flags else { return }

ifisReachableWithFlags(flags) {
iflet block = whenReachable {
                block(self)
            }
        } else {
iflet block = whenUnreachable {
                block(self)
            }
        }

notificationCenter.postNotificationName(ReachabilityChangedNotification, object:self)

previousFlags = flags
    }

privatefunc isReachableWithFlags(flags: SCNetworkReachabilityFlags) ->Bool {

if !isReachable(flags) {
returnfalse
        }

ifisConnectionRequiredOrTransient(flags) {
returnfalse
        }

ifisRunningOnDevice {
ifisOnWWAN(flags) && !reachableOnWWAN {
// We don't want to connect when on 3G.
returnfalse
            }
        }

returntrue
    }

// WWAN may be available, but not active until a connection has been established.
// WiFi may require a connection for VPN on Demand.
privatefunc isConnectionRequired() ->Bool {
returnconnectionRequired()
    }

privatefunc connectionRequired() ->Bool {
let flags = reachabilityFlags
returnisConnectionRequired(flags)
    }

// Dynamic, on demand connection?
privatefunc isConnectionOnDemand() ->Bool {
let flags = reachabilityFlags
returnisConnectionRequired(flags) &&isConnectionOnTrafficOrDemand(flags)
    }

// Is user intervention required?
privatefunc isInterventionRequired() ->Bool {
let flags = reachabilityFlags
returnisConnectionRequired(flags) &&isInterventionRequired(flags)
    }

privatefunc isOnWWAN(flags: SCNetworkReachabilityFlags) ->Bool {
#ifos(iOS)
return flags.contains(.IsWWAN)
#else
returnfalse
#endif
    }
privatefunc isReachable(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.Reachable)
    }
privatefunc isConnectionRequired(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.ConnectionRequired)
    }
privatefunc isInterventionRequired(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.InterventionRequired)
    }
privatefunc isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.ConnectionOnTraffic)
    }
privatefunc isConnectionOnDemand(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.ConnectionOnDemand)
    }
func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) ->Bool {
return !flags.intersect([.ConnectionOnTraffic, .ConnectionOnDemand]).isEmpty
    }
privatefunc isTransientConnection(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.TransientConnection)
    }
privatefunc isLocalAddress(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.IsLocalAddress)
    }
privatefunc isDirect(flags: SCNetworkReachabilityFlags) ->Bool {
return flags.contains(.IsDirect)
    }
privatefunc isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) ->Bool {
let testcase:SCNetworkReachabilityFlags = [.ConnectionRequired, .TransientConnection]
return flags.intersect(testcase) == testcase
    }

privatevar reachabilityFlags: SCNetworkReachabilityFlags {

guardlet reachabilityRef = reachabilityRefelse { returnSCNetworkReachabilityFlags() }

var flags = SCNetworkReachabilityFlags()
let gotFlags = withUnsafeMutablePointer(&flags) {
SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0))
        }

if gotFlags {
return flags
        } else {
returnSCNetworkReachabilityFlags()
        }
    }

overridepublicvar description: String {

var W: String
ifisRunningOnDevice {
            W = isOnWWAN(reachabilityFlags) ? "W" : "-"
        } else {
            W = "X"
        }
let R = isReachable(reachabilityFlags) ? "R" : "-"
let c = isConnectionRequired(reachabilityFlags) ? "c" : "-"
let t = isTransientConnection(reachabilityFlags) ? "t" : "-"
let i = isInterventionRequired(reachabilityFlags) ? "i" : "-"
let C = isConnectionOnTraffic(reachabilityFlags) ? "C" : "-"
let D = isConnectionOnDemand(reachabilityFlags) ? "D" : "-"
let l = isLocalAddress(reachabilityFlags) ? "l" : "-"
let d = isDirect(reachabilityFlags) ? "d" : "-"

return"\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
    }

deinit {
stopNotifier()

reachabilityRef = nil
whenReachable = nil
whenUnreachable = nil
    }
}
Đây là đoạn code free trên mạng, người public yêu cầu giữ nguyên đoạn comment mô tả trên cùng. Bạn đọc thử xem các lập trình viên chuyên nghiệp bài bản thế nào khi cho code free.
Tiếp theo trong class mà bạn chuẩn bị cho sử dụng kết nối mạng, giả sử bạn sẽ bật sang class second cần dùng mạng, trước đó sẽ kiểm tra, nếu không có mạng hiện Dialog thông báo, nếu có cho sang.
Hàm đó cho nút bấm nào đó như sau.
func next(sender: UIButton){
do {
let reachability: Reachability = tryReachability.reachabilityForInternetConnection()
let networkStatus: Int = reachability.currentReachabilityStatus.hashValue
if networkStatus == 0{
var alert = UIAlertView(title: "No Internet Connection", message: "Bật 3G hoặc wifi.", delegate: nil, cancelButtonTitle: "OK")
                alert.show()

            }
else{
let vc = second()
self.presentViewController(vc, animated: true, completion: nil)
        }
  }
catch {
// Handle error
        }
    }
Sau đó bạn set hàm cho nút bấm trong viewDidLoad là được.


No comments:

Post a Comment