Reachability not worked when user launch app offline - ios

I used Reachability Class from apple to check internet connection. I'll share to codes in the below. It's working fine if i open the app with online. But if i try to open app offline Reachability function is not triggered. Can anyone help me to solve this issue.
Thank you
Here is the code which are locate in AppDelegate.swift
var reachability: Reachability?
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "checkForReachability:", name:kReachabilityChangedNotification, object: nil)
reachability = Reachability.reachabilityForInternetConnection();
reachability?.startNotifier();
return true
}
func checkForReachability(notification: NSNotification) {
let title_alert = NSLocalizedString("Connection Problem", comment: "Connection Problem")
let remoteHostStatus = self.reachability!.currentReachabilityStatus()
if (remoteHostStatus == NotReachable) {
let myAlert = UIAlertController(title:title_alert, message:NSLocalizedString("Please Check Your Connection", comment: "Connection Problem"), preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title:NSLocalizedString("Try Again",comment:"Connection Problem"), style:UIAlertActionStyle.Default) {
action in
self.checkForReachability(notification)
}
myAlert.addAction(okAction)
self.window?.rootViewController?.presentViewController(myAlert, animated:true, completion:nil)
}
}

Reachability send you notifications when the state changes. If you have no internet, and this doesn't change, no notification will ever be sent. You need to check once by hand, probably after creating the reachability object. To check, you just check currentReachabilityStatus() outside the notification callback.
PS. I wouldn't send an alert when the internet goes away, but when the user actually does something that would require the internet and it doesn't work. Things like background updates shouldn't be visible to the user and shouldn't show an alert. (Bad example is iTunes on a Mac, which tells you all the time that it couldn't connect to the iTunes store, when I had no intention whatsoever to connect to it).
What exactly you do obviously depends on your app. Take Safari - it doesn't tell you there is no internet connection, only when you connect to a page. There are plenty of features that are useful without internet access.
If your app is absolutely useless without internet access and there is nothing better you can do, display a page that says "no internet". If there are things the user can do, let them do those things. Programmers often see these things from a programmer's point of view: Something went wrong, and everyone told you that you should inform the user. From the user's perspective: Being told with repeated alerts will get on their nerves. Imagine you are a user who just turned on AirPlane mode. That user thinks "for heaven's sake, I know there is no WiFi because I just turned it off. Don't keep telling me".

This is a hard one to answer with all of the code that Reachability requires, but here is the video I used when I set up Reachability for my app! It uses NSNotificationCenter to constantly check if the user is online. When the user goes offline, you can have an action occur until the user comes back online.

create a UIViewController extension and add a function like this -
func networkChanged() {
if let isReachable = Reachability()?.isReachable, isReachable == true {
noInternetErrorManager.removeNoInternetView()
} else {
noInternetErrorManager.displayNoInternetError(forParentView: view)
}
}
Now call this function on viewDidLoad()

Related

Users can't open CKShare record when they accepted the record and application starts

Working my way thru implementing the CKShare functionality of CloudKit.
I Haven manged to get to the part to share a record (via email for example) and also can confirm the user received the invite. The problem is that when user accepts the record then the app pops up but nothing happens. In order to assist here are some key Elements of the app, and please tell me if i am wrong.
1) The application does not require user to login using their Apple ID
2) I am testing the application via a direct built on two different phones (with seperate Apple IDs) when i Connect the phones to the computer with a Cable (aka not using TestFlight yet).
3) I have checked in the CloudkitDashboard and i can see the record that hah been shared and also see that the recored hah been shared, but instead of seeing the user email I sent the invite I see my email and the fact that the record hah been "accepted"
4) I Haven added the CKSharingSupported key in the Info.plist file.
5) The code in the AppDelegate.swift file am using to accept the CKShare is below. I world like to raw your attention to the fact that the string "User Accepted the Share" never gets printed, which makes me think that this part of the code never runs.
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
print("User Accepted the Share")
let acceptShareOperation: CKAcceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
acceptShareOperation.qualityOfService = .userInteractive
acceptShareOperation.perShareCompletionBlock = {meta, share,
error in
print("The Record Share was Accepted")
}
acceptShareOperation.acceptSharesCompletionBlock = {
error in
/// Send your user to where they need to go in your app
let viewController: SNPDetailsViewController = self.window?.rootViewController as! SNPDetailsViewController
viewController.fetchShare(cloudKitShareMetadata)
}
CKContainer(identifier:
cloudKitShareMetadata.containerIdentifier).add(acceptShareOperation)
}
Any insight to guide me where I am wrong, will be much appreciated.
Thank you for your time!
Please see this answer for more context: CloudKit CKShare URL Goes Nowhere
But make sure that:
You specify a fallback URL for your CloudKit Container that redirects to your application.
Inside your app in Xcode, you set up a URL scheme so that custom URLs like yourapp:// open your application and the query parameter from the fallback URL gets passed into userDidAcceptCloudKitShareWith.
After weeks of trial and error, research and LUCK I managed to find out the problem. All tutorials and online solutions relate to the below code in the AppDelegate.swift, to accept a CKShare record:
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
let acceptShareOperation: CKAcceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
acceptShareOperation.qualityOfService = .userInteractive
acceptShareOperation.perShareCompletionBlock = {meta, share,
error in
DispatchQueue.main.async() {
print("The Record Share was Accepted")
}
}
acceptShareOperation.acceptSharesCompletionBlock = {
error in
guard (error == nil) else{
print("Error \(error?.localizedDescription ?? "")")
return
}
let viewController: SNPDetailsViewController = self.window?.rootViewController as! SNPDetailsViewController
viewController.fetchShare(cloudKitShareMetadata)
}
CKContainer(identifier:
cloudKitShareMetadata.containerIdentifier).add(acceptShareOperation)
}
The solution in my case was to add it in the SceneDelegate.swift in the following function:
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
(add above code)
}
Hope this helps others!

Why doesn't my iOS (Swift) app properly recognize some external display devices?

So I have an odd issue and my google-fu utterly fails to even provide me the basis of where to start investigating, so even useful keywords to search on may be of use.
I have an iOS application written in swift. I have a model hooked up to receive notifications about external displays. On some adaptors, I'm able to properly detect and respond to the presence of an external display and programatically switch it out to be something other than a mirror (see code block below). But with another adaptor, instead of just 'magically' becoming a second screen, I'm asked to 'trust' the external device, and it simply mirrors the device screen. Not the intended design at all.
func addSecondScreen(screen: UIScreen){
self.externalWindow = UIWindow.init(frame: screen.bounds)
self.externalWindow!.screen = screen
self.externalWindow!.rootViewController = self.externalVC
self.externalWindow!.isHidden = false;
}
#objc func handleScreenDidConnectNotification( _ notification: NSNotification){
let newScreen = notification.object as! UIScreen
if(self.externalWindow == nil){
addSecondScreen(screen: newScreen)
}
}
#objc func handleScreenDidDisconnectNotification( _ notification: NSNotification){
if let externalWindow = self.externalWindow{
externalWindow.isHidden = true
self.externalWindow = nil
}
}
The worst issue here is that because I'm connecting to an external display to do this, I can't even run this code through the debugger to find out what is going on. I don't know where to even begin.
Any ideas?
Edit:
Thanks to someone pointing out wifi debugging, I can tell you my notifications are firing off, but they're both firing at the same time, one after the other, when the external adaptor is disconnected.

CLCircularRegion and wake up app

In application we have mechanism like native Reminder app in iOS with firing notifications when user enter or exit in some region.
But two devices behave differently (5 and 5s) in same time. All devices have enable notifications, and allow use locations.
Two devices have a some "travel" and in the route created 10 points. First device (5) when came to finish received only 6 notifications, (5s) don't receive any notification.
But my question is how I can know when my app is restart in background or continue working. Because, all log in app I redirect into a file, and after download container and analyze what happened in app in travel time.
I noticed app restart in same times when device is enter to region and my log marks fired in the file but notifications don't receive. This is happended when app try to get some information from web service in didFinishLaunchingWithOptions
And maybe this is problem. How to know distinguish restart app or continue working. Thx.
Are you checking UIApplicationLaunchOptionsLocationKey in didFinishLaunchingWithOptions similar to (sorry, Swift is what I have now):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsLocationKey] != nil {
// app was launched in response to incoming location event
}
}
Additionally, if you're not already doing this you may need to create notifications differently if app is in background:
// Show an alert if application is active
if UIApplication.sharedApplication().applicationState == .Active {
if let message = notefromRegionIdentifier(region.identifier) {
if let viewController = window?.rootViewController {
showSimpleAlertWithTitle(nil, message: message, viewController: viewController)
}
}
}
else {
// Otherwise present a local notification:
let notification = UILocalNotification()
notification.alertBody = notefromRegionIdentifier(region.identifier)
notification.soundName = "Default";
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}

How do I exactly use MMWormhole with Swift?

I have an iPhone application and added a WatchKitExtension. From the iPhone App I want to pass a String to the WatchApp which is supposed to change an image on the Watch.
What I already did was to download the source files and import the MMWormhole.m & .h. They are written in Obj-C and so Xcode automatically bridged them for me.
I also added an app group and activated it for my WatchExtension & my iPhone target
In the tutorial on GitHub it says I have to initialize the wormhole with:
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"group.com.mutualmobile.wormhole"
optionalDirectory:#"wormhole"];
...and send a message using:
[self.wormhole passMessageObject:#{#"titleString" : title}
identifier:#"messageIdentifier"];
But I have actually no idea where to put that, I am using Swift in my iPhone application and the WatchExtension.
Can anyone please help me there?
I suppose it depends on different applications, but for one application I put the listeners in the didFinishLaunchingWithOptions method of my app delegate in the main iOS app. This was because the user would be using the watch, and they would be relaying information off to the phone
There were multiple listeners...
var wormhole = MMWormhole(applicationGroupIdentifier: "group", optionalDirectory: nil)
wormhole.listenForMessageWithIdentifier("identifier", listener: { (message) -> Void in
//do stuff
})
wormhole.listenForMessageWithIdentifier("identifier2", listener: { (message) -> Void in
//do stuff
})
wormhole.listenForMessageWithIdentifier("identifier3", listener: { (message) -> Void in
//do stuff
})
And then in a WKInterfaceController, I sent a message. Sometimes in an action, sometimes in the willActivate method. It really depends on the flow of your app
var wormhole = MMWormhole(applicationGroupIdentifier: "group", optionalDirectory: nil)
#IBAction func buttonPushed(){
wormhole.passMessageObject("object", identifier: "identifier1")
}
This can work both ways though, I could have very easily put a listener in my watch which would wait for messages initiated by some Interface Controller on the phone.
Here are my instructions. Hopefully they help you with the simplest use case and then you can expand from there. (Remember to structure your code so that it actually makes sense!)
Get MMWormhole (the .h and the .m) added to your project. If you know how to use Cocoapods, do that, but otherwise, just use git submodules. (I use git submmodules)
Because you need the .h to be visible from Swift, you need to use a bridging header.
Set up an App Group, which requires using the Developer Portal. Link is here
In your iPhone build target -> Capabilities -> App Groups and add your group. If all three checkboxes do not go perfectly, go back to the Developer Portal and make sure everything is right or start again.
MMWormhole, iPhone Side
Set up the wormhole somewhere you can reach it. NOTE: your group ID has to be the one from above!
let wormhole = MMWormhole(applicationGroupIdentifier: "group.testMe.now", optionalDirectory: nil)
wormhole.listenForMessageWithIdentifier("wormholeMessageFromWatch", listener: { (message ) -> Void in
if let messageFromWatch = message as? String {
// do something with messageFromWatch
}
})
iPhone App Sends String
wormhole.passMessageObject("message from phone to watch", identifier: "wormholeMessageFromPhone")
iPhone app registers to receive and sends again in the callback via MMWormhole (asynchronous but cool)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
universe.initWormhole(.phone, messageHandler: { (message) -> () in
universe.wormhole.passMessageObject("the phone got \(message)", identifier: "wormholeMessageFromPhone")
})
return true
}
MMWormhole, Apple Watch Side
Set up the wormhole somewhere you can reach it. NOTE: your group ID has to be the one from above!
let wormhole = MMWormhole(applicationGroupIdentifier: "group.testMe.now", optionalDirectory: nil)
wormhole.listenForMessageWithIdentifier("wormholeMessageFromPhone", listener: { (message ) -> Void in
if let messageFromPhone = message as? String {
// do something with messageFromPhone
}
})
MMWormhole, watch app registers to receive
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
universe.initWormhole(.watch, messageHandler: { (message) -> () in
println("MMWormhole Message Came to Watch: \(message)")
})
}
MMWormhole, watch app sends
// force open the parent application because otherwise the message goes nowhere until the app is opened
WKInterfaceController.openParentApplication(["":""], reply: nil)
universe.wormhole.passMessageObject("[from watch to phone]", identifier: "wormholeMessageFromWatch")

Cannot Log into Meteor on iOS with ObjectiveDDP

I am trying out the objectiveDDP library to connect my iOS app to my Meteor app. I am running my meteor app on localhost:3000 and I am getting this error when I try to login:
Error Domain=boundsj.objectiveddp.transport Code=0 "You are not connected" UserInfo=0x7fe33b440500 {NSLocalizedDescription=You are not connected}
*Note - I have not done anything to the server to support this, I assuming that it is supported out of the box.
I'm not sure if the url I put in is correct because I couldn't find the documentation for which url I should connect to when using ObjectiveDDP.
Here is my code:
var meteorClient: MeteorClient = MeteorClient(DDPVersion: "1")
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.meteorClient.addSubscription("someSubscription")
var ddp = ObjectiveDDP(URLString: "ws://localhost:3000/websocket", delegate: self.meteorClient)
self.meteorClient.ddp = ddp;
self.meteorClient.ddp.connectWebSocket()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reportConnection", name: MeteorClientDidConnectNotification, object: nil)
}
func reportConnection() {
println("================> connected to server!")
println(self.meteorClient.connected)
self.meteorClient.logonWithEmail("blob#schmoe.com", password: "password", responseCallback: {
(response, error) -> Void in
if (error != nil) {
println("*****Error******")
println(error);
return;
}
println(response);
})
}
*Update
So I try to check whether my meteorClient is connected to the server before I login. I added
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reportConnection", name: MeteorClientDidConnectNotification, object: nil)
To get notify when I am connected. However, when the DidConnectNotification is receive, I check self.meteorClient.connected and got false.
maintainer of said library, can you specify what versions of ObectiveDDP and meteor you are using? First try using ObectiveDDP from master not cocoapods. Your url is correct. First thing to check also, do the sample apps connect to your server?
Can you try your login by using a button instead, I think your hitting a race condition. This is something that could use a bit of tidy up.
Change your DDPVersion from 1 to pre2
var meteorClient: MeteorClient = MeteorClient(DDPVersion: "pre2")
I realize this is old, but thought I could help someone in the future. The issue seems to be that you used MeteorClientDidConnectNotification to decide when to start the login procedure. You should wait for the subsequent notification - MeteorClientConnectionReadyNotification. The button "fixed" it because you coincidentally waited after the MeteorClientDidConnectNotification for the client to become ready and for you to start the login procedure.

Resources