UIApplicationShortcutItem in didFinishLaunching - ios

According to Apple documentation :
It’s your responsibility to ensure the system calls this method conditionally, depending on whether or not one of your app launch
methods (application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions:) has already handled a
quick action invocation. The system calls a launch method (before
calling this method) when a user selects a quick action for your app
and your app launches instead of activating. The requested quick
action might employ code paths different than those used otherwise
when your app launches. For example, say your app normally launches to
display view A, but your app was launched in response to a quick
action that needs view B. To handle such cases, check, on launch,
whether your app is being launched via a quick action. Perform this
check in your application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method by checking for the
UIApplicationLaunchOptionsShortcutItemKey launch option key. The
UIApplicationShortcutItem object is available as the value of the
launch option key.
But why there is a need to handle this in didfinishlauncing/willfinishLauncing methods. If the app is in killed state, eventually it will call the performActionForShortcutItem method, so why there is a need to check in didfinish method?
Sample code:
//code
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var launchedFromShortCut = false
//Check for ShortCutItem
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
launchedFromShortCut = true
handleShortCutItem(shortcutItem)
}
//Return false incase application was lanched from shorcut to prevent
//application(_:performActionForShortcutItem:completionHandler:) from being called
return !launchedFromShortCut
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: Bool -> Void) {
let handledShortCutItem = handleShortCutItem(shortcutItem)
completionHandler(handledShortCutItem)
}
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
var handled = false
//Get type string from shortcutItem
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
//Get root navigation viewcontroller and its first controller
let rootNavigationViewController = window!.rootViewController as? UINavigationController
let rootViewController = rootNavigationViewController?.viewControllers.first as UIViewController?
//Pop to root view controller so that approperiete segue can be performed
rootNavigationViewController?.popToRootViewControllerAnimated(false)
switch shortcutType {
case .Torquiose:
rootViewController?.performSegueWithIdentifier(toTurquoiseSeque, sender: nil)
handled = true
case.Red:
rootViewController?.performSegueWithIdentifier(toRedSeque, sender: nil)
handled = true
}
}
return handled
}
}

It gives you the flexibility to decide - you can handle it "early" in didFinishLaunching - returning FALSE will inhibit performActionForShortcutItem from getting called later. Or you can return TRUE in didFinishLaunching, and performActionForShortcutItem will still get called.

Related

flutter : The screen freezes when I try to output the iOS built-in dictionary with UIReferenceLibraryViewController

I am making an English word learning app with flutter.
I am using the built-in dictionary of iOS.
It is a basic application that the built-in dictionary is displayed when the user presses the "answer confirmation button". I can basically do what I want to do, but I found a bug yesterday.
When I pressed the answer confirmation button for the word "buffalo", the screen froze. The screen itself is displayed instead of being killed, but no further operations are accepted.
It's the first time I've tested over 500 words. The other words are working fine.
The method channel is used to call the built-in dictionary.
//AppDelegate.swift
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
var ref: UIReferenceLibraryViewController = UIReferenceLibraryViewController(term:"")
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name: "hello_ios", binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: FlutterResult) -> Void in
if call.method == "searchDictionary" {
print(call.arguments)
self.searchDictionary(result:result,controller:controller,queryWord:call.arguments as! String)
//result("Hello from iOddddddddddddS")
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func searchDictionary(result: FlutterResult, controller: FlutterViewController, queryWord: String){
print("416\(UIReferenceLibraryViewController.dictionaryHasDefinition(forTerm:queryWord))")
//let ref: UIReferenceLibraryViewController = UIReferenceLibraryViewController(term: queryWord)
ref = UIReferenceLibraryViewController(term:queryWord)
if(UIReferenceLibraryViewController.dictionaryHasDefinition(forTerm:queryWord)){
controller.present(ref, animated: true, completion: nil)
}
}
}
I wrote the above code thinking that it should be conditional branching by
"UIReferenceLibraryViewController.dictionaryHasDefinition",
but it seems that it is not a solution because true is returned even at the time of "buffalo".
Since it works normally with other words, it seems that the caller is not the cause, but the following is the caller.
onPressed: () async {
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
await HomePage._channel.invokeMethod('searchDictionary',
editingTargetTextCtrl.text != '' ? editingTargetTextCtrl.text : data == null ? '' : data.text,);
},
There are almost no error messages, so I can't get any clues. Is there anything?

How to kick user back on homepage without using Navigations in IOS app

Background- I have an Ios Native app for which i am automating test scripts in XCUITest. The current test case flow in a class is as setup device,install app open app-> login ->perform action
for the second test case method it logs out first from the first test case action using navigations and starts
Is there any way that after end of each test case method run it directly jumps on the homepage without using navigations? Navigations takes extra time for large suite to run.
Swift 4.2
I think don't understand your question but if you want jump to homepage without navigation, you could use this line of code:
UIApplication.shared.keyWindow?.rootViewController = HomeViewController()
and if you don't want to jump up with little animation, use this:
DispatchQueue.main.async {
guard let window = UIApplication.shared.keyWindow else { return }
UIView.transition(with: window, duration: 0.5, options: .allowUserInteraction, animations: {
UIApplication.shared.keyWindow?.rootViewController = HomeViewController()
}, completion: { completed in
})
}
If you want to achieve faster navigation for your UI tests, you can use the benefit of launching app with launch arguments.
Edit Scheme > Run > add "UITEST" to Arguments Passed On Launch
For example in your codes define
class AppDelegate: UIResponder, UIApplicationDelegate {
var uiTestModeKey = "UITEST"
static var uiTestMode = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if (CommandLine.arguments.contains(uiTestModeKey)) {
AppDelegate.uiTestMode = true
}
return true
}
}
and in the part you want to navigate back quickly you could define something like:
func onActionComplete() {
if (AppDelegate.uiTestMode) {
navigationController?.popToRootViewController(animated: false)
//Or set the window to a new instance like
//UIApplication.shared.keyWindow?.rootViewController = HomeViewController()
}
}

How to override initial ViewController on First Run thru AppDelegate

I am using the following code to detect whether the app is running for the first time:
// Detect First Launch
let firstLaunch = NSUserDefaults.standardUserDefaults().boolForKey("FirstLaunch")
if firstLaunch {
print("Not first launch.")
Show()
}
else {
print("First launch, setting NSUserDefault.")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "FirstLaunch")
}
I am using the following function to show the ViewController:
func Show(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier(“myViewController”) as! MyViewController
self.window?.rootViewController?.presentViewController(vc, animated: true, completion: nil)
}
I put both code under: func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
However, when I build and run the app, the initial view controller is shown as if nothing happened. I want “myViewController” to be shown FIRST (during the app’s FIRST EVER LAUNCH on the device.) After that first launch, however, the “myViewController” won’t be shown anymore, unless the user uninstalls and freshly reinstalls the app.
Does anyone know how to do it?
Since you want to show the custom screen on first launch and then dismiss it, I would run this code in your normal root VC's viewWillAppear(animated:Bool) method
EDIT:
Yes, you will have to modify the code a bit. For example, like so:
func Show(){
guard let sb = storyboard else { return }
let vc = sb.instantiateViewControllerWithIdentifier(“myViewController”) as! MyViewController
navigationController?.presentViewController(vc, animated: true, completion: nil)
}
After setting the value to user defaults, you have to save it using the synchronize method. This method is invoked periodically buy the system, but if you didn't synchronize before it was called, your value isn't saved.
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "FirstLaunch")
NSUserDefaults.standardUserDefaults().synchronize()
Documentation of synchronizefrom to Apple docs :
Writes any modifications to the persistent domains to disk and updates all unmodified persistent domains to what is on disk.
Discussion
Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.

Attempting to load the view of a view controller while it is deallocating. CoreSpotlight

I integrate CoreSpotlight in my app. I want that user will find need information in spotlight search and after user will open this information in spotlight information opens in DetailViewController. I made it, spotlight works nice, but when application is opening I see this error Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (UIAlertController: 0x1245a0560) although I don't use UIAlertController. I made in AppDelegate func which call function of UITableViewController which must to open object by index. But it is not appear. Still there is an error in showData() performSegueWithIdentifier("show", sender: nil)reason: 'Receiver () has no segue with identifier 'show''. Although I add segue ( with show name) and it works when I usually select cell. Please help me.
AppDelegate
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
print(identifier)
checkWord = identifier // checkWord is String
let tableC = TableViewController()
tableC.showData()
return true
}
}
return false
}
func showData() {
let matchString = appDel.checkWord
if mainArray.contains(matchString) {
let ind = mainArray.indexOf(matchString)!
let indexPathMain = NSIndexPath(forItem: ind, inSection: 0)
print(indexPathMain)
self.tableView.selectRowAtIndexPath(indexPathMain, animated: true, scrollPosition: UITableViewScrollPosition.None)
performSegueWithIdentifier("show", sender: nil)
print("Show data")
}
}
If you don't implement willContinueUserActivityWithType or if it returns false, it means that iOS should handle activity. And in this case it can show UIAlertController. So to get rid this warning return true for your activity in this delegate call:
func application(application: UIApplication,
willContinueUserActivityWithType userActivityType: String) -> Bool {
return true
}

openURL in APPDelegate conversion error NSString -> String (Swift & iOS8)

I'm currently developing an iOS application that integrates Facebook and I'm having a bit of a problem while investigating this with Swift (with ObjC I have no problems).
The thing is, this is the method that gets executed in the appDelegate when coming from another APP (in this case FB in a WebBrowser):
func application(
application: UIApplication,
openURL url: NSURL,
sourceApplication: NSString,
annotation: AnyObject)
-> Bool {
let appString : String = sourceApplication as String // Try to convert format => EXCEPTION
let appString : String = String(sourceApplication) // 'SSS' Suggestion: EXCEPTION
println(sourceApplication) // Try to print the value => EXCEPTION
return FBAppCall.handleOpenURL(url, sourceApplication:sourceApplication,
withSession:session) // With Parse => EXCEPTION
}
And inside that method I'm having real trouble with the 'sourceApplication' parameter. I try to use it, I get an exception. I try to convert it, another exception...can't even log its value because it crashes when accessing its value. Changing the parameter type in the functions signature to String neither worked.
This is the error I'm getting:
EXEC_BAD_ACCESS
And I've been able to track down until I could read this that it's definitely a valuable hint:
ObjectiveC.NSString.__conversion (ObjectiveC.NSString)() -> Swift.String
Could it be an iOS8 bug?
Any of you has had this problem and/or knows how to solve it?
You have made two mistakes:
The function declaration from the app Delegate is func application(application: UIApplication!, openURL url: NSURL!, sourceApplication: String!, annotation: AnyObject!) -> Bool : sourceApplication is an optional String value not NSString.
Since sourceApplication is an optional it may return nil value (In your case returning nil) . Type casting nil to String is not safe , therefore it is crashing.
Solutions :
No type casting is required in your case Since returned value is String type
Use optional form type cast operator as? to safely type cast i.e
if let appString = sourceApplication {
println(appString as? String)
}
This is working for me (with FacebookSDK):
func application(application: UIApplication, openURL url: NSURL, sourceApplication: NSString?, annotation: AnyObject) -> Bool {
var wasHandled:Bool = FBAppCall.handleOpenURL(url, sourceApplication: sourceApplication)
return wasHandled
}
I don't get this in the playground. Could be a iOS 8 bug as you suggest
But for the sake of trying, can you try
let appString : String = String(sourceApplication)
For FB Messenger here's what I did to get some better handling in my AppDelegate. Most of the ideas were taken straight from the FB IOS documentation and ported to Swift.
Why did I feel like I should write an additional reply? I've got some experience with Swift, but felt like I wasted enough time trying to get the right set of code to do what I wanted with FB Messenger. Hopefully the raw code is useful to someone, just to consolidate a lot of bits and pieces and save some time.
NOTE: This does not include all the AppDelegate lifecycle methods you'll want/need, but hopefully it's a head start
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, FBSDKMessengerURLHandlerDelegate {
var window: UIWindow?
var messengerUrlHandler: FBSDKMessengerURLHandler?
var cancelContext : FBSDKMessengerURLHandlerCancelContext?
var composerContext : FBSDKMessengerURLHandlerOpenFromComposerContext?
var replyContext: FBSDKMessengerURLHandlerReplyContext?
// Facebook Messenger
enum MessengerShareMode : Int {
case MessengerShareModeCancel
case MessengerShareModeSend
case MessengerShareModeComposer
case MessengerShareModeReply
}
// shareMode holds state indicating which flow the user is in.
// Return the corresponding FBSDKMessengerContext based on that state.
var shareMode : MessengerShareMode?
/*
* Initialize the FB messenger handler and set self as the delegate.
*/
func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
YARAppearance.setAppearance()
let rootController = TabBarController()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = rootController
self.window!.makeKeyAndVisible()
// Facebook messenger handling
self.messengerUrlHandler = FBSDKMessengerURLHandler()
if (self.messengerUrlHandler != nil) {
self.messengerUrlHandler!.delegate = self
}
return true
}
/*
* Handle the cancel context flow.
*/
func messengerURLHandler(messengerURLHandler: FBSDKMessengerURLHandler!,
didHandleCancelWithContext context: FBSDKMessengerURLHandlerCancelContext!) {
self.cancelContext = context
self.shareMode = .MessengerShareModeCancel
}
/*
* When people enter your app through the composer in Messenger,
* this delegate function will be called.
*/
func messengerURLHandler(messengerURLHandler: FBSDKMessengerURLHandler!,
didHandleOpenFromComposerWithContext context: FBSDKMessengerURLHandlerOpenFromComposerContext!) {
self.composerContext = context
self.shareMode = .MessengerShareModeComposer
}
/*
* When people enter your app through the "Reply" button on content
* this delegate function will be called.
*/
func messengerURLHandler(messengerURLHandler: FBSDKMessengerURLHandler!,
didHandleReplyWithContext context: FBSDKMessengerURLHandlerReplyContext!) {
self.replyContext = context
self.shareMode = .MessengerShareModeReply
}
/*
* Handle URL calls from external applications, particularly Messenger
*/
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
let wasHandled:Bool = self.messengerUrlHandler!.openURL(url, sourceApplication: sourceApplication)
return wasHandled
}
/*
* A way to access the context objects elsewhere
*/
func getContextForShareMode() -> FBSDKMessengerContext? {
// shareMode holds state indicating which flow the user is in.
// Return the corresponding FBSDKMessengerContext based on that state.
if (shareMode == .MessengerShareModeSend) {
// Force a send flow by returning a broadcast context.
return FBSDKMessengerBroadcastContext()
} else if (shareMode == .MessengerShareModeComposer) {
// Force the composer flow by returning the composer context.
return self.composerContext!
} else if (shareMode == .MessengerShareModeReply) {
// Force the reply flow by returning the reply context.
return self.replyContext!
}
return nil
}
}

Resources