I have the following webview implementation built by borrowing code (I am learning SwiftUI). I want to show a message in the webview when the networks is down. It works with a static html loaded from a string and a button but I am unable to do the same calling a web server and triggering javascript using the network status.
import SwiftUI
import WebKit
import Combine
private let urlString: String = "http://127.0.0.1"
class WebViewData: ObservableObject {
#Published var parsedText: NSAttributedString? = nil
var functionCaller = PassthroughSubject<Void,Never>()
var isInit = false
var shouldUpdateView = true
}
struct WebView: UIViewRepresentable {
#StateObject var data: WebViewData
func makeUIView(context: Context) -> WKWebView {
let wkWebview = WKWebView()
wkWebview.navigationDelegate = context.coordinator
return wkWebview
}
func updateUIView(_ uiView: WKWebView, context: Context) {
guard data.shouldUpdateView else {
data.shouldUpdateView = false
return
}
context.coordinator.tieFunctionCaller(data: data)
context.coordinator.webView = uiView
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
uiView.load(request)
}
func makeCoordinator() -> WebViewCoordinator {
return WebViewCoordinator(view: self)
}
func viewWillAppear(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>){
}
}
class WebViewCoordinator : NSObject, WKNavigationDelegate {
var parent: WebView
var webView: WKWebView? = nil
private var cancellable : AnyCancellable?
init(view: WebView) {
self.parent = view
super.init()
}
func tieFunctionCaller(data: WebViewData) {
cancellable = data.functionCaller.sink(receiveValue: { _ in
self.webView?.evaluateJavaScript("message_generator(\"network_down\"")
})
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
DispatchQueue.main.async {
if !self.parent.data.isInit {
self.parent.data.isInit = true
}
}
}
}
Here the ContentView:
import SwiftUI
struct ContentView: View {
#StateObject var webViewData = WebViewData()
#ObservedObject var networkManager = NetworkManager()
var body: some View {
ZStack{
Color.white
VStack {
ExecuteCode {
print(networkManager.isConnected)
webViewData.functionCaller.send() //???
}
WebView(data: webViewData)
if (!networkManager.isConnected) {
//???
}
}
.onReceive(webViewData.$parsedText, perform: { parsedText in
if let parsedText = parsedText {
print(parsedText)
}
})
}
}
}
struct ExecuteCode : View {
init( _ codeToExec: () -> () ) {
codeToExec()
}
var body: some View {
return EmptyView()
}
}
Here the NetworkManager:
import Foundation
import Network
class NetworkManager: ObservableObject {
let monitor = NWPathMonitor()
let queue = DispatchQueue(label: "NetworkManager")
#Published var isConnected = true
var imageName: String {
return isConnected ? "wifi" : "wifi.slash"
}
var connectionDescription: String {
if isConnected {
return "Internet connection looks good!"
} else {
return "It looks like you're not connected to the internet. Make sure WiFi is enabled and try again"
}
}
init() {
monitor.pathUpdateHandler = { path in
DispatchQueue.main.async {
if path.status == .satisfied {
self.isConnected = true
} else {
self.isConnected = false
}
}
}
monitor.start(queue: queue)
}
}
Thanks for helping.
Pointers to good docs are very appreciated.
Related
Afternoon! For starters, I'm using Kotlin to build a tinder-like app with cards and a swiping function inside Android Studio.
I'm trying to populate these cards with an image and 2 text-fields with data from Firebase.. And I can't seem to do it. My difficulty with it seems trivial but it's tripping me up pretty bad.
It looks to me like the app is correctly reading the only 2 datasets currently in the database, evident by only showing 2 cards - but I can't get it to read and display the name, location and image of each of the datasets.
I've attached the CardAdapter.kt file where the values aren't getting read / updated here:
package com.sit708.coupledApp.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.bumptech.glide.Glide
import com.sit708.coupledApp.R
import com.sit708.coupledApp.util.Dates
import com.sit708.coupledApp.activities.UserInfoActivity
class CardsAdapter(context: Context?, resourceId: Int, dates: List<Dates>): ArrayAdapter<Dates>(
context!!, resourceId, dates) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val date = getItem(position)
val finalView = convertView ?: LayoutInflater.from(context).inflate(R.layout.item, parent, false)
val name = finalView.findViewById<TextView>(R.id.nameTV)
val userInfo = finalView.findViewById<LinearLayout>(R.id.userInfoLayout)
val image = finalView.findViewById<ImageView>(R.id.photoIV)
/*THESE VALUES AREN'T GETTING READ, UPDATED OR DISPLAYED*/
name.text = "${date?.dateName}, ${date?.dateLocation}"
Glide.with(context)
.load(date?.dateImg)
.into(image)
userInfo.setOnClickListener {
finalView.context.startActivity(UserInfoActivity.newIntent(finalView.context, date?.dateID))
}
return finalView
}
}
Here you can see that the comma is being displayed but not the name or location
Here is my SwipeFragment.kt just in case this proves useful:
package com.sit708.coupledApp.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
import com.lorentzos.flingswipe.SwipeFlingAdapterView
import com.sit708.coupledApp.R
import com.sit708.coupledApp.util.Dates
import com.sit708.coupledApp.activities.TinderCallback
import com.sit708.coupledApp.adapters.CardsAdapter
import com.sit708.coupledApp.databinding.FragmentSwipeBinding
import com.sit708.coupledApp.util.*
class SwipeFragment : Fragment() {
private var _binding: FragmentSwipeBinding? = null
private val binding get() = _binding!!
private var callback: TinderCallback? = null
private lateinit var dateId: String
private lateinit var dateDatabase: DatabaseReference
private lateinit var chatDatabase: DatabaseReference
private lateinit var userDatabase: DatabaseReference
private var cardsAdapter: ArrayAdapter<Dates>? = null
private var rowItems = ArrayList<Dates>()
private var dateName: String? = null
private var dateImg: String? = null
private var dateLocation: String? = null
fun setCallback(callback: TinderCallback) {
this.callback = callback
dateId = callback.onGetUserId()
userDatabase = callback.getUserDatabase()
chatDatabase = callback.getChatDatabase()
dateDatabase = callback.getDateDatabase()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSwipeBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dateDatabase.child(dateId).addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
val date = p0.getValue(Dates::class.java)
dateName = date?.dateName
dateImg = date?.dateImg
dateLocation = date?.dateLocation
populateItems()
}
})
cardsAdapter = CardsAdapter(context, R.layout.item, rowItems)
binding.frame.adapter = cardsAdapter
binding.frame.setFlingListener(object : SwipeFlingAdapterView.onFlingListener {
override fun removeFirstObjectInAdapter() {
rowItems.removeAt(0)
cardsAdapter?.notifyDataSetChanged()
}
override fun onLeftCardExit(p0: Any?) {
var date = p0 as Dates
dateDatabase.child(date.dateID.toString()).child(DATA_SWIPES_LEFT).child(dateId).setValue(true)
}
override fun onRightCardExit(p0: Any?) {
val selectedDates = p0 as Dates
val selectedDateId = selectedDates.dateID
if (!selectedDateId.isNullOrEmpty()) {
dateDatabase.child(dateId).child(DATA_SWIPES_RIGHT)
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
if (p0.hasChild(selectedDateId)) {
Toast.makeText(context, "Match!", Toast.LENGTH_SHORT).show()
val chatKey = chatDatabase.push().key
if (chatKey != null) {
dateDatabase.child(dateId).child(DATA_SWIPES_RIGHT).child(selectedDateId)
.removeValue()
dateDatabase.child(dateId).child(DATA_MATCHES).child(selectedDateId)
.setValue(chatKey)
dateDatabase.child(selectedDateId).child(DATA_MATCHES).child(dateId)
.setValue(chatKey)
chatDatabase.child(chatKey).child(dateId).child(DATA_DATE_NAME).setValue(dateName)
chatDatabase.child(chatKey).child(dateId).child(DATA_DATE_IMAGE)
.setValue(dateImg)
chatDatabase.child(chatKey).child(selectedDateId).child(DATA_NAME)
.setValue(selectedDates.dateName)
}
} else {
dateDatabase.child(selectedDateId).child(DATA_SWIPES_RIGHT).child(dateId)
.setValue(true)
}
}
})
}
}
override fun onAdapterAboutToEmpty(p0: Int) {
}
override fun onScroll(p0: Float) {
}
})
binding.likeButton.setOnClickListener {
if (rowItems.isNotEmpty()) {
binding.frame.topCardListener.selectRight()
}
}
binding.dislikeButton.setOnClickListener {
if (rowItems.isNotEmpty()) {
binding.frame.topCardListener.selectLeft()
}
}
}
fun populateItems() {
binding.noUsersLayout.visibility = View.GONE
binding.progressLayout.visibility = View.VISIBLE
val cardsQuery = dateDatabase
cardsQuery.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach { child ->
val date = child.getValue(Dates::class.java)
if (date != null) {
var showDate = true
if (child.child(DATA_SWIPES_LEFT).hasChild(dateId) ||
child.child(DATA_SWIPES_RIGHT).hasChild(dateId) ||
child.child(DATA_MATCHES).hasChild(dateId)
) {
showDate = false
}
if (showDate) {
rowItems.add(Dates())
cardsAdapter?.notifyDataSetChanged()
}
}
}
binding.progressLayout.visibility = View.GONE
if (rowItems.isEmpty()) {
binding.noUsersLayout.visibility = View.VISIBLE
}
}
})
}
}
Here is a screenshot of my database with only 2 sets of data that I'd like it to read from
If you need anymore info let me know and but any guidance would be greatly appreciated!
Small screenshot of my Data.kt file
I am trying to create a library that open a web view in a class, where the objetive is opened it but does not see it in the screen. I created and try to execute a javaScript that makes a postMessage, to obtain the result for example in the method userContentController(), but I am getting and error when I execute it. Also, I am used webView.callAsyncJavaScript, cause then I want to execute some async functions that webView.evaluateJavaScript does not let. Some knows what could be the reason that can help me. Thanks.
Code:
import WebKit
class FingerprintJs : NSObject, WKScriptMessageHandler, WKNavigationDelegate{
public func tryFinger(){
getWebView()
}
public func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage){
print("userContentController")
print(message.name)
}
private func getWebView(){
if let viewController = UIWindow.key?.rootViewController {
if Thread.isMainThread {
let webView = self.makeWebView(viewController: viewController as! ViewController)
webView.loadHTMLString(getHtml(), baseURL: nil)
if #available(iOS 14.0, *) {
print("bunea")
webView.callAsyncJavaScript(getJavaScript()!, arguments: [:], in: nil, in: .defaultClient, completionHandler:{(result) in
print("queeeeee")
print(result as Any)
})
} else {
print("Llegaste aca? porqueee ")
// Fallback on earlier versions
}
// webView.evaluateJavaScript(getJavaScript()!,completionHandler:{(result , error) in
// if error == nil {
// print("success")
// print(result as Any)
// }
// else {
// print("error in executing js ", error!)
// }
// })
} else {
print("Llegaste aca? ")
}
}else{
NSLog("The UIApplication keyWindow of the rootViewController must be loaded first", "")
}
}
private func makeWebView(viewController: ViewController) -> WKWebView {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "message5")
config.preferences = preferences
let webView = WKWebView(frame: .init(x: 1.0, y: 1.0, width: 0, height: 0),
configuration: config)
webView.translatesAutoresizingMaskIntoConstraints = false
viewController.view.addSubview(webView)
return webView
}
private func getJavaScript() -> String?{
var javaScript: String? = nil
if let jsSourcePath = Bundle.main.path(forResource: "ejemplo", ofType: "js") {
do {
javaScript = try String(contentsOfFile: jsSourcePath)
}
catch {
NSLog("getJavaScriptString: "+(error.localizedDescription), "")
}
}
return javaScript
}
private func getHtml() -> String {
let html: String =
"""
<html>
<body>
<script>
</script>
</body>
</html>
"""
return html
}
private var webView: WKWebView? {
didSet {
oldValue?.removeFromSuperview()
oldValue?.configuration.userContentController.removeScriptMessageHandler(forName: "message5")
}
}
}
extension UIWindow {
static var key: UIWindow? {
if #available(iOS 13, *) {
return UIApplication.shared.windows.first { $0.isKeyWindow }
} else {
return UIApplication.shared.keyWindow
}
}
}
Javascript:
testFunct();
function testFunct() {
window.webkit.messageHandlers.message5.postMessage("nice" )
}
Error:
failure(Error Domain=WKErrorDomain Code=4 "A JavaScript exception occurred" UserInfo={WKJavaScriptExceptionLineNumber=0, WKJavaScriptExceptionMessage=TypeError: undefined is not an object (evaluating 'window.webkit.messageHandlers'), WKJavaScriptExceptionColumnNumber=0, NSLocalizedDescription=A JavaScript exception occurred})
In addition for example here I have some changes of code but for example I never receive in method userContentController the message. Where I set the window.webkit.messageHandlers in the html.
import WebKit
import UIKit
class FingerprintJs : NSObject, WKScriptMessageHandler, WKNavigationDelegate{
public func tryFinger(){
getWebView()
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
assert(message.name == "myMessageName")
}
private func getWebView(){
if let viewController = UIWindow.key?.rootViewController {
if Thread.isMainThread {
let webView = self.makeWebView(viewController: viewController as! ViewController)
webView.loadHTMLString(getHtml(), baseURL: nil)
// webView.evaluateJavaScript(getJavaScript()!,completionHandler:{(result , error) in
// if error == nil {
// print("success")
// print(result as Any)
// }
// else {
// print("error in executing js ", error!)
// }
// })
} else {
print("Llegaste aca? ")
}
}else{
NSLog("The UIApplication keyWindow of the rootViewController must be loaded first", "")
}
}
private func makeWebView(viewController: ViewController) -> WKWebView {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "myMessageName")
config.preferences = preferences
let webView = WKWebView(frame: .init(x: 1.0, y: 1.0, width: 0, height: 0),
configuration: config)
webView.translatesAutoresizingMaskIntoConstraints = false
viewController.view.addSubview(webView)
webView.navigationDelegate = self
return webView
}
private func getJavaScript() -> String?{
var javaScript: String? = nil
if let jsSourcePath = Bundle.main.path(forResource: "ejemplo", ofType: "js") {
do {
javaScript = try String(contentsOfFile: jsSourcePath)
}
catch {
NSLog("getJavaScriptString: "+(error.localizedDescription), "")
}
}
return javaScript
}
private func getHtml() -> String {
let html: String =
"""
<html>
<body>
<script>
window.webkit.messageHandlers.myMessageName.postMessage("message")
</script>
</body>
</html>
"""
return html
}
private var webView: WKWebView? {
didSet {
oldValue?.removeFromSuperview()
oldValue?.configuration.userContentController.removeScriptMessageHandler(forName: "message5")
}
}
}
extension UIWindow {
static var key: UIWindow? {
if #available(iOS 13, *) {
return UIApplication.shared.windows.first { $0.isKeyWindow }
} else {
return UIApplication.shared.keyWindow
}
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("testFunct()")
}
try calling the JS method from didFinish navigation method once the webView is loaded. It might be due to calling the JS method before the webView is properly loaded.
I am new in the Aurelia community and it was given to me a task to make an entire upgrade of my current platform. (more info at the bottom).
Current problem:
Every time i redirect to the logout.js model a message is prompt
ERROR [app-router] TypeError: "this.view is null"
Questions:
How does a custom component "if-permission" can influence on non-view model?
Conlusions:
- I started to believe that any of the big files bellow are influencing the error at all! After commenting most of the code the error what still showing!
- Removed the noView() logic and added an empty logout.html! Guess what? Works like a charm! The logout will redirect to the login page.
This is my RouteConfig.js
{
route: 'logout',
viewPorts: {
main: {
moduleId: PLATFORM.moduleName('pages/logout/logout')
}
},
nav: false,
sidebar: false,
auth: false,
title: 'Logout',
name: 'logout',
}
This is my logout.js
import { noView } from 'aurelia-framework';
import authService from 'services/authService';
import uiService from 'services/uiService';
#noView()
export class LogoutPage {
activate() {
//THE ERROR PROMPTS EVEN WITH THE ABOVE LINES COMMENTED
uiService.impersonate(null, false);
authService.logout();
}
}
After searching a while i noticed that "this.view" is declared on this 2 files:
if-permission.js
import { inject, customAttribute, templateController, BoundViewFactory, ViewSlot } from 'aurelia-framework';
import userService from 'services/api/userService';
#customAttribute('if-permission')
#inject(BoundViewFactory, ViewSlot)
#templateController
export class IfPermission {
constructor(viewFactory, viewSlot) {
this.viewFactory = viewFactory;
this.viewSlot = viewSlot;
this.showing = false;
this.view = null;
this.bindingContext = null;
this.overrideContext = null;
}
/**
* Binds the if to the binding context and override context
* #param bindingContext The binding context
* #param overrideContext An override context for binding.
*/
bind(bindingContext, overrideContext) {
// Store parent bindingContext, so we can pass it down
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
this.valueChanged(this.value);
}
valueChanged(newValue) {
if (this.__queuedChanges) {
this.__queuedChanges.push(newValue);
return;
}
let maybePromise = this._runValueChanged(newValue);
if (maybePromise instanceof Promise) {
let queuedChanges = this.__queuedChanges = [];
let runQueuedChanges = () => {
if (!queuedChanges.length) {
this.__queuedChanges = undefined;
return;
}
let nextPromise = this._runValueChanged(queuedChanges.shift()) || Promise.resolve();
nextPromise.then(runQueuedChanges);
};
maybePromise.then(runQueuedChanges);
}
}
_runValueChanged(newValue) {
newValue = userService.hasPermission(newValue);
if (!newValue) {
let viewOrPromise;
if (this.view !== null && this.showing) {
viewOrPromise = this.viewSlot.remove(this.view);
if (viewOrPromise instanceof Promise) {
viewOrPromise.then(() => this.view.unbind());
} else {
this.view.unbind();
}
}
this.showing = false;
return viewOrPromise;
}
if (this.view === null) {
this.view = this.viewFactory.create();
}
if (!this.view.isBound) {
this.view.bind(this.bindingContext, this.overrideContext);
}
if (!this.showing) {
this.showing = true;
return this.viewSlot.add(this.view);
}
}
/**
* Unbinds the if
*/
unbind() {
if (this.view === null) {
return;
}
this.view.unbind();
if (!this.viewFactory.isCaching) {
return;
}
if (this.showing) {
this.showing = false;
this.viewSlot.remove(this.view, true, true);
}
this.view.returnToCache();
this.view = null;
}
}
if-user-role.js
import { inject, customAttribute, templateController, BoundViewFactory, ViewSlot } from 'aurelia-framework';
import userService from 'services/api/userService';
#customAttribute('if-user-role')
#inject(BoundViewFactory, ViewSlot)
#templateController
export class IfUserRole {
constructor(viewFactory, viewSlot) {
this.viewFactory = viewFactory;
this.viewSlot = viewSlot;
this.showing = false;
this.view = null;
this.bindingContext = null;
this.overrideContext = null;
}
/**
* Binds the if to the binding context and override context
* #param bindingContext The binding context
* #param overrideContext An override context for binding.
*/
bind(bindingContext, overrideContext) {
// Store parent bindingContext, so we can pass it down
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
this.valueChanged(this.value);
}
valueChanged(newValue) {
if (this.__queuedChanges) {
this.__queuedChanges.push(newValue);
return;
}
let maybePromise = this._runValueChanged(newValue);
if (maybePromise instanceof Promise) {
let queuedChanges = this.__queuedChanges = [];
let runQueuedChanges = () => {
if (!queuedChanges.length) {
this.__queuedChanges = undefined;
return;
}
let nextPromise = this._runValueChanged(queuedChanges.shift()) || Promise.resolve();
nextPromise.then(runQueuedChanges);
};
maybePromise.then(runQueuedChanges);
}
}
_runValueChanged(newValue) {
newValue = userService.hasRole(newValue);
if (!newValue) {
let viewOrPromise;
if (this.view !== null && this.showing) {
viewOrPromise = this.viewSlot.remove(this.view);
if (viewOrPromise instanceof Promise) {
viewOrPromise.then(() => this.view.unbind());
} else {
this.view.unbind();
}
}
this.showing = false;
return viewOrPromise;
}
if (this.view === null) {
this.view = this.viewFactory.create();
}
if (!this.view.isBound) {
this.view.bind(this.bindingContext, this.overrideContext);
}
if (!this.showing) {
this.showing = true;
return this.viewSlot.add(this.view);
}
}
/**
* Unbinds the if
*/
unbind() {
if (this.view === null) {
return;
}
this.view.unbind();
if (!this.viewFactory.isCaching) {
return;
}
if (this.showing) {
this.showing = false;
this.viewSlot.remove(this.view, true, true);
}
this.view.returnToCache();
this.view = null;
}
}
With this update i have integrated Aurelia-cli, updated aurelia-webpack and all the dependencies. Which made me switch some code like:
Add PLATFORM.moduleName() to all my platform
Add Require to all modules that were only getting components via < compose >
I have been using WKWebView.
I would like to work javascript on iOS9+Swift 2.0.
But It couldn't work.
console.log("hoge") couldn't even work.
My code is here
import UIKit
class CommonWebVCL: GABase,UIWebViewDelegate,UIScrollViewDelegate,WKNavigationDelegate,WKUIDelegate {
var webview: WKWebView!
var str_url:String! = ""
var url:String!
var gobackSearch:Bool! = false
var previousScrollViewYOffset:CGFloat? = 0
var toolbarheight:CGFloat = 0
var analytics_category:String!
override func viewDidLoad() {
super.viewDidLoad()
changeUserAgent()
let webViewConfiguration = WKWebViewConfiguration()
self.webview = WKWebView(frame: self.view.bounds, configuration: webViewConfiguration)
self.webview.navigationDelegate = self
self.webview.UIDelegate = self
addObserverForWKWebView()
self.view.addSubview(webview)
setBackButton()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationBarInit()
//タブの遷移でdelegateが外れるので貼り直す
self.webview.scrollView.delegate = self
}
func setBackBtnForWeb(){
let leftbtn = UIButton(frame: CGRect(x: 0, y: 0, width: 48, height: 22))
leftbtn.setImage(UIImage(named: "back_btn"), forState: .Normal)
leftbtn.addTarget(self, action: "goBackForWeb:", forControlEvents: UIControlEvents.TouchUpInside)
let backButton = UIBarButtonItem(customView: leftbtn)
self.navigationItem.setLeftBarButtonItem(backButton, animated: true)
}
func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!){
// インジケータを表示する
if let tmpstr_url = self.webview.URL?.absoluteString{
if (tmpstr_url.rangeOfString("tel") == nil){
SVProgressHUD.show()
}
}
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!){
// インジケータを表示する
if SVProgressHUD.isVisible(){
SVProgressHUD.dismiss()
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
navigationBarInit()
let URL = NSURL(string: url)
let urlRequest: NSURLRequest = NSURLRequest(URL: URL!)
self.webview.loadRequest(urlRequest)
}
override func viewDidLayoutSubviews() {
self.webview.frame = self.view.bounds
navigationBarInit()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
if SVProgressHUD.isVisible(){
SVProgressHUD.dismiss()
}
if (self.gobackSearch != nil){
sendEvent(analytics_category, str_action: "back")
self.navigationController?.popViewControllerAnimated(true)
}
self.webview.scrollView.delegate = nil
self.webview.navigationDelegate = nil
self.webview.stopLoading()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
func addObserverForWKWebView(){
self.webview.addObserver(self, forKeyPath: "canGoBack", options: NSKeyValueObservingOptions.New, context: nil) // WEBページで戻ることができるか監視のため、Observerに追加
self.webview.addObserver(self, forKeyPath: "URL", options: NSKeyValueObservingOptions.New, context: nil) // WEBページで戻ることができるか監視のため、Observerに追加
}
// Observerで監視対象のプロパティに変更があったときの処理
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if object!.isEqual(webview){
if keyPath == "estimatedProgress"{
self.navigationController?.setSGProgressMaskWithPercentage(Float(self.webview.estimatedProgress*100))
navigationBarInit()
}else if keyPath == "canGoBack"{
}else if keyPath == "URL"{
if let tmpstr_url = self.webview.URL?.absoluteString{
self.str_url = tmpstr_url
handleUrl()
}
}
}
}
//URLを見て何かするところ
func handleUrl(){
}
// 画面を閉じるときにObserverを削除。削除しないとアプリ落ちる
deinit{
if self.webview != nil{
//self.webview.removeObserver(self, forKeyPath: "estimatedProgress", context: nil)
self.webview.removeObserver(self, forKeyPath: "canGoBack", context: nil)
self.webview.removeObserver(self, forKeyPath: "URL", context: nil)
}
}
#IBAction func goBackForWeb(sender: AnyObject) {
if self.webview.canGoBack{
webview.goBack()
}else{
self.navigationController?.popViewControllerAnimated(true)
}
}
func webViewDidStartLoad(webView: UIWebView){
SVProgressHUD.showWithStatus("読み込み中")
navigationBarInit()
}
func webViewDidFinishLoad(webView: UIWebView){
SVProgressHUD.dismiss()
}
func webView(webView: UIWebView, didFailLoadWithError error: NSError?){
SVProgressHUD.dismiss()
}
func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void){
let alertController = UIAlertController(title: frame.request.URL?.host, message: message, preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "OK", style: .Default, handler: { action in
completionHandler()
}))
self.presentViewController(alertController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
What is this problem?
By the way this problem is only iOS9 by swift2.0.
It works correctly on other platform including iOS9 by swift 1.2.
I might resolve it by myself.
This web page always use http protocol.
So I changed https protocol,javascript have worked correctly.
I am trying to implement this plugin Phonegap system notification. I am reading two Rss feeds and displaying it to the user as a Status bar notification. Once the Notifications has been displayed to the user and the user clicks on the notification, he is taken to the app but I am not able to clear the notifications from the status bar. Could you please suggest me a way to clear these notifications by looking at my code.
How can I call navigator.systemNotification.cancelNotification() when the user clicks on the statusbar notification.
notification.js
google.load("feeds", "1");
google.setOnLoadCallback(function () {
var rss1old = '',
rss1new = '',
rss2old ='',
rss2new ='',
getRss = function (url, callback) {
(url) && (function (url) {
var feed = new google.feeds.Feed(url);
feed.load(function (result) {
(!result.error && callback) && (callback(result.feed.entries[0].title));
});
}(url));
};
setInterval(function () {
getRss(
'http://yofreesamples.com/category/free-coupons/feed/?type=rss',
function (title) {
rss1new = title;
if(rss1old !== rss1new) {
rss1old = rss1new;
navigator.systemNotification.onBackground();
navigator.systemNotification.updateNotification(rss1new,1);
navigator.notification.beep(1);
navigator.notification.vibrate(2000);
}
}
);
}, 5000);
setInterval(function () {
getRss(
'http://yofreesamples.com/category/real-freebies/feed/?type=rss',
function (title) {
rss2new = title;
if(rss2old !== rss2new) {
rss2old = rss2new;
navigator.systemNotification.onBackground();
navigator.systemNotification.updateNotification(rss2new,1);
navigator.notification.beep(1);
navigator.notification.vibrate(1000);
}
}
);
}, 6000);
});
SystemNotification.js -> Included from the plugin
function SystemNotification() {
}
SystemNotification.prototype.notificationEnabled = false;
SystemNotification.prototype.newCount = 0; //to keep track of multiple notifications events
SystemNotification.prototype.enableNotification = function () {
this.notificationEnabled = true;
};
SystemNotification.prototype.disableNotification = function () {
this.notificationEnabled = false;
};
SystemNotification.prototype.onBackground = function () {
this.enableNotification();
};
SystemNotification.prototype.onForeground = function () {
this.disableNotification();
};
SystemNotification.prototype.createStatusBarNotification = function (contentTitle, contentText, tickerText) {
PhoneGap.exec(null, null, "systemNotification", "createStatusBarNotification", [contentTitle, contentText, tickerText]);
};
SystemNotification.prototype.updateNotification = function (contentText, tickerText, number) {
this.newCount++;
var contentTitle = this.newCount + "RssFeeds";
if (this.newCount === 1) {
this.createStatusBarNotification(contentTitle, contentText, tickerText);
} else {
PhoneGap.exec(null, null, "systemNotification", "updateNotification", [contentTitle, contentText, this.newCount]);
this.showTickerText(tickerText); //optional
}
};
SystemNotification.prototype.cancelNotification = function (contentText) {
this.newCount--;
if (this.newCount === 0) {
PhoneGap.exec(null, null, "systemNotification", "cancelNotification", []);
}
else {
//updating the notification
var contentTitle = "my title";
PhoneGap.exec(null, null, "systemNotification", "updateNotification", [contentTitle, contentText, this.newCount]);
}
};
SystemNotification.prototype.showTickerText = function (tickerText) {
PhoneGap.exec(null, null, "systemNotification", "showTickerText", [tickerText]);
};
SystemNotification.prototype.touch = function () {
PhoneGap.exec(null, null, "systemNotification", "touch", []);
};
PhoneGap.addConstructor(function () {
if (typeof(navigator.systemNotification) == "undefined") {
navigator.systemNotification = new SystemNotification();
navigator.systemNotification.touch(); //this ensures that the plugin is added when phonegap kicks off
}
});
Systemnotification.Java -> Included from the plugin
package com.yfs.project;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
public class SystemNotification extends Plugin {
final int notif_ID = 1234;
NotificationManager notificationManager;
Notification note;
PendingIntent contentIntent;
#Override
public PluginResult execute(String action, JSONArray args, String callbackId)
{
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
try {
if (action.equals("createStatusBarNotification")) {
this.createStatusBarNotification(args.getString(0), args.getString(1), args.getString(2));
}
else if (action.equals("updateNotification")) {
this.updateNotification(args.getString(0), args.getString(1), args.getInt(2));
}
else if (action.equals("cancelNotification")) {
this.cancelNotification();
}
else if (action.equals("showTickerText")) {
this.showTickerText(args.getString(0));
}
return new PluginResult(status, result);
} catch(JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
private void updateNotification(String contentTitle, String contentText, int number)
{
note.setLatestEventInfo(this.ctx, contentTitle, contentText, contentIntent);
note.number = number;
notificationManager.notify(notif_ID,note);
}
private void createStatusBarNotification(String contentTitle, String contentText, String tickerText)
{
notificationManager = (NotificationManager) this.ctx.getSystemService(Context.NOTIFICATION_SERVICE);
note = new Notification(R.drawable.rss, tickerText, System.currentTimeMillis() );
//change the icon
Intent notificationIntent = new Intent(this.ctx, Yfs.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent = notificationIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
contentIntent = PendingIntent.getActivity(this.ctx, 0, notificationIntent, 0);
note.setLatestEventInfo(this.ctx, contentTitle, contentText, contentIntent);
note.number = 1; //Just created notification so number=1. Remove this line if you dont want numbers
notificationManager.notify(notif_ID,note);
}
private void cancelNotification()
{
notificationManager.cancel(notif_ID);
}
private void showTickerText(String tickerText)
{
note.tickerText = tickerText;
notificationManager.notify(notif_ID,note);
}
public void onPause()
{
super.webView.loadUrl("javascript:navigator.systemNotification.onBackground();");
}
public void onResume()
{
super.webView.loadUrl("javascript:navigator.systemNotification.onForeground();");
}
}
On android, you'll need to set the flag AUTO_CANCEL
Where you have this
note = new Notification(R.drawable.rss, tickerText, System.currentTimeMillis() );
Add this line right under
note.flags = Notification.FLAG_AUTO_CANCEL;