I have an application which I was testing on iOS 10.3 since few days and it was working fine. I recently tested it on an iOS 12 device and it was not working as expected. The application was not able to connect to my server.
Upon investigation, I found that the "Mobile Data" switch was turned off for my application in Settings -> AppName. After turning it on, it started working perfectly.
So, given this scenario, is there a way I can determine the status of this switch from my code? If I can know the status and if it's off, I can redirect the user to the application setting using:
let urlObj = NSURL.init(string:UIApplicationOpenSettingsURLString)
if #available(iOS 10.0, *) {
UIApplication.shared.open(urlObj as! URL, options: [ : ], completionHandler: { Success in
})
} else {
let success = UIApplication.shared.openURL(url as URL)
print("Open \(url): \(success)")
}
P.S: I am not looking for a solution using Reachability as it is not completely reliable.
There is no way to check this setting in the latest iOS release. You have two options to deal with this: first is you check if device is not connected to WiFi, and also not in airplane mode. If server can not be reached it’s safe to assume that data is disabled.
Second option is just to present user errors saying the application is unable to reach the server and to change UI/notice in application accordingly to deal with any scenario where a network connection can not be reached.
CTCellularData is a potential option however as of iOS 11 has some down falls of how often the state is checked.
Related
I know this is a common question but since there are lots of things changing with each ios update , curious to ask this again.
I have a requirement in iOS app (implemented using ionic framework) , where there must be an option to go to Bluetooth settings of the iphone from within the app so that the user can turn it on/off.
I have read several articles saying that Apple may reject apps trying to access phone settings and it is not advisable to access phone settings through app. Can someone clarify if this still holds true with latest iOS versions and should I never try to do this in future?
You cannot open the Bluetooth settings by using
App-Prefs:root=Bluetooth
The issue is that Apple does not allow us to use non-public APIs anymore and you maybe at risk of getting your developer program cancelled if you try to do so.
All you can do is open the iPhone settings in general by using this code:
extension UIApplication {
static func openAppSettings(completion: #escaping (_ isSuccess: Bool) -> ()) {
guard let url = URL(string: UIApplication.openSettingsURLString) else {
completion(false)
return
}
UIApplication.shared.open(url) { isSuccess in
completion(isSuccess)
}
}
}
Usage:
UIApplication.openAppSettings { isSuccess in
if isSuccess == false {
//Display error
}
}
I’ve implemented firebase email authentication in iOS. I’ve tested the functionality on 4 different iPhones, all running iOS 13.3.1, and it works in half of them and not in the other. For the half that don’t work, I receive an error message stating “Invalid Dynamic Link - Blocked”. I’m performing the same process on all iPhones and am connected to the same WiFi. I’m trying to understand what would cause this to happen? It’s clearly something on the phone that is preventing it from segueing back to the app after authenticating.
I had the same problem and in my case, it was that I didn't set the dynamicLinkDomain (it isn't on the firebase example).
let actionCodeSettings = ActionCodeSettings()
actionCodeSettings.url = URL(string: "https://XXXXXX.page.link")
actionCodeSettings.dynamicLinkDomain = "XXXXXX.page.link"
actionCodeSettings.handleCodeInApp = true
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
Situation:
Since our users have updated their iOS to 11 and/or WatchOS to 4, our iOS app doesn't seem to fire any scheduled timers when the app gets started by our WatchOS app. Maybe we are doing something wrong when starting our main app from the WatchOS app.
Context & code:
Our WatchOS app is a companion app that lets the user start/stop our iPhone app in the background by pressing a button. We do this by using:
func startMainApp() {
guard WCSession.default().isReachable == true else {
print("Watch is not reachable")
return
}
var data = [String : AnyObject]()
data[WatchActions.actionKey()] = NSNumber.init(value: WatchActions.startApp.rawValue as Int)
WCSession.default().sendMessage(data, replyHandler: { (result: [String : Any]) in
let resultNumber = result[WatchActions.resultKey()] as? NSNumber
let resultBool = resultNumber!.boolValue
if resultBool == true {
self.setModeActivated()
} else {
self.setModeActivationFailed()
}
}) { (error: Error) in
if (error as NSError).code != 7012 {
print("start app error: \(error.localizedDescription)")
self.setModeActivationFailed()
}
}
}
Then in our main app, we receive the message and start our base controller:
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
if let actionNumber : NSNumber = message[WatchActions.actionKey()] as? NSNumber {
if let watchAction : WatchActions = WatchActions(rawValue: actionNumber.intValue) {
switch(watchAction) {
case .isAppActive:
let result = BaseController.sharedInstance.sleepAndWakeUpController.isAwake()
replyHandler([WatchActions.resultKey() : NSNumber.init(value: result as Bool)])
return
case .startApp:
AudioController.sharedInstance().playActivatedSound()
let isRunningOnForeground = ApplicationStateHelper.isActive()
if isRunningOnForeground == false {
BaseController.sharedInstance.start(inBackground: true)
}
let result = true
replyHandler([WatchActions.resultKey() : NSNumber.init(value: result as Bool)])
DDLogInfo("[APPLE WATCH] [didReceiveMessage] [.startApp]")
return
}
}
}
replyHandler([WatchActions.resultKey() : NSNumber.init(value: false as Bool)])
return
}
Everything seems to work as before, we correctly get GPS locations, all our processes get started, however, Timer objects that get started, don't fire.
This worked perfectly before on iOS 10, so I suspect this has something to do with iOS 11 background states that work differently. However, I cannot seem to find any documentation of this.
Extra info:
On iOS 10, when we started our main app this way, the app got visible in the multitask view on the iPhone. Now on iOS 11, it isn't visible in the multitask view, however it does run on background. I successfully see local notifications that I schedule on the background, I can debug through the active code and when tapping the app icon, the app is immediately available.
Our WatchOS app has the deployment target of 2.0
Debugged via XCode with device connected, using Debug-->Attach to PID or Name-->Entered app name. Then start our app from the Apple Watch and debug.
Reproducable on iOS 11.0.3 with WatchOS 4.0 on iPhone 6
Questions:
What is the best way to start our main app from the watch app? Has something changed in iOS 11/WatchOS 4 regarding to background states? Can I find documentation of this? Could this be an iOS bug?
All I can offer you is a confirmation that this behavior did in fact change from iOS 10 to iOS 11. It is my suspicion that the behavior on iOS 10 (and earlier?) was incorrect. Apple doesn't have any qualms about changing behavior that was inadvertent/what they deem incorrect even if developer's come to rely on the behavior (I'm pretty sure I used this behavior on my last watch project).
The fact is that the UIApplication's state when launched by a message from the watch is background. Timer's aren't supposed to run when the application is in the background unless using particular background execution modes/background task. That fact is pretty well known and is usually encountered quite early on in an iOS developer's career. The fact that timer's would run in the background when launched from the watch was, I can surmise, a mistake.
I do not know your use case, i.e. why you were relying on those timers, but one thing you can do that is quite simple is to create an empty background task that will get you a little more time when the app is launched.
var backgroundTask: UIBackgroundTaskIdentifier?
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "app Start Task", expirationHandler: {
guard let task = backgroundTask else { return }
UIApplication.shared.endBackgroundTask(task)
})
let timer = Timer(timeInterval: 1, repeats: true) { (timer) in
print("Running")
}
If you need a more consistent, longer running solution, you may need to leverage your location updates as an opportunity to do whatever work the timer is currently for. There are plenty of other background modes to pursue as well.
Summary of your questions:
Q: What is the best way to start our main app from the watch app?
A: Your proposed code is a great way to launch the companion app.
Q: Has something changed in iOS 11/WatchOS 4 regarding to background states?
A: No, especially in regards to timers. The different behavior is likely a correction.
Q: Can I find documentation of this?
A: I can't. Sometimes you can squeeze this information out of apple engineers on the forums or via the code level support questions through your developer account or go to WWDC.
Q: Could this be an iOS bug?
A: The earlier behavior was likely the bug.
When app is closed and not running in background then location never track in iOS 11 it's not an iWatchOS 4 and iOS 11 Bug.
Changes to location tracking in iOS 11
follow this documentation link: Changes to location tracking in iOS 11
iOS 11 also makes some major changes to existing APIs. One of the affected areas is location tracking.
If your app only uses location while the app is in the foreground, as most apps do, you might not have to change anything at all; however, if it’s one of those apps that continuously track user’s location throughout the day, you should probably book some time this summer for making some changes in how you do the tracking and testing possible usage scenarios.
For example, let’s consider those two apps again; the continuous background location and the significant location change monitoring app. Suppose the user goes for a run with the continuous background location app. They’ll go on the run, they’ll come back, they’ll see the solid arrow the whole time, and when they look at their map, they’ll see every twist and turn they took. When they install that app that uses significant location change monitoring, they’ll see the same thing, a solid arrow. As far as the user is aware, this app is probably receiving the same amount of information as their run tracking app. So if users are misinterpreting our signals, we decided the best way to fix this was to adjust how we indicate location usage.
watchOS has access to many of the same technologies found in iOS apps; however, even if a technology is available, you may not be able to use it in quite the same way you did on iPhone.
Do not use background execution modes for a technology. In general, Watch apps are considered foreground apps; they run only while the user interacts with one of their interfaces. As a result, the corresponding WatchKit extension cannot take advantage of most background execution modes to perform tasks. might be help this documentation:
Leveraging iOS Technologies for watch
Our app is using WLAN to communicate with a wireless device. When our app is installed in iOS 10. Sometimes, udp socket doesn't work. The reason for that is, in iOS 10 they added a new setting or permission under your app that allows the user to switch on or off the user of WLAN or cellular data.
The following would appear in the settings of the app:
When I tap on the Wireless... It will bring me to this UI:
After allowing WLAN use. The app would work fine.
Now, the problem is, sometimes, or in some devices running iOS 10, the settings that I just showed you doesn't appear(I am referring to the setting shown on the first image). So, is there anything I can do to make that settings always appear? It seems that sometimes iOS system doesn't recognize that my app is using wireless data. And it would result in my app would never get to use WLAN forever.
There is no user permission to use WIFI in iOS10.
The application in your screenshot (BSW SMART KIT) is using Wireless Accessories (WAC), i.e. is able to connect to wireless speakers. To accomplish this the Wireless Accessory Configuration capability is required. This is what you can dis/enable in the systems settings (your screenshot).
The switch in the settings shows up after connecting to a device via WIFI through WAC. You can see this behaviour in your sample app (BSW SMART KIT) too.
This sample code might let you get the idea. Detailed information in Apples documentation.
After a time of researching. I ended up seeking help with Apple Code Level Support. Apple states that this problem would most probably occur when you reskin your app. They say that probably it's because of the Image UUID of the main app and the reskinned app are the same. When you install both of them in your phone, the system will treat the reskinned app as the same app compared to the main app. So, if the one app fails to access WLAN, then it will also affect the other one. According to them, this appears to be a bug in iOS. And currently, they don't have any solution for the developers. This is the radar bug number:
What I did to somehow lessen the occurrence of the problem is to add tracking to the Network Restriction by using the following code.
- (void)startCheckingNetworkRestriction
{
__weak AppDelegate *weakSelf = self;
_monitor = [[CTCellularData alloc] init];
_monitor.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state)
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
NSString * statusStr;
switch(state)
{
case kCTCellularDataRestrictedStateUnknown:
{
statusStr = #"restriction status:Unknown";
}
break;
case kCTCellularDataRestricted:
{
statusStr = #"restriction status:restricted";
[weakSelf performUrlSession];
}
break;
case kCTCellularDataNotRestricted:
{
statusStr = #"restriction status:not restricted";
}
break;
default:
{
abort();
}
break;
}
NSLog(#"Restriction state: %#", statusStr);
}];
};
}
Take note that you have to import CoreTelephony to do this.
#import CoreTelephony;
when I detect that the network is restricted. I will open a URL session to force internet access attempt and would hope that the restriction alert dialog would pop out. Once the alert is pop out, then the WLAN Access Settings that I was talking about would definitely appear under the settings. There are times that this doesn't work. If it happens, then you'll just have to rename the bundle ID, and make a couple of changes to your code and then rebuild it a couple of times (Well, that's what I did when I was experimenting this). Reinstalling the app won't do a thing. Restarting and resetting the phone won't do either.
Hope this helps.
I am trying to replicate an existing Android Application that I made to iOS. In my application, I should be able to connect to a WiFi network given the SSID and the Password. The SSID and the Password can be hardcoded or can be inputted by the user. I was going through the internet to research how this can be done on iOS, however, it seems that this behavior is highly discouraged and that there's no way on doing this using public/documented libraries that won't get your application rejected by the App Store.
The thing is I'll be using this application personally and I don't plan on submitting it to the App Store so it's okay if I use external libraries. If my friends would want to use it, I could export an enterprise ipa and give them instructions on how to install it.
Upon searching, it seemed that MobileWifi.framework was a good candidate. However, it does not seem that there's a straightforward way of using this library to connect to a WiFi network given the SSID and the Password.
Is there anyone who has successfully tried to connect to a Wifi Network given the SSID and Password?
With iOS 11, Apple provided public API you can use to programmatically join a WiFi network without leaving your app.
The class you’ll need to use is called NEHotspotConfiguration.
To use it, you need to enable the Hotspot capability in your App Capabilities (Adding Capabilities). Quick working example :
NEHotspotConfiguration *configuration = [[NEHotspotConfiguration
alloc] initWithSSID:#“SSID-Name”];
configuration.joinOnce = YES;
[[NEHotspotConfigurationManager sharedManager] applyConfiguration:configuration completionHandler:nil];
This will prompt the user to join the “SSID-Name” WiFi network. It will stay connected to the WiFi until the user leaves the app.
This doesn't work with the simulator you need to run this code with an actual device to make it work.
More informations here :
https://developer.apple.com/documentation/networkextension/nehotspotconfiguration
connect wifi networks in iOS 11. You can connect wifi using ssid and password like following.
Enable Hotspot on App Id configure services
After Enable Hotspot Configuration
Swift 4.0 Code for iOS 11 Only:
import NetworkExtension
...
let configuration = NEHotspotConfiguration.init(ssid: "SSIDname", passphrase: "Password", isWEP: false)
configuration.joinOnce = true
NEHotspotConfigurationManager.shared.apply(configuration) { (error) in
if error != nil {
if error?.localizedDescription == "already associated."
{
print("Connected")
}
else{
print("No Connected")
}
}
else {
print("Connected")
}
}
Contrary to what you see here and other places, you can do it. It's hard to make pretty enough for normal users, but if doesn't have to be then it's really easy to code. It's an enterprise admin thing, but anyone can do it. Look up "Connection Profiles." Comcast does this in their iOS app to setup their hotspots for you, for example.
Basically, it's an XML document that you get the device to ingest via Safari or Mail. There's a lot of tricks to it and the user experience isn't great, but I can confirm that it works in iOS 10 and it doesn't require jailbreaking. You use Configurator to generate the XML, and I suggest this thread for serving it (but people don't find it because it's not specifically about WiFi), and this blog for querying if it's installed which is surprisingly nontrivial but does indeed work.
I've answered this question many times, but either don't have enough rep or am too dumb to figure out how to close questions as duplicate so better answers can be found more easily.
UPDATE: iOS 11 provides APIs for this functionality directly (finally!). See NEHotspotConfiguration. Our Xamarin C# code now looks like:
var config = new NEHotspotConfiguration(Ssid, Pw, false);
config.JoinOnce = false;
Short answer, no.
Long answer :)
This question was asked many times:
Connect to WiFi programmatically in ios
connect to a specific wifi programmatically in ios with a given SSID and Password
Where do I find iOS Obj-C code to scan and connect to wifi (private API)
Programmatically auto-connect to WiFi iOS
The most interesting answer seems to be in the first link which points to a GitHub project: wifiAssociate. However someones in the third link explains that this doesn't work anymore with iOS8 so you might have hard time getting it running.
Furthermore the iDevice must be jailbroken.
Make sure both Network Extensions & Hotspot Configuration are turned on in Capabilities.
let wiFiConfig = NEHotspotConfiguration(ssid: YourSSID,
passphrase: YourPassword, isWEP: false)
wiFiConfig.joinOnce = false /*set to 'true' if you only want to join
the network while the user is within the
app, then have it disconnect when user
leaves your app*/
NEHotspotConfigurationManager.shared.apply(wiFiConfig) { error in
if error != nil {
//an error occurred
print(error?.localizedDescription)
}
else {
//success
}
}
my two cents:
if you got:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_NEHotspotConfigurationManager", referenced from:
objc-class-ref in WiFiViewController.o "_OBJC_CLASS_$_NEHotspotConfiguration", referenced from:
objc-class-ref in WiFiViewController.o ld: symbol(s) not found for architecture i386
simply it does NOT work under simulator.
on real device it does compile.
so use:
class func startHotspotHelperStuff(){
if TARGET_IPHONE_SIMULATOR == 0 {
if #available(iOS 11.0, *) {
let configuration = NEHotspotConfiguration(ssid: "ss")
configuration.joinOnce = true
NEHotspotConfigurationManager.shared.apply(configuration, completionHandler: { (err: Error?) in
print(err)
})
} else {
// Fallback on earlier versions
}
}// if TARGET_IPHONE_SIMULATOR == 0
}
We can programmatically connect wifi networks after IOS 11. You can connect wifi using ssid and password like following.
Swift
var configuration = NEHotspotConfiguration.init(ssid: "wifi name", passphrase: "wifi password", isWEP: false)
configuration.joinOnce = true
NEHotspotConfigurationManager.shared.apply(configuration) { (error) in
if error != nil {
//an error accured
print(error?.localizedDescription)
}
else {
//success
}
}
You can connect to wifi using Xcode 9 and swift 4
let WiFiConfig = NEHotspotConfiguration(ssid: "Mayur1",
passphrase: "123456789",
isWEP: false)
WiFiConfig.joinOnce = false
NEHotspotConfigurationManager.shared.apply(WiFiConfig) { error in
// Handle error or success
print(error?.localizedDescription)
}
I'm not sure if this will help anyone but there IS a Wifi QR code that Apple put into iOS since version 9 if I'm not mistaken. It's been there a while.
You can go here: https://qifi.org
Put your SSID and password in. Print out the QR code or just keep it on a screen. Point the iPhone at the QR code using the camera app. You will get a notification bar that if you press will setup your WIFI connection on the fly.
This 'should' work with Android but I haven't test it yet. It DOES work with iOS.
The nice thing with this solution is you can make your WIFI base 'hidden' on the network and folks can still connect to it which is very desirable in a high traffic area for security reasons.