Failed to call javascript method from Swift code using WKWebView - javascript

I have a method in reacts form which I have loaded on WKWebView
window.initWithInfo = (credsObj) => {
}
I am using the following code to setup the WKWebView:
let config = WKWebViewConfiguration()
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = true
let userContentController = WKUserContentController()
userContentController.add(self,name:"messageHandler")
config.preferences = preferences
config.userContentController = userContentController
webView = WKWebView(frame:.zero, configuration: config)
view.addSubview(webView)
let urlString = "https://example.com"
if let url = URL(string: urlString) {
webView.load(URLRequest(url: url))
}
I am getting the callback message from javascript to iOS native userContentController but javascript method window.initPayrixInfo = (payrixCredsObj) is not getting called using evaluatejavascript from the userContentController
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if (message.name == "messageHandler") {
self.webView.evaluateJavaScript("initWithInfo(\(["api_key": "1233654", "api_token": "654321"])") { result, error in
if(error != nil) {
print(error)
}
}
}
}
How to make this work?
communicate between javascript and native iOS platform.

Related

Node grpc client error: TypeError: _d.substring is not a function

I have encountered strange issue and I am unable to make a call to my grpc server from node.
I am using:
"#grpc/grpc-js": "^1.6.8",
"#grpc/proto-loader": "^0.7.0",
Node version: 16.14.2
npm version: 8.5.0
And im my client file:
const grpc = require('#grpc/grpc-js');
const PROTO_PATH_LOGS_REQUEST = __dirname + '/proto/Attempt.proto';
const protoLoader = require('#grpc/proto-loader');
const AuthService = require('./AuthService');
const packageDefinition = protoLoader.loadSync(
PROTO_PATH_LOGS_REQUEST, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const ServiceDefinition = grpc.loadPackageDefinition(packageDefinition).com.example.AttemptService;
const updateAttempt = async (id, operationStatus) => {
const client = new ServiceDefinition('127.0.0.1:6513', grpc.credentials.createInsecure());
console.log(`Created gRPC client`);
const request = {
id: id,
status: operationStatus
};
const meta = new grpc.Metadata();
const token = AuthService.generateServiceToken();
meta.add('Authorization', `Bearer ${token}`);
await client.UpdateAttempt(request, meta);
}
My proto file:
syntax = "proto3";
package com.example;
message AttemptUpdateRequest {
string id = 1;
string status = 2;
}
message ContactAttempt {
string status = 1;
}
service AttemptService {
rpc UpdateAttempt (AttemptUpdateRequest) returns (ContactAttempt);
}
So my app crashes before reaching "Created gRPC client" log.
After putting new ServiceDefinition('127.0.0.1:6513', grpc.credentials.createInsecure()); inside try catch I received:
TypeError: _d.substring is not a function
And this error comes from node_modules\#grpc\grpc-js\build\src\channel.js:202:201 from this lines:
const error = new Error();
logging_1.trace(constants_1.LogVerbosity.DEBUG, 'channel_stacktrace', '(' + this.channelzRef.id + ') ' + 'Channel constructed \n' + ((_d = error.stack) === null || _d === void 0 ? void 0 : _d.substring(error.stack.indexOf('\n') + 1)));
The best part is that commenting this two lines solves this issue... But I need to put my app inside a Docker container so this is not an acceptable solution.
Did anybody encounter similar issue?

Cannot read property 'messageHandlers' of undefined

I want to pass JavaScript variable to Swift.
I get an error in JavaScript & search for that but I didn't get any result.
The error is:
TypeError: Cannot read property 'messageHandlers' of undefined
Any one can help?
My code in Xcode:
import UIKit
import WebKit
class ViewController: UIViewController,WKScriptMessageHandler,WKNavigationDelegate {
var webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
webView.frame = view.bounds
webView.navigationDelegate = self
let url = URL(string: "MyUrl")!
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
webView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
view.addSubview(webView)
let userContentController = WKUserContentController()
userContentController.add(self, name: "test")
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("Name: \(message.name)")
print("Body: \(message.body as! String)")
}
}
And in JavaScript:
click: event => {
window.webkit.messageHandlers.test.postMessage("hi");
}
After adding the "test" message handler, you need to add it to your webView's configuration, but you are not doing so.
By including this message handler into your WKUserContentController object, your webView will define a new function window.webkit.messageHandlers.name.postMessage(messageBody) that can be called in all frames.
Try changing your viewDidLoad() to the following:
override func viewDidLoad() {
super.viewDidLoad()
let config = WKWebViewConfiguration()
let userContentController = WKUserContentController()
userContentController.add(self, name: "test")
config.userContentController = userContentController
webView = WKWebView(frame: view.bounds, configuration: config) // Create webView with the configuration
webView.navigationDelegate = self
let url = URL(string: "MyUrl")!
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
webView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
view.addSubview(webView)
}

iOS Enable Sound for Push Notification Sound FCM

I'm trying to enable sound for my Firebase push notifications and I'm not sure if there is code in the App Delegate which I need to implement, or if the code in my index.js is wrong.
I thought there was something I needed to import in AppDelegate related to sound, but all the guides I've found for implementing push notifications only have the basic code where [options] contains the only thing related to the notification's sound.
index.js Code:
var notification = {
notification: {
title: conversation.conversationName,
body: user.username + ': ' + message.text,
sound: 'default'
},
topic: topic
}
App Delegate Code: Function called in didFinishLaunchingWithOptions.
import UIKit
import Firebase
import UserNotifications
private func attemptRegisterForNotifications(application: UIApplication) {
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
let options: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized {
// Push notifications already authorized, do nothing
print ("push notifications authorized")
} else if settings.authorizationStatus == .notDetermined {
// User hasn't specified notification status
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { (granted, error) in
if let error = error {
print ("Failed to request authorization:", error)
return
}
guard granted else {return}
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
})
} else if settings.authorizationStatus == .denied {
// User has denied notifications
UNUserNotificationCenter.current().requestAuthorization(options: options, completionHandler: { (granted, error) in
if let error = error {
print ("Failed to request authorization:", error)
return
}
let alertController = UIAlertController(title: "Enable Push Notifications", message: "Enable push notifications for optimal chat experience", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
})
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
alertController.addAction(settingsAction)
alertController.preferredAction = settingsAction
DispatchQueue.main.async {
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
})
}
}
}

Apple IAP Receipt Validation Node.js

I'm trying to use Node.js from here for my IAP Receipt Validation, but it always returns error on a server's log: "The data in the receipt-data property was malformed."
Can someone help me to properly send base64 string to Node.js and to decode it there as same base64 string for receipt validation? I have zero experience with javascript and been trying to make this simple code work for two days now, to no avail.
Here is my Swift code:
let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
let receiptString = receiptData.base64EncodedString(options: [])
var request = URLRequest(url: URL(string: "https://us-central1-calendizer-6a849.cloudfunctions.net/receiptValidation")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
// attach receipt data to a request
request.httpBody = receiptString.data(using: .utf8)
print("httpBody: \(request.httpBody?.base64EncodedString())")
Logs.log("✉️ Receipt Validation -> sending request ...")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
...
}
Here is my Node.js code (Firebase Cloud Functions):
const functions = require('firebase-functions');
var iap = require('in-app-purchase');
exports.receiptValidation = functions.https.onRequest((request, response) => {
var receipt_try1 = request.body
var receipt_try2 = request.body.toString('base64');
var receipt_try3 = JSON.stringify(receipt);
var receipt_try4 = new Buffer(request.body.toString(), 'base64')
console.log("receipt: " + receipt_try1)
iap.config({
applePassword: 'my shared key',
test: true
});
iap.setup(function (error) {
if (error) {
console.log("Setup error:" + error) // Failed to validate
}
iap.validate(iap.APPLE, receipt_try1, function (error, appleResponse) {
if (error)
{
console.log("Validation error:" + error) // Failed to validate
}
if (iap.isValidated(appleResponse)) {
console.log("successful validation" + appleResponse)
response.send(appleResponse)
}
});
});
});
did it using json:
let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
let receiptBase64String = receiptData.base64EncodedString(options: [])
//.replacingOccurrences(of: "+", with: "%2B")
// prepare json data
let json: [String: Any] = ["receipt_string": receiptBase64String]
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
// create post request
var request = URLRequest(url: URL(string: "https://us-central1-calendizer-6a849.cloudfunctions.net/receiptValidation")!)
//request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = jsonData
and in Node.js simply:
var receipt = request.body.receipt_string

How to send in media[] parameters for update with media API call of twitter using javascript

I'm implementing twitter status update in my phonegap/cordova application on android. all the work is going perfect even simple text can be update on my twitter status.. but when try to upload media api, it response error code 195 and message is Missing or invalid url paramter
I'm following this example
var mediaUrl = 'http://fbrell.com/f8.jpg';
//update_with_media.json
//update.json
oauth.post('https://api.twitter.com/1.1/statuses/update_with_media.json',
{ 'status' : 'testing message', // javascript OAuth encodes this
'media[]' : mediaUrl
},
function(data) {
console.log('success posted');
console.log(JSON.stringify(data));
var entry = JSON.parse(data.text);
// just for eg.
tweetPostDone();
},
function(errorData) {
console.log('error on posted');
console.log(JSON.stringify(errorData));
}
);
}
What I'm doing wrong with javascript code on media[] parameter?
Twitter response:
"text": {
"errors": [
{
"code": 195,
"message": "Missing or invalid url parameter."
}
]
},
I solved my problem by create a custom phonegap plugin.
Step#1 - Create javascript file on /assets/www/js/UploadTwitterMedia.js
(function() {
/* This increases plugin compatibility */
var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks
/**
* The Java to JavaScript Gateway 'magic' class
*/
function UploadTwitterMedia() { }
UploadTwitterMedia.prototype.uploadImage = function(params, win, fail) {
cordovaRef.exec(win, fail, "UploadTwitterMedia", "uploadImage", [params]);
};
cordovaRef.addConstructor(function() {
if (!window.plugins) {
window.plugins = {};
}
if (!window.plugins.UploadTwitterMedia) {
window.plugins.UploadTwitterMedia = new UploadTwitterMedia();
}
});
})();
Step#2 - Create a Java Class File in org.apache.cordova.plugins and name it UploadTwitterMedia.java
package org.apache.cordova.plugins;
/**
* A phonegap plugin that upload media on twitter profile by using url address
*
* #author mcaesar
* #lincese MIT.
*/
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import twitter4j.auth.OAuthAuthorization;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationBuilder;
import twitter4j.media.ImageUpload;
import twitter4j.media.ImageUploadFactory;
import android.provider.SyncStateContract.Constants;
import android.util.Log;
public class UploadTwitterMedia extends Plugin {
#Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
Log.d("upload action on UploadTwitterMedia.java", action);
if (!action.equals("uploadImage")) {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
try {
String uploadImageurl = "";
String accessToken = "";
String accessSecret = "";
String statusText = "";
JSONObject params = args.getJSONObject(0);
uploadImageurl = params.getString("urllink");
accessToken = params.getString("accessToken");
accessSecret = params.getString("accessSecret");
statusText = params.getString("statusText");
Log.d("UploadImageUrl", uploadImageurl);
Log.d("accessToken", accessToken);
Log.d("accessSecret", accessSecret);
Log.d("statusText", statusText);
return this.uploadImage(uploadImageurl, accessToken, accessSecret, statusText, callbackId);
} catch (JSONException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
} catch (InterruptedException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, e.getMessage());
}
}
private PluginResult uploadImage(String imageUrl, String accessToken, String accessSecret, String statusText, String callbackId) throws InterruptedException, JSONException {
// TODO Auto-generated method stub
String url;
String consumerKey = "xxxxxxxxxxxxxxxxxxxx";
String consumerSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
String oth = accessToken;
String src = accessSecret;
Configuration conf = new ConfigurationBuilder()
.setOAuthConsumerKey(consumerKey)
.setOAuthConsumerSecret(consumerSecret)
.setOAuthAccessToken(oth).setOAuthAccessTokenSecret(src)
.build();
OAuthAuthorization auth = new OAuthAuthorization(conf);
//ImageUpload upload = ImageUpload.getTwitpicUploader(Constants.twitpic_api_key, auth);
ImageUpload upload = new ImageUploadFactory(conf).getInstance(auth);
Log.d("Twitter Native Function in UploadTwitterMedia.java", "Start sending image...");
try {
url = upload.upload(" ", new URL(imageUrl).openStream(),statusText);
} catch (Exception e) {
//e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, ""+e.getMessage());
}
return new PluginResult(PluginResult.Status.OK, "Saved successfully!");
}
}
Step#3 - Download latest version of Twitter library for android from twitter4j.org
Step#4 - Add a line in the plugin.xml or config.xml in res/xml/
<plugin name="UploadTwitterMedia" value="org.apache.cordova.plugins.UploadTwitterMedia"/>
Step#5 - Now call to upload method.
post: function(){
/*
oauth.post('https://api.twitter.com/1.1/statuses/update.json',
{ 'status' : theTweet+' Test ', // javascript OAuth encodes this
'media[]': base64String
}
function(data) {
console.log('success posted');
console.log(JSON.stringify(data));
var entry = JSON.parse(data.text);
console.log(entry);
// just for eg.
tweetPostDone();
},
function(errorData) {
console.log('error on posted');
console.log(errorData);
console.log(JSON.stringify(errorData));
}
);
*/
/*
oauth.push("https://api.twitter.com/1.1/statuses/update_with_media.json",
imageURI.substr(imageURI.lastIndexOf("/") + 1),
new FileUploadOptions("media", "image.jpg", "image/jpeg", {status:tweetStatus}, {}),
onTweetSuccess,
onTweetError
);
*/
var storedAccessData, rawData = localStorage.getItem(twitterKey);
storedAccessData = JSON.parse(rawData); // Paring Json
window.plugins.UploadTwitterMedia.uploadImage(
{
urllink : 'http://fbrell.com/f8.jpg',
accessToken : storedAccessData.accessTokenKey,
accessSecret : storedAccessData.accessTokenSecret,
statusText : 'Hello World...'
},
function(result) {
console.log('success');
console.log(result);
}, function(error) {
console.log('error');
console.log(error)
});
}
hope and when i call the Twitter.post() method it will post on provided valid acessToken and accessSecret. If success, it will call the success function
function(result) {
console.log('success');
console.log(result);
},
else call error function
function(error) {
console.log('error');
console.log(error)
}
Note: Update the consumerKey & consumerSecret with your twitter application in UploadTwitterMedia.java file.

Categories