Spotlight search doesn't work when app not running - ios

I want to tell first UIViewController that Spotlight opened it. I try to make it by NSNotificationCenter. But I tried several methods and they don't it when I make my key like "spotlightOpen". When I use standard name like UIApplicationDidFinishLaunchingNotification it works for me. Below I wrote several methods which I tried
In
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSNotificationCenter.defaultCenter().postNotificationName("selectSong", object: nil)
return true
}
In First controller
NSNotificationCenter.defaultCenter().addObserverForName("selectSong", object: nil, queue: NSOperationQueue.mainQueue()) { (NSNotification) -> Void in
print("Song table is loaded")
}
Still I made it in the first controller. But it too didn't work for me.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "selectedSong", name: "selectSong", object: nil)
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
print("continueUserActivity")
userDefault.setBool(true, forKey: "applicationDelegateOpen")
if userActivity.activityType == CSSearchableItemActionType {
print("CSSearchableItemActionType")
if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] {
userDefault.setValue(identifier, forKey: "spotlightIdentifier")
userDefault.setBool(true, forKey: "spotlightBool")
print(identifier)
return true
}
}
return false
}

It's very likely that on a cold launch that your view controller hasn't been initialized yet. You need to temporarily save that notification data until something like loadView or viewDidLoad is called.

In the continueUserActivity callback of UIApplicationDelegate, you can handle the NSUserActivity of type CSSearchableItemActionType for spotlight actions, if you are using the CoreSpotlight API. If you are using NSUserActivity API, you can also handle those in this delegate callback.
You create a core spotlight item with your song ID:
let item = CSSearchableItem(uniqueIdentifier: songId, domainIdentifier: "com.mycompany.song", attributeSet: attributeSet)
// ... Save it and all that
You can handle the spotlight launch using:
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
if let songId = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
// Do notification with songId
// Or let view controllers know about selected songId
}
}
return true
}

I made it. It works for me
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.reloadData()
self.tabBarController!.tabBar.hidden = false
fetchFilesFromFolder()
//
checkSpotlightResult()
}
func checkSpotlightResult() {
print("checkSpotlightResult")
// print(arrayForCheckSpot)
// userDefault.setValue(identifier, forKey: "spotlightIdentifier")
// userDefault.setBool(true, forKey: "spotlightBool")
// var boolCheckSpot: Bool!
// var identifierCheckSpot: String!
boolCheckSpot = userDefault.boolForKey("spotlightBool")
if boolCheckSpot != nil {
if boolCheckSpot == true {
identifierCheckSpot = userDefault.valueForKey("spotlightIdentifier") as! String
if arrayForCheckSpot.contains(identifierCheckSpot) {
// print("Array title contains \(identifierCheckSpot)")
let index = arrayForCheckSpot.indexOf(identifierCheckSpot)!
let myIndexPath = NSIndexPath(forRow: index, inSection: 0)
print(myIndexPath)
self.tableView.selectRowAtIndexPath(myIndexPath, animated: true, scrollPosition: .None)
self.performSegueWithIdentifier("listenMusic", sender: self)
userDefault.setBool(false, forKey: "spotlightBool")
}
}
}
}

Related

appflayer deeplinking not working when application open from link (delegate method not called)

I have deeplinking with AppFlayer SDK and universal link open the application successfully means deeplinking working fine.
Now issue is that when application open from link then it will not redirect to it's page. But if I put application in background and take in foreground then deeplinking works
I follow this guide lines.(link)
AppFlayer setup code
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
AppsFlyerTracker.shared().appsFlyerDevKey = "xxxxxxxxxxxx"
AppsFlyerTracker.shared().appleAppID = "xxxxxxx"
AppsFlyerTracker.shared().isDebug = false
AppsFlyerTracker.shared().delegate = self
self.pushNotificationService(application)
objStoryBoard = UIStoryboard(name:"Main", bundle: nil)
return true
}
AppFlayer delegate
Edit :: This method not called when application is open from link of appsflayer marketing
//MARK:
//MARK: appflayer delegate
func onAppOpenAttribution(_ installData: [AnyHashable: Any]) {
NSLog("installData ::%#", installData )
if let link = installData["link"] as? String
{
if link.contains(read_Localizable("titleAppflayer"))
{
if let arrQueryItems = URLComponents(string: link)!.queryItems {
for obj in arrQueryItems {
if obj.name.caseInsensitiveCompare(read_Localizable("appflayerParameter")) == .orderedSame
{
self.redirectAppflayer(withstrUrl: obj.value!)
return
}
}
}
}
}
}
user activity method of application
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
// if let url = userActivity.webpageURL
// {
// NSLog("URL :: %#",[url])
// }
if userActivity.webpageURL?.absoluteString.contains(read_Localizable("titleAppflayer")) == true
{
AppsFlyerTracker.shared().continue(userActivity, restorationHandler: restorationHandler)
return true
}
return Branch.getInstance().continue(userActivity)
}
Let me know what i doing wrong.
You are calling the AppsFlyerTracker from willFinishLaunchingWithOptions when it should be called from didFinishLaunchingWithOptions. Could you move the relevant code and test again?
Add
[[AppsFlyerTracker sharedTracker] trackAppLaunch];
onConversionDataSuccess method will start getting call.

SWIFT: I don't get notifications from my CloudKit app?

Here is my code:
#IBAction func sendSweet(sender: AnyObject) {
//delegate method
let newSweet = CKRecord(recordType: "Extra1")
newSweet["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newSweet, completionHandler: { (record:CKRecord?, error:NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.beginUpdates()
self.sweets.insert(newSweet, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
self.textField.text = ""
self.textField.resignFirstResponder()
})
}})
// Put the CloudKit private database in a constants
let privateDatabase = CKContainer.defaultContainer().publicCloudDatabase
// Create subscription and set three of its properties (an id, a predicate, and options)
let friendsSubscription = CKSubscription(recordType: "Extra1",
predicate: NSPredicate(format: "TRUEPREDICATE"),
subscriptionID: "Extra1",
options: .FiresOnRecordCreation)
// Create a notification and set two of its properties (alertBody and shouldBadge)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New message in Lms Chat"
notificationInfo.shouldBadge = false
// Attach the notification to the subscription
friendsSubscription.notificationInfo = notificationInfo
// Save the subscription in the private database
privateDatabase.saveSubscription(friendsSubscription) {recordReturned, error in
// On the main thread, display an error/success message in the textView
if error != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = "Cloud error\n\(error!.localizedDescription)"
}
} else {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = ""
}
}
}
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
It's a messaging app so people can message eachother but I also want them to recieve notifications. This is the code for notifications and I also have some code for notifications in App Delegate:
import UIKit
import CloudKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge () {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
But notifications do not come in even though I have all of the code needed to make notifications every time someone sends a message. Am I missing something? Thanks!
Someone answered me before saying:
You are adding data to the public database but you are creating your subscription on the private database. One of those two needs to be changed to match the other.
So I changed the let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase to let privateDatabase = CKContainer.defaultContainer().publicCloudDatabase
In your app Delegate, you are missing this method :
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
print(deviceToken) // This is the device token
var token: String = "\(deviceToken)" /// formatted token.
let rawtoken = token.stringByReplacingOccurrencesOfString(">", withString: "")
let cleantoken = rawtoken.stringByReplacingOccurrencesOfString("<", withString: "")
var finaltoken = cleantoken.stringByReplacingOccurrencesOfString(" ", withString: "")
// Send device token to server
}
}
}
WHat I mean is that to send notification, you register user for push Notification. For the case If the register succeeds, you have to implement the method above.
if the registeration fails, you have to implement this :
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error)
}

SWIFT: Why do I not get notifications from my CloudKit app?

Here is my code:
#IBAction func sendSweet(sender: AnyObject) {
//delegate method
let newSweet = CKRecord(recordType: "Extra1")
newSweet["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newSweet, completionHandler: { (record:CKRecord?, error:NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.beginUpdates()
self.sweets.insert(newSweet, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
self.textField.text = ""
self.textField.resignFirstResponder()
})
}})
// Put the CloudKit private database in a constants
let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase
// Create subscription and set three of its properties (an id, a predicate, and options)
let friendsSubscription = CKSubscription(recordType: "Extra1",
predicate: NSPredicate(format: "TRUEPREDICATE"),
subscriptionID: "Extra1",
options: .FiresOnRecordCreation)
// Create a notification and set two of its properties (alertBody and shouldBadge)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New message in Lms Chat"
notificationInfo.shouldBadge = false
// Attach the notification to the subscription
friendsSubscription.notificationInfo = notificationInfo
// Save the subscription in the private database
privateDatabase.saveSubscription(friendsSubscription) {recordReturned, error in
// On the main thread, display an error/success message in the textView
if error != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = "Cloud error\n\(error!.localizedDescription)"
}
} else {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = ""
}
}
}
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
It's a messaging app so people can message eachother but I also want them to recieve notifications. This is the code for notifications and I also have some code for notifications in App Delegate:
import UIKit
import CloudKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge () {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
But notifications do not come in even though I have all of the code needed to make notifications every time someone sends a message. Am I missing something? Thanks!
You are adding data to the public database but you are creating your subscription on the private database. One of those two needs to be changed to match the other.

3D Touch shortcuts won't work due to Unexpected Crash

I am trying to add 3D Touch Shortcuts to an application, I have managed to have the shortcuts appear when using 3DTouch on the app icon from the homescreen; however when using the shortcut the application crashes on load and I am unsure why.
I have managed to get the application to load for the bookmarks shortcut but it does not initiate the BookmarksViewController, it just loads the InitialViewController.
The application is embedded within a UITabBarController and a UINavigationController for each Tab. Both View Controllers I am trying to load are in different tabs but the first view in the navigation controller stack.
Does anyone know where I am going wrong ?
info.plist file
App Delegate
enum ShortcutItemType: String {
case Bookmarks
case Favourites
init?(shortcutItem: UIApplicationShortcutItem) {
guard let last = shortcutItem.type.componentsSeparatedByString(".").last else { return nil }
self.init(rawValue: last)
}
var type: String {
return NSBundle.mainBundle().bundleIdentifier! + ".\(self.rawValue)"
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
handleShortcutItem(shortcutItem)
}
return true
}
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let sb = UIStoryboard(name: "main", bundle: nil)
let favouritesVC = sb.instantiateViewControllerWithIdentifier("FavouritesVC") as! FavouritesTableViewController
let bookmarksVC = sb.instantiateViewControllerWithIdentifier("BookmarksVC") as! BookmarksNotesViewController
switch shortcutItemType {
case .Bookmarks:
rootViewController.presentViewController(bookmarksVC, animated: true, completion: nil)
break
case .Favourites:
rootViewController.presentViewController(favouritesVC, animated: true, completion: nil)
break
}
}
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
handleShortcutItem(shortcutItem)
}
}
Storyboard ID
These are the StoryboardID for the View Controllers.
I think you forgot the 's' in com.appName.Bookmark
Or you need to remove the 's' from Bookmarks here:
enum ShortcutItemType: String {
case Bookmarks
case Favourites
Instead, try using a shortcut like this:
if shortcutItem.type == "com.appName.Bookmark"
#available(iOS 9.0, *)
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
if shortcutItem.type == "com.appName.Bookmark" {
}
}

How to open needed controller from Spotlight Search? In Swift

I made spotlight search in my app. I have UIPageViewController as RootViewController and seven different ViewControllers. I made when user found information in spotlight from my app it can open needed controller. But I made it with help NSNotificationCenter that I can open a needed controller. I was able to do it and I have works. I think it is wrong way and I don't know how can I make it otherwise. How can I it otherwise?
I show my realization and it works but I think that it is not a good
AppDelegate
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
let identifier = Int(userActivity.userInfo![CSSearchableItemActivityIdentifier] as! String)!
startIndex = identifier
notificatioCenter.postNotificationName("openSpotlight", object: nil)
}
return true
}
UIPageViewController as RootViewController
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.addObserver(self, selector: "openSearchFromSpotligt", name: "openSpotlight", object: nil)
}
func openSearchFromSpotligt() {
let startIndex = appDel.startIndex
let startingController = viewControllerAtIndex(startIndex)! // 0
pageViewController.setViewControllers([startingController], direction: .Forward, animated: false, completion: { done in })
}

Resources