I have an app that does a simple textbook navigator.geoLocation.watchPosition(...) that works great in iOS 5.x both in Safari and as a web app (using apple-mobile-web-app-capable meta tag).
However, in iOS6, GeoLocation does not work in the webapp. It still works in safari as expected, but when I run the webapp, it prompts me for location permission, then silently fails. I see the location icon, but no events are thrown from watchLocation. I get no error events or any location events.
Has anyone run into this? Any workarounds? It's definitely iOS6 specific and also specific to the apple-mobile-web-app-capable/webapp.
This is definitely a bug but I found a work around. You aren't going to like this but at least it will get your web app working again. You need to examine the User Agent header and if it contains "iPhone OS 6" then do not use:
<meta content="yes" name="apple-mobile-web-app-capable" />
Yes, this means that it won't be a true web app and you will get the Safari header and footer bars. But at least it will make your app work again from the home screen. You can see how this works by going to my site www.nextbus.com.
Note that it appears that Google ran into this problem. Try going to maps.google.com and then adding the web app to your homescreen. The geolocation will work for it but you will indeed see the ugly Safari header and footer bars.
Please complain loudly to Apple!
The good news is: I've done it... I've figured it out. The bad news is: somebody smarter than me is going to have to tell you why this works, whereas any other variation of this solution or any of the other solutions offered don't work. This was a hard-fought victory but I'm too embarrassed to say how many hours (days) it took me to figure this out. Without further ado:
if (window.navigator.geolocation) {
var accuracyThreshold = 100,
timeout = 10 * 1000,
watchID = navigator.geolocation.watchPosition(function(position) {
$('#latitude').val(position.coords.latitude); // set your latitude value here
$('#longitude').val(position.coords.longitude); // set your longitude value here
// if the returned distance accuracy is less than your pre-defined accuracy threshold,
// then clear the timeout below and also clear the watchPosition to prevent it from running continuously
position.coords.accuracy < accuracyThreshold && (clearTimeout(delayClear), navigator.geolocation.clearWatch(watchID))
}, function(error) {
// if it fails to get the return object (position), clear the timeout
// and cancel the watchPosition() to prevent it from running continuously
clearTimeout(delayClear);
navigator.geolocation.clearWatch(watchID);
// make the error message more human-readable friendly
var errMsg;
switch (error.code) {
case '0':
errMsg = 'Unknown Error';
break;
case '1':
errMsg = 'Location permission denied by user.';
break;
case '2':
errMsg = 'Position is not available';
break;
case '3':
errMsg = 'Request timeout';
break;
}
}, {
enableHighAccuracy: true,
timeout: timeout,
maximumAge: 0
}),
delayClear = setTimeout(function() {
navigator.geolocation.clearWatch(watchID);
}, timeout + 1E3); // make this setTimeout delay one second longer than your watchPosition() timeout
}
else {
throw new Error("Geolocation is not supported.");
}
Note: for some reason, this doesn't seem to work as consistently if the execution of this code it delayed at some point after initially launching the app. So, this is the FIRST thing I execute in my initialization method.
Note: The only other thing I've added to my app is, when I need to use the geolocation data (which, for me, takes place after the initialization of several other Classes/Object Literals), is to check for the latitude/longitude values. If they exist, continue; If not, run the above geolocation method again, then continue.
Note: One of the things that threw me for a long time was that I only needed to get the current position of the user. I didn't need to track the users' movements. I kept trying different iterations of this with the getCurrentPosition() method. For whatever reason, it doesn't work. So, this is the solution I came up with. Run it like you're going to track the users location (to get their location in the first place), then once you've gotten their location, clear the watchPosition ID to prevent it from tracking them. If you need to track their location as it changes over time, you can of course... not clear the watchPosition ID.
HTH. From everything I've been reading, there are a lot of developers who need this functionality to work for their mission-critical apps. If this solution doesn't work for you, I'm not sure what other direction I can give. Having said that, I've tested this several hundred times and this successfully retrieves the users' location in a WebApp (navigator.standalone) on iOS 6.
here is a video of me replicating the bug and demonstrating a work around. This bug appears to exist weather you use the web app meta tag or not.
http://youtu.be/ygprgHh6LxA
Update: 121212 - IOS 6.1 Beta 3 testing shows the bug is still not resolved...
Update: 122012 - IOS 6.1 Beta 4 testing shows the bug is still not resolved...
Update: 031113 - Replication Example
Okay, it is a simple issue to replicate in just a few seconds. I feel it is not a safari, but an IOS issue. It’s almost as if Google wrote the bios for the IOS to meet the WC3 html geo location spec and took it with them when IOS6 kicked them off the bus.
Using an IOS device go here:
http://uc.myaesc.com/geoloctestorig.htm
Click start, watch should return result almost every second.
Then click the Google link to leave this page.
Then user browser back button to return
Click start.
Watch will return 1 to 3 records and hang.
Minimize safari (home button) and then restore (safari icon); stops hanging
That's it. until it does not hang, the issue remains.
Mark
UPDATE:
IOS 7.1 fixed the issue...
It appears it only works once, then any secondary calls fail. One alternative is to cache the result and use the cached result if you have one, though this means you can't have an app that follows someone's position.
This is not exactly an answer as it seems like Home Screen apps in ios6 has some bug related to GeoLocation, but I found the following link very helpful. It explains that as Home Screen apps are now stored like native apps, they have their own storage/caching.
Geolocation works on the first iteration but fails to update from then on. the work around is to remove the following meta tag so that Home Screens app run in Browser mode (I am not sure if it is exactly call a Browser mode). The app will unfortunately render with the browser headers and footers, but GeoLocationwill be working again.
<meta content="yes" name="apple-mobile-web-app-capable" />
iOS 6 Geolocation and Local Data Storage
"Data in Home Screen apps are now stored like native apps. Native apps each have their own sandbox where their data is stored, backed up and restored to. Prior to iOS 6, Home Screen apps shared data with the same app running in the browser. If the user cleared the cache in the browser, the Home Screen version of the app would lose its data too.With iOS 6, Home Screen apps’ data gets saved to a sandbox just like native apps. Backups and restores handle the data properly, and clear cache in the browser will not affect them."
I'm having the same problem. Looks like watchPosition is simply failing out after the first position is received. I haven't found a work around yet, but I wanted to confirm that I was experiencing issues.
Using these samples:
http://www.w3schools.com/html/html5_geolocation.asp
I get expected results on ios5 but ios6 drops the ball with watchPosition.
I can confirm I get the same problem when running my web app in fullscreen.
Interestingly, when Safari in Fullscreen asked permission to use my location, the website title was 'web' rather than the title of the website, as in previous versions of iOS.
Removing the "apple-mobile-web-app-capable" meta tag is fine, and it works, but only if you "Add to Homescreen" again. We have ~7000 daily users who have already added our icon to their homescreen. Getting them to do so again, then potentially again when a fix is implimented isn't great.
This appears to be fixed under iOS 6.1! It wasn't in the recent betas, but today's final 6.1 release seems to be good with my testing.
seems to be fixed in iOS 6.1, finally! See my site www.slople.com where it works again under 6.1
This is FINALLY fixed in iOS7 beta (beta 2 is all I've tested)!
You must take care of non secure content loaded. For me loading all javascript, images and css from secure context solved the problem with safari.
Related
I am developing an extension of an iPhone App for Apple Watch (written in Swift running WatchOS2.2, Xcode 7.3, testing on a physical device)
I am running into an issue where I expect no prompt/confirmation on the Apple Watch when calling the tel schema for the following function:
WKExtension.sharedExtension().openSystemURL(NSURL(string:"tel:1231231234"))
I am not calling the telprompt function as my understanding is that this is not supported directly by Apple however it seems to be behaving as if it is telprompt. Interestingly enough, telprompt doesn't work.
I am expecting this to directly call the phone number without the prompt on the Apple Watch, however it is first prompting the user displaying a Cancel button on the top left, the number in the middle of the screen, and a Call button at the bottom. This also doesn't appear to be picking up the localization on the system (french for example) so it is always displaying Cancel and Call in English.
Image of Cancel, Number, and Call being prompted when French in the system language
Has anyone else encountered this issue? I haven't found much talk about it on the web. Or is this a known issues? Again, I don't see any bugs reported for it.
Fix Found for the Localization - See Below
Interestingly enough I was able to get the localization working.
The fix for it was that my Target for my Watch and WatchExtension needed to be checked off for my Launch Images (or splash screen images, or commonly left as "default.png"). Previously my Launch Images were for the app Target only.
I have no idea why the localization seemed to be require my launch images be added to the Watch and WatchExtension targets.
This did not resolve the prompting as it still took place - however since it was at least presenting the correct language this was good for us.
I'm making a turn-based game, using the Game Center Turn-Based Gaming functionality. I'm using the simulator and my iPhone to test notifications of turn events. The results are very inconsistent. About 75% of the time when I make a move on the simulator and pass the turn I don't get any notifications on my iPhone. It seems that this function doesn't get called:
handleTurnEventForMatch:didBecomeActive:
I set the GKEventHandlerDelegate in the code that authenticates the local user and it seems to be set correctly. The fact that I get notifications once in a while suggests that this isn't where the problem lies.
Does anyone have any idea what could be the problem here? Could this be a problem with the Sandbox Game Center Server? Do they limit the amount of notifications you can send in a short amount of time or something like that?
Edit: I just found some posts of people experiencing the same problem, but no solution. No very recent posts though. Any updates?
Edit2: I also find that after a move, when I open the GKTurnBasedMatchmakerViewController on the other device it still says that it's the turn of the other player. When I close and reopen it the information is correct. Also, when I open the game GKTurnBasedMatchmakerViewController when it's still showing incorrect information, it does gives me the correct match object with the updated information. Could this be related in any way?
I thought I would share my solution with you, in the hopes that it is the same problem you are experiencing. As you can see from my comment to your question, I was having the same issue as you. It turned out that my settings in iTunes Connect were the culprit, unbeknownst to me. What you should check for is that you still have Game Center enabled in BOTH places that iTunes Connect requires. First, after going into "Manage my Apps" in iTunes Connect, select your app and on the right menu, and click "Manage Game Center". Make sure this is currently enabled. I would even recommend disabling it, and re-enabling it again for good measure.
In addition (and this is what I was missing), you must enable it in one more place. Go back to the previous screen when you first select the app. The top right is where you can select Manage Game Center, but we are looking on the bottom in the "Versions" section. Click the View Details button for your app, and make sure the Game Center button is enabled near the bottom. Again, I would disable and re-enable it here as well. Give it about 10 minutes, clear all your open turns and matches from the Game Center app (this might be an optional step), and build & run again. Hopefully, you will receive turn notifications properly again.
I got the same problem. However when i tested it on multiple devices it was working fine... May be some problem with the simulator
I created a web app and added to my iPhone Home Screen. When I switch to another app and back, iPhone automatically reload my web app. This breaks my app flow.
How do I prevent iPhone from reloading the app?
I have apple-mobile-web-app-capable meta tag enabled to hide Safari toolbar and I don't want to turn it off.
I just found this related question on SO: Stop native web app from reloading itself upon opening on iOS
As it seems it's a limitation of Safari, a proposed solution is to persist your web apps state using Javascript and HTML5 localStorage. When your web app is launched, check for the persisted state and load it if available.
You can read about using localStorage in Safari here: http://developer.apple.com/library/safari/#documentation/iPhone/Conceptual/SafariJSDatabaseGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007256-CH1-SW1
Hope that helps you. At least it did for me, as I had the same problem as you. :-)
The short answer is that you can't control this. Sometimes iOS will keep a web app active in the background, at other times it will kill it. It's entirely related to how much memory is available on the device.
So, your best approach is to minimise the problems presented by this reload. Make sure your webapp updates the URL when you move from view to view, either by changing location.hash or using history.pushState(). This will allow you to reload whatever view the user was on before they switched apps. There are pagehide and pageshow events that allow you to execute code when the user moves away from your app - take that opportunity to store local state in localStorage and/or IndexedDB, then fetch that data again when the webapp is reopened.
I found a hack, tested on iOS 11.4.1/12.0
Open file uploading window and then switch back to the home screen.
The app still continues to work, in my case audio is playing and localStorage is updating
Proofs:
https://youtu.be/heehLUhGKYY
PS. note how song progress changes when we seek, it proves that app works in the background
Update: as this answer is receiving downvotes, I added this explanation.
Your problem might not be the actual reload, but the fact that Mobile Safari treats your user's cache and cookies differently when your web app is opened through the browser, than when it's 'installed' as a web app to the home screen. Although the solutions proposed here that use localStorage will work, they're a lot of work for client-side logic that can be avoided if your server is already responsible for persisting the session state of your user. The 30-second solution is to simply explicitly set the session cookie to have a longer lifetime.
This allows you to keep the state intact even between device reboots, so even though it doesn't technically stop the web app from being reloaded when launched from the home screen, it is an easy way to restore the state for the user without him/her noticing the reload - which in many cases I suspect is the real problem.
For a more elaborate discussion of this strategy and code examples, take a look at these questions and my answers there:
Maintain PHP Session in web app on iPhone
iPhone "Bookmark to Homescreen" removes cookies and session?
I've just written my first app in intraweb (control pack for the iphone)
It works, but after several hours I still cannot get the iphone to not show safari.
I've added a link to my homescreen and I start the (web)app from there and things start out ok.
But when I change a page safari shows up and breaks the look I'm an iphone app® magic and eats up 25% of my screen.
The intraweb documentation says this is due to the fact that the webadres changes (parameters in the url), so I switched to hidden fields, but to no avail.
How do I make sure my iphone webapp stays full screen and never goes to safari?
It works for me, and I suggest you ask in the TMS Software newsgroups, with steps to reproduce this behaviour, so they can look into it (which will give you a better chance of solving the issue that asking here, to be honest)...
During the development of my iPhone app, I decided to integrate Google Maps by including a UIWebView with a URL I construct pointing to maps.google.com. The problem is that the website tries to get the user's location, displaying the "This app wants to use your location". This creates a number of issues for me, which I can explain if necessary.
Is there any way to disable the UIWebView / Mobile Safari from asking for the user's location. The only workaround I can think of is to use Google's Javascript v3 API to make my own map, but I would have to get a license and I'm not 100% certain I would be able to disable asking for location (although I think I can).
Does anyone have an alternate workaround or way of tackling this issue? Thanks!
I believe I found a suitable workaround for now. I remembered that you can embed maps from Google in your website using an iframe. I checked the url they construct to do this and noticed they appended an extra parameter, "output=embed". I appended the "output" variable in the url and pasted this into Mobile Safari on the simulator and it no longer asks me for my location.
The only drawback of this is that I can see so far is that there are some extra UI components on the page as it includes both the mobile controls and website embed controls on the page. Also, I have not tested this on an actual device yet.
Edit: Testing on the device showed that the embed controls don't work, but the mobile controls work as does every other aspect of the map.
Use an MKMapView instead.
(I'm assuming there's a reason you're not using MKMapView for this task and you're stuck with the UIWebView)
You might try asking for user location earlier in your application via CLLocationManager, i.e. right after launch. The permission alert will not be shown more than once. (whether the user chooses to allow or not, which you do not seem to care about)
Not entirely sure that this will work in your case though, UIWebView may still prompt for permission to share location data with the websites it shows.
Assuming it's done via JavaScript, you could try injecting some javascript in - (void)webViewDidStartLoad:(UIWebView *)webView using -[UIWebView stringByEvaluatingJavaScriptFromString:] (e.g. you could set the location-requesting function to null). I'm not entirely sure how well that works while the page is still loading, though...