Swift 5 store event until all of the app has initialised - ios

Im working on an Ionic/Capacitor native plugin in Swift
I am trying to integrate OneSignal push notification service, my code below works just fine if the app is in the foreground and a user clicks the notification, however if the app is started from clicking a notification my events dont fire, I figure this is because the AppDelegate.swift loads before Capacitor/Ionic and when my NotificationCenter.default.post gets fired the data doesnt reach my plugin.
I guess I want to know if there is any way in Swift to store this post data until all parts of the app have loaded and initialised i.e Capacitor/Ionics webview
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let notificationOpenedBlock: OSHandleNotificationActionBlock = { result in
// This block gets called when the user reacts to a notification received
let payload: OSNotificationPayload = result!.notification.payload
var fullMessage = payload.body
let nc = NotificationCenter.default
nc.post(name: Notification.Name("TestingEvents"), object: nil)
if payload.additionalData != nil {
if payload.title != nil {
let messageTitle = payload.title
print("Message Title = \(messageTitle!)")
}
let additionalData = payload.additionalData
if additionalData?["actionSelected"] != nil {
fullMessage = fullMessage! + "\nPressed ButtonID: \(additionalData!["actionSelected"])"
}
}
}
return true
}
MyPlugin.swift
public override func load() {
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(handleSignal), name: Notification.Name("TestingEvents"), object: nil)
}
#objc func handleSignal() {
self.bridge.triggerWindowJSEvent(eventName: "myCustomEvent")
self.notifyListeners("myPluginEvent", data: [:])
}
and my app.component.ts
window.addEventListener('myCustomEvent', () => {
alert("myCustomEvent ")
});
Plugins.myPlugin.addListener("myPluginEvent", () => {
alert("myPluginEvent ")
});

this is a logical problem. what you can do here is save the notificaiton data (OSNotificationPayload) in an object/dictionary/array you receive in didFinishLaunchingWithOptions and add an observer "HandleNotificaionAfterPluginSetup" to appdelegate.
For this observer, you can trigger a post notification from your plugin once it is initialized and have everything ready to handle notificaitons. once you are all set in your plugin and have triggered the notificaion that code can execute in the notificaion handler you just setup which willl execute the code
if payload.title != nil {
let messageTitle = payload.title
print("Message Title = \(messageTitle!)")
}
let additionalData = payload.additionalData
if additionalData?["actionSelected"] != nil {
fullMessage = fullMessage! + "\nPressed ButtonID: \(additionalData!["actionSelected"])"
}
}```

Related

AVSystemController_SystemVolumeDidChangeNotification not giving callback for the first time

I want to check if both the volume buttons are working fine. So I set the observer AVSystemController_SystemVolumeDidChangeNotification to check that.
NotificationCenter.default.addObserver(self, selector: #selector(volumeCallback(notification:)), name: NSNotification.Name("AVSystemController_SystemVolumeDidChangeNotification"), object: nil)
Given is volumeCallback method:
#objc private func volumeCallback(notification: NSNotification) {
// check if app is in forground
guard UIApplication.shared.applicationState == .active else {
return
}
//get volume level
if let userInfo = notification.userInfo {
if let volumeChangeType = userInfo["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String {
if volumeChangeType == "ExplicitVolumeChange" {
print("value changed")
let level = userInfo["AVSystemController_AudioVolumeNotificationParameter"] as? Float
guard let volLevel = level else {
return
}
// my work here
}
}
}
}
Now the problem is, I am not getting callback in volumeCallback for the first installation of the app. The weird thing is, this method is being called when the app is in background, but not being called in foreground.
I am using iPhone 5s (iOS 10.3.3).
I don't understand what is the problem in this code. Any help will be appreciated.
This can be easily done with key-value observer as AVAudioSession provides outputVolume property. Check here.
You can just add observer on this property and get callbacks.
Here's a simple way of doing this in Swift 5:
// Audio session object
private let session = AVAudioSession.sharedInstance()
// Observer
private var progressObserver: NSKeyValueObservation!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
try session.setActive(true, options: .notifyOthersOnDeactivation)
} catch {
print("cannot activate session")
}
progressObserver = session.observe(\.outputVolume) { [weak self] (session, value) in
print(session.outputVolume)
}
return true
}

Got an "configurationError" when sending a transaction in Xcode

When I try to send a transaction on the Aion network, I keep getting the "configurationError". I am using Xcode to create my IOS dApp.
The code i have to send a transaction is:
#IBAction func sendTxButton(_ sender: Any) {
//deleted my address and pk
let address = "0x0"
let privateKey = "0x0"
let nonce = BigInt.init(3)
let to = "0xa0d969df9232b45239b577c3790887081b5a22ffd5a46a8d82584ee560485624"
let value = BigInt.init(10000000)
let nrgPrice = BigInt.init(10000000000)
let nrg = BigInt.init(21000)
var txParams = [AnyHashable: Any]()
txParams["nonce"] = HexStringUtil.prependZeroX(hex: nonce.toString(radix: 16))
txParams["to"] = to
txParams["data"] = ""
txParams["value"] = HexStringUtil.prependZeroX(hex: value.toString(radix: 16))
txParams["nrgPrice"] = HexStringUtil.prependZeroX(hex: nrgPrice.toString(radix: 16))
txParams["nrg"] = HexStringUtil.prependZeroX(hex: nrg.toString(radix: 16))
do {
let importWallet = try PocketAion.importWallet(privateKey: privateKey, subnetwork: "32", address: address, data: nil)
try PocketAion.eth.sendTransaction(wallet: importWallet, nonce: nonce, to: to, data: "", value: value, nrgPrice: nrgPrice, nrg: nrg, handler: { (result, error) in
if error != nil {
print(error!)
return
} else {
print("the hash:", result!)
}
})
} catch{
print(error)
}
}
I have met all of the requirements on sending a transaction, but cant figure out what is wrong.(this is for sending test tokens on the Aion test net "32").
check your AppDelegates class. make sure you have added "configuration" and point it to the right URL and under the application function you make sure you have the configuration is set to "self".
class AppDelegate: UIResponder, UIApplicationDelegate, Configuration {
public var nodeURL: URL{
get {
return URL.init(string: "https://aion.pokt.network")!
}
}
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
PocketAion.shared.setConfiguration(config: self)
return true
}

NSNotification getting called multiple times

I have the following code in my didFinishLaunchingWithOptions
NotificationCenter.default.addObserver(
self,
selector: #selector(addressBookDidChange),
name: NSNotification.Name.CNContactStoreDidChange,
object: nil)
This is the method it calls
#objc func addressBookDidChange(notification: NSNotification){
self.processContacts()
}
and here is the notification being removed
func applicationWillTerminate(_ application: UIApplication) {
NotificationCenter.default.removeObserver(NSNotification.Name.CNContactStoreDidChange)
}
The problem is that when I add a new contact via the method below, addressBookDidChange gets called multiple times after, not just once
func addContact(contact:ContactObject) {
let store = CNContactStore()
let contactToAdd = CNMutableContact()
contactToAdd.givenName = contact.firstName
contactToAdd.familyName = contact.lastName
contactToAdd.organizationName = contact.company
for case let contactNumber as PhoneNumberObject in contact.phoneNumbers!{
let mobileNumber = CNPhoneNumber(stringValue: contactNumber.number)
contactToAdd.phoneNumbers.append(CNLabeledValue(label: contactNumber.type.getCNLabelValue(), value: mobileNumber))
}
if let image = contact.image {
contactToAdd.imageData = UIImagePNGRepresentation(image)
}
let saveRequest = CNSaveRequest()
saveRequest.add(contactToAdd, toContainerWithIdentifier: nil)
do {
try store.execute(saveRequest)
} catch {
NSLog("Error adding contact \(contact.firstName) \(contact.lastName) : \(error)")
}
}
How can I make the notification be just called once for an addition of one contact?
I believe that it is not good idea to set notification posting based on delegate and so what should be doing is post notification from delegate checking condition whether change in notification is contact is added.

Notifications with Swift 2 and Cloudkit

I am making a "texting app" you can call it and it uses cloudkit and I have been looking everywhere to add notifications that work with cloudkit... Would someone be able to tell me the code to add push notifications for cloudkit in detail because I am very lost... Also I wan't the notifications to go to different "texting rooms" (in cloudkit it would be record types...) For instance I have one record type called "text" and another one called "text 2" I don't want notifications from "text" to get to people who use "text2" and vise versa.
Using Swift 2.0 with El Captain & Xcode 7.2.1
Elia, You need to add this to your app delegate. Which will arrive in a userInfo packet of data, which you can then parse to see which database/app sent it.
UIApplicationDelegate to the class
application.registerForRemoteNotifications() to the
func application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Than this method
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let notification = CKQueryNotification(fromRemoteNotificationDictionary: userInfo as! [String : NSObject])
let container = CKContainer(identifier: "iCloud.com")
let publicDB = container.publicCloudDatabase
if notification.notificationType == .Query {
let queryNotification = notification as! CKQueryNotification
if queryNotification.queryNotificationReason == .RecordUpdated {
print("queryNotification.recordID \(queryNotification.recordID)")
// Your notification
}
}
print("userInfo \(userInfo["ck"])")
NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: self, userInfo:dataDict)
}
}
}
}
}
That'll get you started.
You can use this method to check your subscriptions programmatically, of course while your developing you can use the dashboard.
func fetchSubsInPlace() {
let container = CKContainer(identifier: "iCloud.com")
let publicDB = container.publicCloudDatabase
publicDB.fetchAllSubscriptionsWithCompletionHandler({subscriptions, error in
for subscriptionObject in subscriptions! {
let subscription: CKSubscription = subscriptionObject as CKSubscription
print("subscription \(subscription)")
}
})
}
And finally when you got it; you can this routine to ensure you capture any subscriptions you missed while your app was sleeping and make sure that subscriptions don't go to all your devices, once you treated them too.
func fetchNotificationChanges() {
let operation = CKFetchNotificationChangesOperation(previousServerChangeToken: nil)
var notificationIDsToMarkRead = [CKNotificationID]()
operation.notificationChangedBlock = { (notification: CKNotification) -> Void in
// Process each notification received
if notification.notificationType == .Query {
let queryNotification = notification as! CKQueryNotification
let reason = queryNotification.queryNotificationReason
let recordID = queryNotification.recordID
print("reason \(reason)")
print("recordID \(recordID)")
// Do your process here depending on the reason of the change
// Add the notification id to the array of processed notifications to mark them as read
notificationIDsToMarkRead.append(queryNotification.notificationID!)
}
}
operation.fetchNotificationChangesCompletionBlock = { (serverChangeToken: CKServerChangeToken?, operationError: NSError?) -> Void in
guard operationError == nil else {
// Handle the error here
return
}
// Mark the notifications as read to avoid processing them again
let markOperation = CKMarkNotificationsReadOperation(notificationIDsToMarkRead: notificationIDsToMarkRead)
markOperation.markNotificationsReadCompletionBlock = { (notificationIDsMarkedRead: [CKNotificationID]?, operationError: NSError?) -> Void in
guard operationError == nil else {
// Handle the error here
return
}
}
let operationQueue = NSOperationQueue()
operationQueue.addOperation(markOperation)
}
let operationQueue = NSOperationQueue()
operationQueue.addOperation(operation)
}
}

Push notification displaying whole receiving content when is on background

I'm developing a chat app. I'm using apple push notification service to notify user when he receives new messages. There are two scenarios.
The first when user is chatting and receiving a message, the user shouldn't be notified (meaning that notification shouldn't be shown) and when the app is in background i want to alert user for the messages. Everything is ok except that when app is on background the notification shows the whole JSON object the client is receiving.
The idea is ignore visually notification and if its on background show a local Notification.
This is how i have implemented the notification settings
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let types: UIUserNotificationType = [UIUserNotificationType.None]
let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
{
//App handle notifications in background state
if application.applicationState == UIApplicationState.Background {
var login_user = LoginUser();
login_user.loadData();
var username:String!;
var message:String!;
if let msg = userInfo["aps"]as? Dictionary<String,AnyObject>
{
if let alert = msg["alert"] as? String{
if let data = alert.dataUsingEncoding(NSUTF8StringEncoding)
{
do
{
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data,options: [])
username = jsonObject["senderUserName"] as! String;
message = jsonObject["content"] as! String!;
DatabaseOperations().insert(DatabaseOperations().STRING_VALUE_CHATING_USERNAME, value: username);
NSNotificationCenter.defaultCenter().postNotificationName("push_notification", object: self)
}
catch
{
}
}
}
}
let localNotification: UILocalNotification = UILocalNotification()
switch(login_user.privacyLevelId)
{
case 1:
localNotification.alertBody = username + ":" + message;
break;
case 2:
localNotification.alertBody = username;
break;
case 3:
localNotification.alertBody = "New Message";
break;
default:
localNotification.alertBody = "New Message";
break;
}
localNotification.alertAction = "Message"
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
//App is shown and active
else
{
if let msg = userInfo["aps"]as? Dictionary<String,AnyObject>
{
if let alert = msg["alert"] as? String
{
if let data = alert.dataUsingEncoding(NSUTF8StringEncoding)
{
do
{
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data,options: [])
let sender:String = jsonObject["senderUserName"] as! String;
DatabaseOperations().insert(DatabaseOperations().STRING_VALUE_CHATING_USERNAME, value: sender);
NSNotificationCenter.defaultCenter().postNotificationName("push_notification", object: self)
}
catch
{
}
}
}
}
}
}
I set UIUserNotificationType to NONE. Shouldn't by default the notification shows nothing?
I also have read some other posts, but i couldn't find anything to solve the problem.
Why does UIUserNotificationType.None return true in the current settings when user permission is given?
Hide, do not display remote notification from code (swift)
Any help would be appreciated.
application didReceiveRemoteNotification won't be called if the app is closed or in the background state, so you won't be able to create a local notification. So you need to pass the text you want to display in the aps dictionnary, associated with the alert key.
If you want to pass more information for the active state case, you should add them with a custom key to the push dictionnary.
For example :
{"aps": {
"badge": 1,
"alert": "Hello World!",
"sound": "sound.caf"},
"task_id": 1}

Resources