Notification Content Extension is not appearing for multiple category name? - swift3

I have added Custom Remote Notification Content Extension to my project and added multiple Extension categories into the Notification Content Extension target info.plist file like the following:
added different types of notification action categories for different notifications into the AppDelegate:
func addRichRotificationActions() {
let confirmAction = UNNotificationAction(identifier: "ConfirmAction", title: "Confirm", options: [.foreground])
let cancelAction = UNNotificationAction(identifier: "CancelAction", title: "Cancel", options: [.destructive])
let closeAction = UNNotificationAction(identifier: "CloseAction", title: "Close", options: [.foreground])
let openTicketCategory = UNNotificationCategory(identifier: "OpenTicket", actions: [confirmAction, cancelAction], intentIdentifiers: [], options: [])
let confirmTicketCategory = UNNotificationCategory(identifier: "ConfirmTicket", actions: [closeAction, cancelAction], intentIdentifiers: [], options: [])
let closeTicketCategory = UNNotificationCategory(identifier: "CloseTicket", actions: [], intentIdentifiers: [], options: [])
let cancelTicketCategory = UNNotificationCategory(identifier: "CancelTicket", actions: [], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([openTicketCategory, confirmTicketCategory, closeTicketCategory, cancelTicketCategory])
}
Now I am sending the apns json following way:
For Open tickets getting category name as "OpenTicket":
[AnyHashable("default"): You have a new ticket, AnyHashable("aps"): {
alert = "#8556 - New Booking for Mr. Tomas";
badge = 1;
category = OpenTicket;
"mutable-content" = 1;
sound = default;
}]
For Confirm tickets getting category name as "ConfirmTicket":
[AnyHashable("default"): You have a confirmed ticket, AnyHashable("aps"): {
alert = "#8556 - Ticket Confirmed for Mr. Tomas";
badge = 1;
category = ConfirmTicket;
"mutable-content" = 1;
sound = default;
}]
and so on.
But unfortunately, I am receiving the default notification with different action buttons rather than the custom notification content extension with different actions. I can't able to figure out the problem. How is it possible to get notification content extension with different actions for remote notification?

Just need to make the UNNotificationExtensionCategory as an Array rather than String in the info.plist of Notification Content Extension target.

Related

WatchOS, SwiftUI: How to send local notifications with a 'View' as body

When creating the XCode project, I selected Apple Watch Application and enabled "Include Notification".
This lead me to have a NotificationView.swift and a NotificationController.swift in my project.
I have filled the NotificationView.swift with the View content i would like to be in my local notification.
In my regular HostingController.swift I would now like to schedule a local notification with the content of NotificationView.swift.
So far, I am using the current code:
let userNotificationCenter = UNUserNotificationCenter.current()
let notificationContent = UNMutableNotificationContent()
notificationContent.title = "title"
notificationContent.body = "body"
notificationContent.categoryIdentifier = "categoryNameDummy"
let category = UNNotificationCategory(identifier: "categoryNameDummy", actions: [], intentIdentifiers: [] as? [String] ?? [String](), options: .customDismissAction)
let categories = Set<AnyHashable>([category])
userNotificationCenter.setNotificationCategories(categories as? Set<UNNotificationCategory> ?? Set<UNNotificationCategory>())
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: trigger)
userNotificationCenter.add(request) { (error) in
if let error = error {
debugPrint(error)
}
}
I have not made any changes to Info.plist regarding the categoryIdentifier.
What and where do I have to add code to now "catch" this notification and fill it with my custom NotificationView.swift content?
Did you ask the user for permission to send notifications?
UNUserNotificationCenter.current().requestAuthorization(options: [.sound, .alert]) { success, error in
if success {
print("Ok!")
} else if let error = error {
print(error.localizedDescription)
}
}
It only presents a permission alert the first time: once given permission it won't show up again.

open graph Meta Tags in Vue JS don't show image

I design a blog and I would like that when sharing to social networks, the preview image displays like in Medium's posts
<meta property="og:image" content="https://medium.com/js-dojo/getting-your-head-around-vue-js-scoped-slots-281bf82a1e4e"/>
I using vuejs2 with webpack and vue-meta to change the dynamic image of my post.
But for Facebook, there don't working even when I put them in index.html.
I find this article on medium where it is said that it is necessary to use Server Side Rendered, but it is not said how to migrate from a totally designed project with a basic configuration (without SSR) to a project solving the problem. already the architecture is different and I have no reference
here is my code vue-meta
metaInfo () {
return {
title: '41devs | blog',
titleTemplate: '%s - le titre',
meta: [
{name: 'viewport', content: 'user-scalable=no'},
{property: 'og:title', content: 'title'},
{property: 'og:type', content: 'article'},
{property: 'og:url', content: 'http://c5e3b0ec.ngrok.io/blog/s'},// here it is just ngrok for my test
{property: 'og:description', content: 'description'},
{property: 'og:image', content: 'https://firebasestorage.googleapis.com/v0/b/dev-blog-2503f.appspot.com/o/postsStorage%2F-KxXdvvLqDHBcxdUfLgn%2Fonfleck?alt=media&token=24a9bf5b-dce2-46e8-b175-fb63f7501c98'},
{property: 'twitter:image:src', content: 'https://firebasestorage.googleapis.com/v0/b/dev-blog-2503f.appspot.com/o/postsStorage%2F-KxXdvvLqDHBcxdUfLgn%2Fonfleck?alt=media&token=24a9bf5b-dce2-46e8-b175-fb63f7501c98'},
{property: 'og:image:width', content: '1000'},
{property: 'og:site_name', content: '41devs | blog'}
]
}
}
When Facebook checks your page to find the meta data, they don't run your Javascript. Vue never runs, your tags are never replaced. This is a limitation of Facebook's crawler.
This means you would indeed have to render those tags at the server level, whether by Vue's server side rendering or by some other method (I don't know what type of server you are running). But yes, ultimately, you must be able to hard-code this value into your server response, otherwise it won't display in Facebook.
The way I handled this was to create some middleware that parses the URL path and dynamically updates the meta tags with the Cheerio module.
File for OG meta tag info:
const products = [
{
id: 111111111,
title: 'Corporate Fat Cat',
ogImage: 'https://cdn.com/corporate.jpg',
description: 'The fat cats in Washington don’t even look this good'
},
{
id: 222222222,
title: 'Gangsta Cat',
ogImage: 'https://cdn.com/gangsta.jpg',
description: 'That’s how we roll'
},
{
id: 333333333,
title: 'Mechanic Cat',
ogImage: 'https://cdn.com/mechanic.jpg',
description: 'I have no idea what I’m doing.'
}
];
Middleware:
app.use('/*', (req, res, next) => {
if (/^\/api\//.test(req.originalUrl)) next();
else if (/\/item\//.test(req.originalUrl)) updateMetaTags(req, res);
else res.sendFile(`${__dirname}/client/dist/index.html`);
});
updateMetaTags function:
async function updateMetaTags(req, res) {
// Get and parse products array from app src
const productsSrc = `${__dirname}/client/src/products.js`;
const productsText = await fs.promises.readFile(productsSrc);
const productsArr = JSON.parse(productsText);
// Retrieve product object that includes the current URL item id
const productID = (req.originalUrl.match(/\d{9}/) || [])[0];
const productObj = productsArr.find(prod => prod.id == productID);
// Update the meta tag properties in the built bundle w/ Cheerio
const baseSrc = `${__dirname}/client//dist/index.html`;
const baseHTML = await fs.promises.readFile(baseSrc);
const $base = $(baseHTML);
const $url = $base.find('meta[property=og\\:url]');
const $title = $base.find('meta[property=og\\:title]');
const $image = $base.find('meta[property=og\\:image]');
$desc = $base.find('meta[property=og\\:description]');
$url.attr('content', `https://${req.get('host')}${req.originalUrl}`);
$title.attr('content', productObj.title);
$image.attr('content', productObj.ogImage);
$desc.attr('content', productObj.description);
// Send the modified HTML as the response
res.send($.html($base));
}
I have this approached detailed more in this blog post.
Your title and titleTemplate has wrong structure.
return {
title: 'Le titre', // Set a current title of page
titleTemplate: '%s - 41devs blog', // Name of your blog/website,
// Title is now "Le titre - 41devs blog"
meta: [ ...
]
}
It performed for best SEO in google https://support.google.com/webmasters/answer/79812?hl=en

Loopback: How to add afterRemote of a model to another model

I have Notification model which looks like this
"use strict";
module.exports = function(Notification) {
};
And I have another model which is Post:
"use strict";
module.exports = function(Post) {
Post.prototype.postLike = function(options, cb) {
this.likes.add(options.accessToken.userId);
cb(null, "sucess");
};
Post.remoteMethod("postLike", {
isStatic: false,
accepts: [{ arg: "options", type: "object", http: "optionsFromRequest" }],
returns: { arg: "name", type: "string" },
http: { path: "/like", verb: "post" }
});
}
What I want is to add afterRemote method of Post inside of notification model ?
Is it possible in loopback ?
It should looks like :
"use strict";
module.exports = function(Notification) {
var app = require("../../server/server.js");
var post = app.models.Post;
post.afterRemote('prototype.postLike', function(context, like, next) {
console.log('Notification after save for Like comment');
});
};
But this does not work.
NOTE: I can do it Post model itself, but I want to add all of my notification logic in Notification model for simplification and future customization.
You can use events to do.
Loopback application emits started event when it started after all boot scripts loaded here
and in Notification model do like this :
"use strict";
module.exports = function(Notification) {
var app = require("../../server/server.js");
app.on('started', function(){
var post = app.models.Post;
post.afterRemote('prototype.postLike', function(context, like, next) {
console.log('Notification after save for Like comment');
});
});
};
Or create a boot script and emit a custom event like 'allModelsLoaded'. So make sure the boot script is the last one to be run. Boot scripts run in alphabetic order by default. So make z.js and emit that custom event there then listen to that event in Notification model.
Loopback boot process first loads models, and then invoke boot scripts once all models have been loaded. If your aim is to consolidate things across models, then it is better to do this in a boot script, rather than in model.js file.

How to create a multi-use partial "template" in AngularJS?

I have a large list of items. Each item has it's own details.
In my main view/partial, I simply display a large list list of the item names.
When the user clicks on an item, I want the page to go to a partial which works as a "template", displaying information based on which list item is clicked, and hence possibly what the URL looks like. E.g. /listItem1/
This diagram below hopefully sums up what I want to achieve pretty clearly.
How can I do this?
Right now, I have a pretty standard set up in which I have all the information for each list item in an array of object literals, which is contained in a controller injected into the main app module. Like so:
var app = angular.module('app', [/*nodependencies*/]);
var controllers = {};
app.controller(controllers);
controllers.listController = function ($scope){
$scope.list = [
{name: 'List Item 1 Name', detail1: 'blahblah1', detail2: 'blahblah2'},
{name: 'List Item 2 Name', detail1: 'blahblah1', detail2: 'blahblah2'},
{name: 'List Item 3 Name', detail1: 'blahblah1', detail2: 'blahblah2'}
..... and so on
I know how to create basic views/partials as well. But what would be my next steps?
You can do what you want, using the built-in router which ships with AngularJS.
var app = angular.module('app', [/*nodependencies*/])
.config(function($routeProvider) {
$routeProvider
.when('/:itemId', {
templateUrl: '/path/to/partial',
controller : function($scope, $routeParams) {
$scope.item = $routeParams.itemId;
}
})
});
Basically, what the above means, is that if you browse to pdf/item/1
Then you will have access in your controller to $routeParams.itemId which will be equal to 1. You can then do whatever logic is necessary with this information on your partial to show the information you want.
Hope this helps.
Update
Please look at the controller, this is how you would get the param you passed via the URL, you would then do whatever it is you need to do with that param in the controller, and pass the data back to the view.
You can create a small directive that will use the multi-use partial to display each item on the list
Take a look at this working example (http://plnkr.co/edit/0jNVxRg6g3p8uxpustzz?p=preview)
var myApp = angular.module('myApp', []);
myApp.controller('listController', ['$scope', function ($scope) {
$scope.list = [
{
name: 'List Item 1 Name',
url: 'pdfs/item1.pdf',
detail: 'blahblah'
},
{
name: 'List Item 2 Name',
url: 'pdfs/item2.pdf',
detail: 'blahblah'
},
{
name: 'List Item 3 Name',
url: 'pdfs/item3.pdf',
detail: 'blahblah'
}
];
$scope.selectItem = function(item){
$scope.selected = item;
}
}]);
myApp.directive('listItem', [function () {
return {
restrict: 'A',
scope: {
item: '='
},
templateUrl: 'multiple-partial.html',
link: function (scope, element, iAttrs) {
}
};
}])

How to change tab programmatically?

I am creating an iOS app using RubyMotion. I would like to create a custom looking tabBar therefor I need to know how to change tab programmatically and how to get the current tab. Is this possible? How can I do it?
You could try using motion-tab to create the tab bar.
Basic Usage (from the Readme)
def application(application, didFinishLaunchingWithOptions:launchOptions)
#window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
tabs = [
{
systemIcon: UITabBarSystemItemContacts,
navigationController: true,
viewController: ContactsViewController
}, {
title: "Custom",
icon: "custom.png",
navigationController: false,
viewController: CustomViewController.alloc.initWithCustomInit(true)
}, {
title: "Settings",
icon: "settings.png",
navigationController: true,
viewController: SettingsViewController
}
]
tabBarController = MotionTab::TabBar.createTabBarControllerFromData(tabs)
MotionTab::TabBar.select(tabBarController, title: "Settings")
# MotionTab::TabBar.select(tabBarController, tag: 0) # Selects first tab
#window.rootViewController = tabBarController
#window.makeKeyAndVisible
end
The gem doesn't have the current tab functionality now, but it wouldn't be too difficult to add a class attribute to MotionTab::TabBar that returns the current tab.