在開發前,有兩個方式可以方便 debug:
- 建立 Progressive Web App (Universal Windows),設定 Start Page 與 WindowsRuntimeAccess = true 如下:
有宣告 WindowsRuntimeAccess 才能操作 Windows Runtime APIs。我比較建議使用這個方式。<Application Id="App" StartPage="http://localhost:65278/"> <uap:ApplicationContentUriRules> <uap:Rule Match="http://localhost:65278/" Type="include" WindowsRuntimeAccess="all" /> <uap:Rule Match="https://*.*" Type="include" WindowsRuntimeAccess="none" /> <uap:Rule Match="http://*.*" Type="include" WindowsRuntimeAccess="none" /> </uap:ApplicationContentUriRules> </Application>
- 利用 Microsoft Edge Developer Tools 幫助我們開發 PWA 與確定那些 APIs 是否能使用
利用 JavaScript 開發與使用 WinRT APIs 與 C# 大部分相同,但需要注意:
- WinRT features 在 Javascript 使用時命名大小寫有些不同,可參考 Casing Conventions with Windows Runtime Features;
- 事件注冊改用 string 名稱搭配 addEventListener 或 removeEventListener 來處理,或是event = function(ev) {} 的寫法;
var locator = new Windows.Devices.Geolocation.Geolocator(); locator.addEventListener( "positionchanged", function (ev) { console.log("Got event"); });
- 非同步執行模式使用 JavaScript Promise model,寫法如下:
更多詳細的寫法可參考:Asynchronous programming in JavaScript。client.createResourceAsync(uri, description, item) // Success. .then(function(newItem) { console.log("New item is: " + newItem.id); });
- Windows.UI.Xaml 不支援 JavaScript apps,改用 EdageHTML engine 來渲染 CSS 與 HTML
- Windows.UI.WebUI.WebUIApplication 負責 App 生命周期與相關事件,非常重要。App activated, resume, and suspend using the WRL sample 與 App activate and suspend using WinJS sample 介紹怎麽操作 actived, suspended 的事件。
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", function (activatedEventArgs) { // Check activatedEventArgs.kind and respond as needed });
- 利用 if(window.Windows){} 檢查是否支援 Windows Runtime APIs;或者是多檢查特定的 API:if (window.Windows && Windows.UI.Popups);
另外可參考 Windows UWP Namespaces 與搭配 Universal Windows Platform documentation 補充 UWP 開發的基本觀念。
列出常用到 PWA 的功能:
1. 如何支援 Push Notification
Windows Push Notification 的機制可以參考之前的文章:Universal App - 整合 Windows Notification Service (WNS) for Server 與 Universal App - 整合 Windows Notification Service (WNS) for Client。PWA 中注冊取得 WNS channel uri
- 先到 Microsoft Dev Center 的專案啓動 WNS 服務,如下圖:Server side 建議參考sample code來測試;
- 利用 VS2017 讓 PWA app 關聯到 Microsoft Dev Center 注冊的專案,更新 package.appxmanifest 的參數;重點在:<Identity Name="PouMason.PROJECT1" Publisher="CN=4CABE714-48E4-AC4F-7F16-A5774B167C16F8" Version="1.1.2.0" />
- 加入 JavaScript 操作 CreatePushNotificationChannelForApplicationAsync 來得到 channel_uri;
var wnsChannel; // 注冊 push notification if (typeof Windows !== 'undefined' && typeof Windows.UI !== 'undefined' && typeof Windows.UI.Notifications !== 'undefined') { Windows.Networking.PushNotifications.PushNotificationChannelManager.createPushNotificationChannelForApplicationAsync() .then(function (newChannel) { console.log(newChannel); wnsChannel = newChannel; wnsChannel.addEventListener("pushnotificationreceived", pushNotificationReceivedHandler, false); }, function (error) { console.log(error); }); }
- 處理收到的訊息:
function pushNotificationReceivedHandler(e) { var notificationTypeName = ""; var notificationPayload; var pushNotifications = Windows.Networking.PushNotifications; // 拆解對應的類型 switch (e.notificationType) { case pushNotifications.PushNotificationType.toast: notificationTypeName = "Toast"; notificationPayload = e.toastNotification.content.getXml(); break; case pushNotifications.PushNotificationType.tile: notificationTypeName = "Tile"; notificationPayload = e.tileNotification.content.getXml(); break; case pushNotifications.PushNotificationType.badge: notificationTypeName = "Badge"; notificationPayload = e.badgeNotification.content.getXml(); break; } e.cancel = true; // 取得 payload 中的内容 var xmlDox = new Windows.Data.Xml.Dom.XmlDocument(); xmlDox.loadXml(notificationPayload); var textElements = xmlDox.getElementsByTagName("text") var messageDialog = new Windows.UI.Popups.MessageDialog(notificationTypeName, textElements[0].innerText); messageDialog.showAsync(); }
關於 JavaScript 注冊與處理 Push Notification 的事件可以參考 PushNotificationReceivedEventArgs Class。
另外,如果想要讓 App 開啓時收到 Push notification 就做處理,建議可以把注冊的邏輯放到 Service Worker 裏面,讓背景來處理。
更多細節可參考 Push notifications in a PWA running on Windows 10 的範例。
2. 在 PWA 中發送 Toast
function (message, iconUrl) {
// 檢查是否支援 Windows Runtime API
if (typeof Windows !== 'undefined' &&
typeof Windows.UI !== 'undefined' &&
typeof Windows.UI.Notifications !== 'undefined') {
var notifications = Windows.UI.Notifications;
// 利用 ToastTemplateType 列舉選擇要用的範本
var template = notifications.ToastTemplateType.toastImageAndText01;
// 轉成 XML
var toastXml = notifications.ToastNotificationManager.getTemplateContent(template);
var textElements = toastXml.getElementsByTagName("text");
textElements[0].appendChild(toastXml.createTextNode(message));
var imageElements = toastXml.getElementsByTagName("image");
// 設定 image 的 src 屬性
var srcAttr = toastXml.createAttribute("src");
srcAttr.value = iconUrl;
var attribs = imageElements[0].attributes;
attribs.setNamedItem(srcAttr);
// 建立 toast 並發送
var toast = new notifications.ToastNotification(toastXml);
var toastNotifier = notifications.ToastNotificationManager.createToastNotifier();
toastNotifier.show(toast);
}
}
可以根據需求 ToastTemplateType Enum 調整需要的範本。3. 在 App 的 Local folder 讀寫檔案
參考 ApplicationData Class 來操作:讀取檔案
function (fileName) {
if (typeof Windows !== 'undefined' &&
typeof Windows.Storage !== 'undefined' &&
typeof Windows.Storage.ApplicationData !== 'undefined') {
var localFolder = Windows.Storage.ApplicationData.current.localFolder;
localFolder.getFileAsync(fileName)
.then(function (file) {
// 抓到檔案並讀取
return Windows.Storage.FileIO.readTextAsync(file);
}, function (ex) {
console.log(ex);
})
.done(function (content) {
console.log(content);
});
}
}
寫入檔案function (fileName, content) {
if (typeof Windows !== 'undefined' &&
typeof Windows.Storage !== 'undefined' &&
typeof Windows.Storage.ApplicationData !== 'undefined') {
var localFolder = Windows.Storage.ApplicationData.current.localFolder;
localFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting)
.then(function (file) {
// 抓到檔案並寫入
return Windows.Storage.FileIO.writeTextAsync(file, content);
})
.done(function () {
console.log("saved.");
});
}
}
4. 操作 User Activity
在 time line 加入 activityfunction (activityId) {
if (typeof Windows !== 'undefined' &&
typeof Windows.ApplicationModel !== 'undefined' &&
typeof Windows.ApplicationModel.UserActivities !== 'undefined') {
createActivity(activityId).the(function () {
console.log("done");
});
}
};
async function createActivity(activityId) {
var channel = Windows.ApplicationModel.UserActivities.UserActivityChannel.getDefault();
var activity = await channel.getOrCreateUserActivityAsync(activityId);
if (activity.state == Windows.ApplicationModel.UserActivities.UserActivityState.new) {
activity.visualElements.displayText = "new activity";
activity.activationUri = new Windows.Foundation.Uri('testapp://mainPage?state=new&id=' + activityId);
} else {
activity.visualElements.displayText = "published activity";
activity.activationUri = new Windows.Foundation.Uri('testapp://mainPage?state=published&id=' + activityId);
}
activity.contentInfo = Windows.ApplicationModel.UserActivities.UserActivityContentInfo.fromJson('{ "user_id": "pou", "email": "poumason@live.com"}');
await activity.saveAsync();
var activitySesion = activity.createSession();
}
利用 async function 處理 then 裏面還有非同步方法的複雜寫法。處理點擊 User Activity 帶入的 protocol
if (typeof Windows !== 'undefined' &&
typeof Windows.UI !== 'undefined' &&
typeof Windows.UI.WebUI !== 'undefined') {
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", function (activatedEventArgs) {
console.log(activatedEventArgs);
if (activatedEventArgs.kind == Windows.ApplicationModel.Activation.ActivationKind.protocol ) {
var query = activatedEventArgs.uri.queryParsed;
console.log(query);
for (var i = 0; i < query.length; i++) {
console.log(query[i].name + '=' + query[i].value);
}
}
});
}
[範例]
DotblogsSampleCode/27-PWASample/
======
以上是我研究 PWA 使用 Windows Runtime APIs 的心得,希望有助於大家快速理解。
如果有寫錯的地方,也歡迎大家留言讓我知道,謝謝大家。
References:
- Using the Windows Runtime in JavaScript
- Progressive Web Apps
- Progressive Web App debugging & Tailor your PWA for Windows
- Asynchronous programming in JavaScript
- PWA Tools & Microsoft Edge Developer Tools
- Benefits of PWA on Windows 10s & Windows Runtime APIs
- Service Worker API & Service Worker
- Browser push notifications using JavaScript & Introduction to Push Notifications
- 在傳統 Web 應用程式和單頁應用程式 (SPA) 之間作選擇
- PWA talk at Build 2018 (1:23:05)
- Push notifications in a PWA running on Windows 10
- [UWP]Whats is difference between PWA and UWP?
- Submit your PWA to the Microsoft Store
- ManifoldJS & Web App Manifest
- JavaScript Promises: an Introduction
- Uri Class & 異步函數 - 提高 Promise 的易用性
- XmlDocument Class
- Progressive Web Apps on Windows 10: Live Tiles, Toast Notifications and Action Center
- Sending push notifications with WNS (XAML)
- PushNotificationChannel.PushNotificationReceived Event
- EventTarget.addEventListener()
- 側載 UWP app 的代理 Windows 執行階段元件
沒有留言:
張貼留言