IOS Reachibility in whole app and just on on view load - ios

I've added the following code to the viewDidLoad of my view controller:
let reachability: Reachability
do {
reachability = try Reachability.reachabilityForInternetConnection()
} catch {
print("Unable to create Reachability")
return
}
reachability.whenReachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
dispatch_async(dispatch_get_main_queue()) {
if reachability.isReachableViaWiFi() {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
}
}
reachability.whenUnreachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
dispatch_async(dispatch_get_main_queue()) {
print("Not reachable")
}
}
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
It is notifying me of when the view loads however not when the state changes after the load. I'm wondering how to expand this so that I can have a notification anywhere in the application an not just on when the view loads as the internet may come back after a view loads.
Thanks

You should declare the reachability variable as instance variable and not only as local variable within a function. You could further use notifications to make the information available across your app. Just make sure the instance containing the reachability variable lives during the lifecycle of your app.

start reachability in AppDelegate and make it notify all registered observers creating and posting a notification, so you can register every controller you need, as "BostonMacOSX" suggested.

I recommend you create something along the lines of a connectivity controller that manages all reachability related events & holds the instance itself. Then this controller can fire events to all listening view controllers about changes in internet connectivity. Initialize and start the reachability notifier in your app delegate after didFinishLaunchingWithOptions

Related

Send data created while the user offline

I want to allow users to create some items while they are offline. then send created items to backend when the user reconnect to internet.I am confused, What's the proper way to achieve that?
Should I use waitsforconnectivity of URLSession and it will send the request even when the user close the app
Or should I schedule a background task? if so then how to trigger this task when user connect to the internet?
Notes: I am using Alamofire for networking
I think you're overcomplicating this.
If you're using Alamofire for networking then I wouldn't suggest the first approach as that would be mixing the usage of URLSession and Alamofire for networking and that's not a great idea.
In terms of your second approach. Why does it need to be a background task? Why can't you just check if the user is first connected to the internet, and if they are you can proceed normally. If not, then just create items and cache them somehow. Then when you reconnect to the internet you can send the cached items first as you would send normal items.
Alamofire has a built in NetworkReachabilityManager which will help you determine your network status. There's a nice example in this answer for using it.
You can use Alomafire itself do that:
NetworkManager
class NetworkManager {
//shared instance
static let shared = NetworkManager()
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.google.com")
func startNetworkReachabilityObserver() {
reachabilityManager?.listener = { status in
switch status {
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.wwan):
print("The network is reachable over the WWAN connection")
}
}
// start listening
reachabilityManager?.startListening()
}
}
Reachability Observer
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// add network reachability observer on app start
NetworkManager.shared.startNetworkReachabilityObserver()
return true
}
}

Firebase Connection manager should return only one result

I am following documentation located at: https://www.firebase.com/docs/ios/guide/offline-capabilities.html#section-connection-state
However, my implementation and test of:
connectionCheck.observeEventType(.Value, withBlock: { snapshot in
let connected = snapshot.value as? Bool
if connected != nil && connected! {
print("Connected")
} else {
print("Not connected")
}
})
The output in Xcode notes:
Not connected
Connected
If however I turn off the wifi, the result is simply:
Not connected
Given I wish to allow actions to occur and present to the user if there is not a connection, how can I ensure that this Firebase listener only returns the correct response once?
As a suggestion, you may want to add some additional functionality;
If you want to know about the users connection to Firebase, you should observe the .info/connected path, as stated in the docs.
The design pattern I use is to set up a global app variable called isConnected, and attach a Firebase observer to the .info/connected path.
Then in my app wherever I need to know if the user is connected or not, I attach a KVO Observer to my global isConnected var.
When the user disconnects (or connects), the Firebase observer is called which sets isConnected = false (or true).
Then, any places in my app that are observing the isConnected var is notified of the disconnect/connect and can take appropriate action.
Here's the code
let connectedRef = rootRef.childByAppendingPath(".info/connected")
connectedRef.observeEventType(.Value, withBlock: { snapshot in
self.isConnected = snapshot.value as! Bool //KVO property
//this is just so you can see it's being set, remove
if ( self.isConnected == true ) {
print("connected")
} else {
print("not connected")
}
// testing code
})

Apple Watch Background Mode?

I am developing apple watch application. when i run the app it is working fine. Now my problem is when the app goes to background mode, the app on the apple watch app will closing automatically. I am writing small code in iPhone app:
func viewDidLoad() {
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
// In your WatchKit extension, the value of this property is true when the paired iPhone is reachable via Bluetooth.
// On iOS, the value is true when the paired Apple Watch is reachable via Bluetooth and the associated Watch app is running in the foreground.
// In all other cases, the value is false.
if session.reachable {
lblStatus.text = "Reachable"
}
else
{
lblStatus.text = "Not Reachable"
}
func sessionReachabilityDidChange(session: WCSession)
{
if session.reachable {
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.text = "Reachable"
})
}
else
{
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.text = "Not Reachable"
})
}
}
}
}
in WatchExtention Code is
func someFunc() {
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
if session.reachable {
ispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Reachable")
})
}
else
{
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Not Reachable")
})
}
func sessionReachabilityDidChange(session: WCSession)
{
if session.reachable {
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Reachable")
})
}
else
{
dispatch_async(dispatch_get_main_queue(), {
self.lblStatus.setText("Not Reachable")
})
}
}
}
}
Now when enter to background in apple Watch the iPhone app showing Not reachable why ?
It's the default behavior of the AppleWatch, mainly to spare with resources like battery.
session.reachable property is true only when Apple Watch is reachable via Bluetooth and the associated Watch app is running in the
foreground in all other cases, the value is false.
In your case the second option which caused the problem I suppose the bluetooth connection is working.
Anyway the question is what do you like to reach.
Actually the simple rule is that you couldn't wake up the Watch from the iPhone but you could wake up the iPhone app from the Watch.
Two ways with three options to reach the watch when it's counterpart is in the background: send a complication update or send a message (2 options) in the background which will be available for the Watch when it will awake again.
All of them are part of the WCSession Class.
The two options for sending messages are:
- updateApplicationContext:error:
You can use this method to transfer a dictionary of data to the counterpart Watch app.iPhone sends context data when the opportunity arises, means when the Watch app arises.The counterpart’s session on the Watch gets the data with the session:didReceiveUpdate: method or from the receivedApplicationContext property.
You may call this method when the watch is not currently reachable.
The other option is sending data in the background like
- transferUserInfo:
You can use this method when you want to send a dictionary of data to the Watch and ensure that it is delivered. Dictionaries sent using this method are queued on the other device and delivered in the order in which they were sent. After a transfer begins, the transfer operation continues even if the app is suspended.
BUT true for both methods that they can only be called while the session is active. Calling any of these methods for an inactive or deactivated session is a programmer error.
The complication solution is a little bit different but belongs to the same WCSession Class as the earliers.
-transferCurrentComplicationUserInfo:
This method is specifically designed for transferring complication user info to the watch with the aim to be shown on the watch face immediately.
Of course it's available only for iOS, and using of this method counts against your complication’s time budget, so it's availability is limited.
The complication user info is placed at the front of the queue, so the watch wakes up the extension in the background to receive the info, and then the transfer happens immediately.
All messages received by your watch app are delivered to the session delegate serially on a background thread, so you have to switch to the main queue in case you'd like to use or presenting them for UI.
The WWDC talk on WatchConnectivity discusses "reachability" and its nuances in quite a lot of detail, so you should definitely give it a watch.
TL;DR: reachable on the watch is for the most part only going to be true/YES when the watch app's UI is visible on the screen.

how to block ios swift when no network, any best practice?

I want to block my app's ui when there is no network connectivity.
How can I do this?
Creating a blocking wide-as-screen transparent view
move it to the front when needed to block ui touches?
move it to the rear when network is back?
Is there a best UX practice for this backed up in swift implementation?
Instead of doing all that you can just disable user interaction for that particular view. Like below
[self.view setUserInteractionEnabled:NO];
self.view.userInteractionEnabled = false //swift implementation
This will disable user interaction for all subviews of that view
If code that handles internet connection is not in currently shown view controller
UIApplication.sharedApplication().keyWindow.rootViewController.view.userInteractionEnabled = false
It's not ideal but I have solution based on Nanayakkara project. AppDelegate creates MyConnectionManager which is observed on networkStatusChanged selector:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("networkStatusChanged:"), name: ReachabilityStatusChangedNotification, object: nil)
Reach().monitorReachabilityChanges()
Each time connection state was changed manager calls networkStatusChanged and checks if connection is lost & top view isn't special connection view with message like "Please check your internet connection". If it isn't manager retrieves topController from sharedApplication
func topController() -> UIViewController? {
if var topRootController =
UIApplication.sharedApplication().keyWindow?.rootViewController {
while((topRootController.presentedViewController) != nil) {
topRootController = topRootController.presentedViewController!
}
return topRootController
}
return nil
}
and calls presentViewController with ConnectionViewController.

How to check when ios device just lost connection to internet

how to detect losing connection to network as fast as it can be
now im using reachabitity framework for swift and it takes abouut 3 sec to detect that there is no network so user can make the app crash
here how i check it :
func setupReachability() {
do {
self.reachability = try Reachability.reachabilityForInternetConnection()
} catch {
print("Cannot setup reachability monitoring")
return
}
self.reachability!.whenReachable = { reachability in
self.loginButton(true)
}
self.reachability!.whenUnreachable = { reachability in
self.loginButton(false)
}
do { try self.reachability!.startNotifier() } catch {
print("Cannot start reachability monitoring")
return
}
print("Started reachability")
}
You always, ALWAYS, must handle errors in your connections. You absolutely cannot rely on Reachability. You also cannot rely that your connection goes to the server that you want to connect to. It is possible and practically happens a lot that you get a reply from a totally different server in a totally different format than you expected, and you need to handle that. (Take your app to the nearest Starbucks and check whether it survives).

Resources