What in ios is a required delegate method?
Example:
CLLocationManagerDelegate
locationManagerDidPauseLocationUpdates:
Tells the delegate that location updates were paused. (required)
The question is related to the last word: '(required)'
I know that there are optional delegate methods, but what happens if I do not implement
all of the required delegates?
Especially I have not implemented the above method in my App which uses the LocationManager,
SDK is ios6, target = ios5.
Could my App crash if I do not implement that method?
I recently ported to ios6, but till now it did not crash!
Was that luck that the method had not been called, or is required a recommendation?
Update:
The info above is from ios6.1 Docu set.
When I open the ios6.1 header file
LocationManager.h:
* Discussion:
* Invoked when location updates are automatically paused.
*/
- (void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0);
There is no hint for required, they are all under the #optional tag
It could crash is as close as we can get. Generally it means that if you don't implement it something won't work, but the documentation isn't as accurate as it could be. In some cases the class using the delegate doesn't check that the delegate actually implements the specified method so whenever it was called you'd get an exception. So, if the documentation says it's required it's best to implement it, even if your implementation is an empty method.
In this particular case, "locationManagerDidPauseLocationUpdates" is called on iOS 6 (and newer) OS'es to inform your app that the location isn't changing and that it's shutting down to save power.
On iOS 5, this delegate method won't get called but if you implement it, it will get called in iOS 6.
If you target iOS 6 (instead of iOS 5), you'll get a compiler warning if you didn't implement any "required" delegate methods.
Related
In an iOS app using SwiftUI and CoreLocation. I need to keep an eye on the status of the permissions given by the user for location access.
Looking at the documentation for CLLocationManagerDelegate, it looks like I am supposed to use the locationManagerDidChangeAuthorization method.
The problem is this method never gets called (as far as I can see). Beside, the other method locationManager:didChangeAuthorization (supposed to be deprecated) seems to do the job.
If someone has any good advice, please let me know.
locationManagerDidChangeAuthorization is only available in iOS 14. If you are running an earlier version of iOS you will need to use the older method, locationManager:didChangeAuthorization. Although it is deprecated, it still works (and you must use it if you are supporting earlier iOS versions).
The reason for the delegate method change is covered in a WWDC 2020 session - In iOS 14 location permission has a time permission (never/when in use/always) and a new precision permission (high/low precision).
The original delegate method delivers the time permission to the delegate method, but doesn't deliver the precision information (since that permission didn't exist previously).
Rather than create a new delegate method that receives both the time and precision permission details, Apple have opted for a simple method that tells you something has changed. You then need to write code to check the CLLocationManager to determine the permissions you have and what to do about it.
Why they did it this way, we can only speculate, but I suspect that it gives them flexibility to add further permissions in the future without changing the method signature.
I tested:
UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
which is for putting app on background and it works.
How do I put app back on foreground?
I tried:
UIControl().sendAction(#selector(URLSessionTask.resume), to: UIApplication.shared, for: nil)
But eventually it crashes...
Thank you
Update:
Since you've indicated that you're looking for any technical solution, even those not compatible with the App Store or Apple's terms, this should be possible using the Private API LSApplicationWorkspace: openApplicationWithBundleID. Try something like this:
Create a .h file and set up an interface to the LSApplicationWorkspace class and list the required method. You will need to #import "PrivateHeaders.h" in your bridging header.
//
// PrivateHeaders.h
//
#ifndef PrivateHeaders_h
#define PrivateHeaders_h
#interface LSApplicationWorkspace : NSObject
- (bool)openApplicationWithBundleID:(id)arg1;
#end
#endif /* PrivateHeaders_h */
You should then be able to call this function and pass in the Bundle Identifier of your app as an string.
//
// SomeClass.swift
//
import MobileCoreServices
let workspace = LSApplicationWorkspace()
/**
Launch an App given its bundle identifier
- parameter bundleIdentifier: The bundle identifier of the app to launch
- returns: True if app is launched, otherwise false
*/
func openApp(withBundleIdentifier bundleIdentifier: String) -> Bool {
// Call the Private API LSApplicationWorkspace method
return workspace.openApplication(withBundleID: bundleIdentifier)
}
Original:
What you are doing is likely a violation of the iOS Human Interface Guidelines (although the "Don’t Quit Programmatically" is no longer specifically defined), so as the comments have said, it is not suited to the App Store. Regardless, once your app is suspended in this way, I don't expect that there is a way to resume it programmatically, unless you can hook into a Background Operation to run URLSessionTask.resume, but I have not tested it and am unsure whether it can work.
Apps can be launched (and hence brought into the foreground) programmatically from another app or today extension by using a Custom URL Scheme, or via a Push Notification. It isn't possible to launch the app from the Background Operation via a URL Scheme, since it is part of the UIKit framework, which must be run in the main thread.
In summary, I think your best option is to try to use a Notification. This just means that the user will need to click on the notification to bring your app back into the foreground.
Closing/opening the app should be done explicitly by the user. Any other way of closing or opening the app is not supported by Apple and will be rejected when uploaded to app store. iOS Human Interface Guideline states:
Don’t Quit Programmatically
Never quit an iOS application
programmatically because people tend to interpret this as a crash.
However, if external circumstances prevent your application from
functioning as intended, you need to tell your users about the
situation and explain what they can do about it. Depending on how
severe the application malfunction is, you have two choices.
*Display
an attractive screen that describes the problem and suggests a
correction. A screen provides feedback that reassures usersthat
there’s nothing wrong with your application. It puts usersin control,
letting them decide whether they want to take corrective action and
continue using your application or press the Home button and open a
different application
*If only some of your application's features are
not working, display either a screen or an alert when people activate
the feature. Display the alert only when people try to accessthe
feature that isn’t functioning
Just as a follow up to Jordan's excellent answer I want to give an explanation for why your code works in the first place and why that alone will get your app rejected, even without any functionality to make it active again and bring it to the foreground.
As maddy pointed out in a comment, you're basically calling a method from UIApplication's private API. This works due to the Objective-C runtime's dynamic linking. You might wonder "But I am using Swift, what does that have to do with Objective-C?" The answer lies in #selector mechanism. A Selector is basically just a symbol that the Objective-C runtime looks up in a table to get a method it invokes (for you). This is why it's technically not correct to say you "call a method" when you do something like myObjectInstance.someMethod(). The correct way to phrase that would be to "send a message" to the object, because that's what is happening in the runtime. The target-action mechanism is build around that. The sendAction(_: Selector?, to: Any?) method does the same thing. So in effect your code does the following:
Get the symbol that corresponds to URLSessionTask's suspend() method.
Tell the shared instance of UIApplication to invoke the method that it has for that symbol.
Now usually that would result in a crash with the typical "unknown selector sent to instance..." error message. But here, by sure coincidence UIApplication also has a method for that instance (or rather, the runtime also has one of its methods listed in its table for that symbol). You kind of "found" a method that is not declared in its public header. You successfully circumvented a compile-time check for this and invoke a method that is part of a private API. This is explicitly forbidden in the Apple Developer Program License Agreement
Besides all that, I would strongly advise against trying to design an app that way in the first place. As maddy pointed out it's also likely considered to violate the HIGs. Even if you're not trying to do anything malicious and properly explain the feature in your app's description, that won't make Apple let it slide (I assume). Personally, as a user, I'd also find it annoying if the app did something the system already has a specific mechanic for in a different manner, at least in terms of app's coming to background and foreground.
I don't think it can be done without user interaction
The option is you can generate a push notification to tell the user to bring the application to foreground
When the operating system delivers push notification and the target application is not running in the foreground, it presents the notification.
If there is a notification alert and the user taps or clicks the action button (or moves the action slider), the application launches and calls a method to pass in the local-notification object or remote-notification payload.
When setting up a WCSession in a watchOS app, does the WCSessionDelegate's sessionReachabilityDidChange: method always get invoked immediately after calling activateSession? From my testing this seems to be true but I am not finding any confirmation of this in documentation.
I ask because if I can rely on sessionReachabilityDidChange: being called immediately after activating the session, I can remove some redundant code from applicationDidBecomeActive that checks for a reachable session and sends some initial messages to the iPhone app.
Why not just call it yourself after you are done doing all your set up? That way you don't rely on any undocumented behavior, yet you don't have to duplicate code in two places
I am using HomeKit apis in my iOS application.
I am facing very weird issue whenever I do Add/Delete Home/Room/Acccessory/ActionSet
No callback method is getting called of HMHomeDelegate protocol.
Except the homeManagerDidUpdateHomes method no other callback method is getting called of HMHomeManagerDelegate protocol.
And I have set delegate properly.
I have gone through a stack over link having the same issue.
here it is
Please suggest if anybody faced the similar problem.
Caveat: I know that private frameworks won't fly in the App Store.
I'm trying to use the BluetoothManager framework to let me 1) check if Bluetooth is enabled on a device, and 2) if not on, turn it on.
I can successfully load BluetoothManager per the instructions found here, but once I've done so, BluetoothManager just doesn't seem to do anything.
Calling enabled always returns NO, even when Bluetooth is enabled. Calling setEnabled never changes its state. Absolutely every other BluetoothManager call I've tried always returns NO, nil, zero-element arrays, etc. In short, the BluetoothManager that gets returned seems totally neutered. (I'm running off my 4.2.1 iPhone, not the simulator.)
I've seen posts from people who claim to have gotten this working, yet I haven't been able to get a response from them. Can anyone shed light on why this might not be working?
Thanks very much.
It takes a second for the BluetoothManager to startup and attach to the BTServer. I just did a quick call to the shared instance - [BluetoothManager sharedInstance]; - to force it to init. Once it's finished init it will post a BluetoothAvailabilityChangedNotification. I'd just listen for that and try [[BluetoothManager sharedInstance] setEnabled:YES]; in the notification callback.
FYI, the object included in the NSNotification for BluetoothAvailabilityChangedNotification is a boolean, so you should be able to check that to make sure that Bluetooth is really available.
That's because of IPC. BluetoothManager communicates via sendMsg et el. Use for example NSTimer to query BluetoothManager sharedInstance so your program has time to receive and process messages.
There is a demo project on Github called BeeTee for demonstrating the private framework BluetoothManager.