UrbanAirship Swift - swift

I am trying to integrate UrbanAirship into my project but I am getting the following error:
2016-08-25 16:26:23.898 Fibre[7758:368753] [E] __52+[UAirship handleAppDidFinishLaunchingNotification:]_block_invoke [Line 320] Please ensure that [UAirship takeOff] is called synchronously before application:didFinishLaunchingWithOptions: returns
Any help resolving this would be much appreciated!
My application delegate is as as below:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
let configuration = ParseClientConfiguration { (configuration) -> Void in
configuration.applicationId = "******"
configuration.clientKey = "*****"
configuration.server = "https://*****.herokuapp.com/parse"
}
Parse.initializeWithConfiguration(configuration)
let config: UAConfig = UAConfig.defaultConfig()
UAirship.takeOff(config)
return true
}

I haven't used the Urban Airship SDK before myself but going off of the error message that you posted the key bit seems to be "is called Synchronously before application:didFinishLaunchingWithOptions: returns".
What I would guess is happening is that you're calling the takeOff method on UAirship but didFinishLaunchingMethod is returning before that method completes; More than likely because it's being handled in another thread).
Try forcing the takeOff method to be run on the main thread with "dispatch_async" and then passing in the main queue such as:
dispatch_async(dispatch_get_main_queue(), {
UAirship.takeOff(config)
})
Try that out, hopefully that will fix the issue.

Related

Can method_exchangeImplementations work between objective-c and swift?

I'm trying to get a Cordova plugin to work with a Capacitor project.
Capacitor is in Swift and the plugin is in Obj-c.
The plugin runs this bit of code to swizzle itself in the app start flow.
+ (void)load {
Method original = class_getInstanceMethod(self, #selector(application:didFinishLaunchingWithOptions:));
Method swizzled = class_getInstanceMethod(self, #selector(application:swizzledDidFinishLaunchingWithOptions:));
method_exchangeImplementations(original, swizzled);
}
original (application:didFinishLaunchingWithOptions) is in the Swift AppDelegate and swizzled (application:swizzledDidFinishLaunchingWithOptions) is in the plugin's obj-c code.
This doesn't work.
When the app starts, this bit of code runs, but the result of class_getInstanceMethod for original is always nil.
The swift delegate has
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
And the obj-c plugin has
- (BOOL)application:(UIApplication *)application swizzledDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
}
I've tried adding #objc and dynamic to the swift func but that didn't help.
Is there a way to make this work?

Background Fetch working every Second in Swift 4.2

Behold. I am trying to create an IOS app with swift 4.2, which, according to my client, has to sound an alarm, but it can only be stopped with a QR code. Therefore, what I will need will be a timer that works in the background, checking the time every second, because something else, the user could stop it, without the QR code. The only thing I've found for that is the Background Fetch, but can it work with 1 second checks every time?
Does anyone have a better idea?
Cheers
This is my source code for now
App delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.setMinimumBackgroundFetchInterval(2.0)
}
//ini----------------------------background task
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// fetch data from internet now
let time:TimerNow = TimerNow();
let hours = preferences.string(forKey: "hours");
let minutes = preferences.string(forKey: "minutes");
if((hours == time.getHours())&&(minutes == time.getMinutes())){
lem.onPlayCommand();
}
completionHandler(.newData)
}
//fin ---------------------------background task
I think you should use local notifications instead. It will be fired when you want to notify about the elapsed time. You could cancel it whenever and with any condition you want.
IMO timer and background fetching is not suitable for that situation.

BackgroundTask iOS 13 Swift with Scenes

I am trying to implement the new iOS 13 background tasks functionality with a new app that is utilizing Scenes (also new). There are not a lot of examples, but the ones I have found were not using SceneDelegates, instead all the logic was contained in the AppDelegate. This causes issues since the event for the app going into the background is now raised in the SceneDelegate, not the AppDelegate.
This method does not fire in AppDelegate when using scenes
func applicationDidEnterBackground(_ application: UIApplication) {
}
This method does fire in SceneDelegate
func sceneDidEnterBackground(_ scene: UIScene) {
}
I am not able to find a corresponding SceneDelegate method for the following method in AppDelegate where all the examples show BGTaskScheduler registering tasks
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BGTaskScheduler.shared.register(forTaskWithIdentifier: "UNIQUE_ID", using: DispatchQueue.global()) { task in
refreshMethod(task: task as! BGAppRefreshTask)
}
}
My question is whether there is some event in the SceneDelegate where I can register the task and if not what is the recommended pattern to follow for scene based apps.
AppDelegate.didFinishLaunchingWithOptions is still called at app launch, even when you have a SceneDelegate. So you should continue to register your background tasks in that method.

How to detect if an app was opened by a notification action in the ViewController

I am working on a notification app and was wondering if it is possible to detect if the app is opened from a notification action in ViewController.Swift instead of the AppDelegate.swift. How can I do this?
Krish, Whenever the app is launched the AppDelegate gets called(if the default Main class is not modified) and in the AppDelegate class you can check for the launch option in launch option delegate whether its opened through remote notification. This is the first action where you will catch the app opening event.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let remoteNotif = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary
if remoteNotif != nil {
let notifName = remoteNotif?["aps"] as! String
print("Notification: \(notifName )")
}
else {
print("Not remote")
}
}
Here is a complete and easy answer to that
After you handle the event on AppDelegate you can use an observer to let your ViewController of that event.
Normally you should redirect the user to a specific view, depending on the notification payload.

Performing Long-Term Actions in the Background Swift 3

I have an app that uses Corebluetooth and uses its Background services. I need also long-term CoreBluetooth action so I need to implement State preservation and restoration. I followed Apple document the link at the below https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html also watched WWDC 2013 presentation video link below,
https://developer.apple.com/videos/play/wwdc2013/703/
First of all, the sources are in Objective C and I am not familiar with that, I tried to convert them in swift3.
Then, I defined CBCentralManager with restoration identifier shown below,
self.myCentralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: "myCentralManager"])
Then, added delegate method something in the below,
func centralManager( willRestoreState dict: [String : AnyObject]) {
let peripherals: [CBPeripheral] = dict[CBCentralManagerRestoredStatePeripheralsKey] as! [CBPeripheral]
for peripheral in peripherals {
self.peripherals.append(peripheral)
peripheral.delegate = self
}
}
Finally did didFinishLaunchingWithOptions method in AppDelegate,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
if let peripheralManagerIdentifiers: [String] = launchOptions?[UIApplicationLaunchOptionsKey.bluetoothCentrals] as? [String]{
for identifier in peripheralManagerIdentifiers{
if (identifier == "myCentralManager"){
}
}
}
return true
}
When I debug, didFinishLaunchingWithOptions method called, the parameter launchOptions?[UIApplicationLaunchOptionsKey.bluetoothCentrals] is nil I think because of first time I created the app, then got error when defining CBCentralManager with restoration identifier the error is "reason: ' has provided a restore identifier but the delegate doesn't implement the centralManager:willRestoreState: method'"
I'm totally confused, thank you for any help,
Best regards,