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

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
}
}

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?

issue while accessing global func in app delegate

I am new to swift and i created one swift file with name mySession and in that file i am storing login data like below
Store Login Data
func setLoginData (data:Data) {
let preferences = UserDefaults.standard
let Key_Login = "Login"
preferences.set(data, forKey: Key_Login)
preferences.synchronize()
}
and second func for use that stored data
Get Data
func getLoginData ()->Data {
let preferences = UserDefaults.standard
let Key_Login = "Login"
if preferences.object(forKey: Key_Login) == nil {
return data
} else {
return preferences.data(forKey: Key_Login)!
}
}
So now my question is that i want to use getLoginData func in my app delegate to check user is logged in or not so not able to under stand how to use that func in app delegate
You should encapsulate these methods inside a class and use an instance of that class inside AppDelegate
class MySession {
func setLogin(_ data: Data){...}
func getLoginData() -> Data {...}
}
At the call site, instantiate MySession and use your methods to do what's needed.
class AppDelegate {
//...
let sessionHandler = MySession()
sessionHandler.getLoginData()
}
Sidenote, make sure you're using proper Swift 4 naming conventions.
First Import the file name in AppDelegate
Import mySession
Then just call the method in your desired function.
setLoginData(data:<your Data>)
For example.If you want to use it in didFinishLaunchingWithOptions method, follow this
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DefaultNetworkManager.appsConfigSetup()
window = UIWindow(frame: UIScreen.main.bounds)
setupApplication()
setLoginData(data: <Your Data>)
return true
}

Chartboost Delegate not working in swift

Desired : I want to do something when Delegates method call Observed :Delegates method not calling Ad's show on the screen successfully
Error
code:Chartboost.delegate=self
Error: Type 'Chartboost' has no member 'delegate'
AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Chartboost.start(withAppId: "4f21c409cd1cb2fb7000001b", appSignature: "92e2de2fd7070327bdeb54c15a5295309c6fcd2d", delegate: nil)
return true
}
ViewController Code
class ViewController: UIViewController,GADBannerViewDelegate, GADInterstitialDelegate,GADRewardBasedVideoAdDelegate,IMBannerDelegate, IMInterstitialDelegate ,ChartboostDelegate{
#IBAction func Vedio(_ sender: Any) {
Chartboost.showRewardedVideo(CBLocationMainMenu)
}
#IBAction func LoadFullAd(_ sender: Any) {
Chartboost.showInterstitial(CBLocationHomeScreen)
}
private func shouldDisplayRewardedVideo(_ location: CBLocation) -> Bool {
return true
}
private func shouldRequestInterstitial(_ location: CBLocation) -> Bool {
return true
}
}
A had to set the delegate as self with Chartboost.setDelegate(self)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Chartboost.start(withAppId: "4f21c409cd1cb2fb7000001b", appSignature: "92e2de2fd7070327bdeb54c15a5295309c6fcd2d", delegate:self as ChartboostDelegate)
Chartboost.setDelegate(self as ChartboostDelegate)
return true
}
After looking at how to properly convert Objective-C methods in Swift, I added the underscore (_), which changed the function to:
func shouldDisplayRewardedVideo(_ location: CBLocation) -> Bool
{
return true
}
func shouldRequestInterstitial(_ location: CBLocation) -> Bool {
return true
}
XCode then gave me a hint that I was close to the delegate method, but needed to change the type of location and I ended up with
func shouldDisplayRewardedVideo(_ location: String) -> Bool
{
return true
}
func shouldRequestInterstitial(_ location: String) -> Bool {
return true
}
If the delegate is set to nil, the class that calls the delegate's methods (in this case Chartboost) will not be able to make the delegate's method calls. You should set the delegate to the 'self' of the class where you have implemented the delegate methods expected by Chartboost.
In the example above, you could set the Chartboost delegate to the 'self' of the ViewController.
For example, inside of ViewController, you have already declared the 'ChartboostDelegate' in the class signature. When you want to turn on the Chartboost delegate methods, assign the ViewController's 'self' to the Chartboost delegate using something like:
Chartboost.delegate = self
In the case of Chartboost, it looks like the author made the delegate private, so it can be set in the ViewController using:
Chartboost.start(withAppId: "some uid", appSignature: "some other uid", delegate: self)
or, as later found out:
Chartboost.setDelegate(self)
(It can also be set in the AppDelegate class by locating the ViewController instance in the storyboard. Not a great fit in this case.)
If you're having problems generating the delegate method's call signature stubs (the method calls expected by the delegate), XCode will autogenerate them for you. Just click on the error message found next to your class declaration:
Type '<your class implementing the delegate methods>' does not conform to protocol '<the delegate protocol to implement>'
More detail about the error will appear. Click the 'Fix' button and XCode will autogenerate the method stubs for you.

UIApplicationShortcutItem in didFinishLaunching

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.

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
}

Resources