Hi I'm using the facebook share sheet on SDK 3.1 and it works except when I try to share 2 urls it crashes.
NSArray* urls = [NSArray arrayWithObjects:#"http://google.com", #"http://yahoo.com", nil];
BOOL displayedNativeDialog = [FBNativeDialogs presentShareDialogModallyFrom:self
initialText: #"hellooo"
images: nil
urls: urls
handler: ^(FBNativeDialogResult result, NSError *error) {
if (error) {
NSLog(#"handler error:%#, %#", error, [error localizedDescription]);
} else {
if (result == FBNativeDialogResultSucceeded)
{
NSLog(#"handler success");
}
else
{
NSLog(#"handler user cancel");
}
}
}];
Result:
-[__NSCFConstantString isMusicStoreURL]: unrecognized selector sent to instance 0x3d23e8
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString isMusicStoreURL]: unrecognized selector sent to instance 0x3d23e8'
I believe that the NSArray of Urls it is taking is expecting NSURLs, not NSStrings. You can use the static URLWithString to get the NSURLs for the array.
Related
I've setup Firestore DB and wanted to test it. However I was unable neither read or wright data to it. Here is what console shows on connection status to Firebase.
Debug Console:
2020-11-19 17:58:49.802849+0300 Demo [813:165300] 6.34.0 - [Firebase/Analytics][I-ACS023007] Analytics v.60900000 started
2020-11-19 17:58:49.803819+0300 Demo [813:165300] 6.34.0 - [GoogleUtilities/AppDelegateSwizzler][I-SWZ001014] App Delegate does not conform to UIApplicationDelegate protocol.
2020-11-19 17:58:49.804498+0300 Demo [813:165300] 6.34.0 - [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see https://help.apple.com/xcode/mac/8.0/#/dev3ec8a1cb4)
2020-11-19 17:58:50.586054+0300 Demo [813:165306] 6.34.0 - [Firebase/Analytics][I-ACS800023] No pending snapshot to activate. SDK name: app_measurement
2020-11-19 17:58:51.138985+0300 Demo [813:165306] 6.34.0 - [Firebase/Analytics][I-ACS023012] Analytics collection enabled
2020-11-19 17:58:51.139362+0300 Demo [813:165306] 6.34.0 - [Firebase/Analytics][I-ACS023220] Analytics screen reporting is enabled. Call +[FIRAnalytics logEventWithName:FIREventScreenView parameters:] to log a screen view event. To disable automatic screen reporting, set the flag FirebaseAutomaticScreenReportingEnabled to NO (boolean) in the Info.plist
2020-11-19 17:58:51.850205+0300 Demo [813:165012] [AXRuntimeCommon] This class 'SwiftUI.AccessibilityNode' is not a known serializable element and returning it as an accessibility element may lead to crashes
Also Firebase it self shows that I had some reads in stats.
I stuck and don't understand what important part I didn't do in order to start implementing Firebase use. Will appreciate any help or suggestion.!
I used several methods to initiate Firebase in SwiftUI.
This one:
import SwiftUI
import Firebase
#main
struct DemoApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL(perform: { url in
Auth.auth().canHandle(url)
})
}
}
Also tried with DelegateAdaptor:
#main
struct DemoApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
appDelegate
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("Application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.")
return true
}
}
To test writing to:
func letE() {
let db = Firestore.firestore()
db.collection("wine").addDocument(data: ["Cool" : 2017])
}
And to fetch an array
private var db = Firestore.firestore()
func fetchData() {
db.collection("collection").document("document")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data()!["array name"] else {
print("Document data was empty.")
return
}
print(data)
}
And like this:
func getData(){
db.collection("skills").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
Hello, Peter! I was trying to come to solving this problem from different angels but still no luck.
Basically I am trying to perform wright operation
It goes like this
#main
struct DemoApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL(perform: { url in
Auth.auth().canHandle(url)
})
}
}
}
func addData() {
//
let db = Firestore.firestore()
//
db.collection("wine").addDocument(data: ["Test":100])
}
Then i press Build.
From my understanding that already should create collection "Wine" with autogenerated Document with contains field "Test" with value "100"
But once again nothing happens on the firebase side.
The Debug console
2021-01-26 16:22:21.907902+0300 Demo [27616:342494] 6.34.0 - [Firebase/Analytics][I-ACS023007] Analytics v.60900000 started
2021-01-26 16:22:21.908067+0300 Demo [27616:342494] 6.34.0 - [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see https://help.apple.com/xcode/mac/8.0/#/dev3ec8a1cb4)
2021-01-26 16:22:21.934626+0300 Demo [27616:342498] 6.34.0 - [GoogleUtilities/AppDelegateSwizzler][I-SWZ001014] App Delegate does not conform to UIApplicationDelegate protocol.
2021-01-26 16:22:21.979114+0300 Demo [27616:342490] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
2021-01-26 16:22:21.994921+0300 Demo [27616:342490] 6.34.0 - [Firebase/Analytics][I-ACS800023] No pending snapshot to activate. SDK name: app_measurement
2021-01-26 16:22:22.000217+0300 Demo [27616:342490] 6.34.0 - [Firebase/Analytics][I-ACS023012] Analytics collection enabled
2021-01-26 16:22:22.000724+0300 Demo [27616:342490] 6.34.0 - [Firebase/Analytics][I-ACS023220] Analytics screen reporting is enabled. Call +[FIRAnalytics logEventWithName:FIREventScreenView parameters:] to log a screen view event. To disable automatic screen reporting, set the flag FirebaseAutomaticScreenReportingEnabled to NO (boolean) in the Info.plist
2021-01-26 16:22:22.032550+0300 Demo [27616:342197] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x60000341ce60 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fb0e7d16480]-(6)-[_UIModernBarButton:0x7fb0e7d0a710'Skills'] (active)>",
"<NSLayoutConstraint:0x60000341ceb0 'CB_Trailing_Trailing' _UIModernBarButton:0x7fb0e7d0a710'Skills'.trailing <= _UIButtonBarButton:0x7fb0e7d10cb0.trailing (active)>",
"<NSLayoutConstraint:0x60000341dc20 'UINav_static_button_horiz_position' _UIModernBarButton:0x7fb0e7d16480.leading == UILayoutGuide:0x600002e54ee0'UIViewLayoutMarginsGuide'.leading (active)>",
"<NSLayoutConstraint:0x60000341dc70 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x7fb0e7d10cb0]-(0)-[UILayoutGuide:0x600002e54e00'UINavigationBarItemContentLayoutGuide'] (active)>",
"<NSLayoutConstraint:0x60000347eda0 'UINavItemContentGuide-trailing' UILayoutGuide:0x600002e54e00'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x7fb0e7e0f7e0.trailing (active)>",
"<NSLayoutConstraint:0x60000341e3f0 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x7fb0e7e0f7e0.width == 0 (active)>",
"<NSLayoutConstraint:0x60000347e9e0 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x600002e54ee0'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':_UINavigationBarContentView:0x7fb0e7e0f7e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000341ce60 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7fb0e7d16480]-(6)-[_UIModernBarButton:0x7fb0e7d0a710'Skills'] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
The changed Debug Console outcome presented is because I've setup authorised access and it does work. I can create user and then perform login with crated login and password. All users do show up on the Firebase console.
Using PubNub and trying to setup push for ios (apns) per their docs.
The following generates the error:
var initPubnubPush = function(token) {
Pubnub.push.addChannels(
{
channels: ['my_first_channel'],
device: token,
pushGateway: 'apns' // apns, gcm, mpns
},
function(status) {
if (status.error) {
console.log("operation failed w/ error:", status);
} else {
console.log("operation done!")
}
}
);
}
I have their publish/subscribe working on the channel my_first_channel and push should work when the app is in background. The token is valid and is returned from
pushNotification.register(
tokenHandler,
errorHandler,
{
'badge':'true',
'sound':'true',
'alert':'true',
'ecb':'onNotificationAPN'
}
);
function tokenHandler(token) {
// This is a device token you will need later to send a push
// Store this to PubNub to make your life easier :-)
initPubnubPush(token);
}
The complete error in Safari console while running on real iPhone device:
Error in Success callbackId: PushPlugin63370093 : TypeError:
Pubnub.push.addChannels is not a function.
(In 'Pubnub.push.addChannels', 'Pubnub.push.addChannels' is undefined)
Can't find anything in google for this error.
Turns out there is a bug in the PubNub Angular library and that method push.addChannels is not properly delegated.
I'm using Twilio's NodeJS module & API to send out MMS messages with images attached (from a remote URL), and I want to delete the Media instances that get created on Twilio servers as soon as I send out the messages.
My messages send out correctly, and in the callback, I'm trying to 1) list media instances for the current message, then 2) loop through those instances and delete. The problem is that the mediaList array that comes back from the API for a current message is always empty.
Here's my code:
twilio_client.messages.create({
body: "Thanks for taking a photo. Here it is!",
to: req.query.From,
from: TWILIO_SHORTCODE,
mediaUrl: photo_URL,
statusCallback: STATUS_CALLBACK_URL
}, function(error, message) {
if (!error) {
twilio_client.messages(message.sid).media.list(function(err, data) {
console.log(data);
// The correct object comes back as 'data' here per the API
// but the mediaList array is empty
}
console.log('Message sent via Twilio.');
res.status(200).send('');
} else {
console.log('Could not send message via Twilio: ');
console.log(error);
res.status(500).send('');
}
});
So, it turns out that trying to get the media list at the point I was trying to doesn't work because the media instances didn't exist yet.
I have a separate little app running at the statusCallback (I supply a URL via a constant in the code above, STATUS_CALLBACK_URL), that until now, just checked to see if a message I tried to MMS to a user wasn't handled properly by Twilio, and alerted the user to a problem via SMS. So, I added a check in that same app to see if the message was actually 'sent' to the user, and then checked for and deleted the media instance(s) associated with the message at that point, and it works fine. Here's my code:
// issue message to user if there's a problem with Twilio getting the photo
if (req.body.SmsStatus === 'undelivered' || req.body.SmsStatus === 'failed') {
twilio_client.messages.create({
body: "We're sorry, but we couldn't process your photo. Please try again.",
to: req.body.To,
from: TWILIO_SHORTCODE
}, function(error, message) {
if (!error) {
console.log('Processing error message sent via Twilio.');
res.send(200,'');
} else {
console.log('Could not send processing error message via Twilio: ' + error);
res.send(500);
}
});
}
// delete media instance from Twilio servers
if (req.body.SmsStatus === 'sent') {
twilio_client.messages(req.body.MessageSid).media.list(function(err, data) {
if (data.media_list.length > 0) {
data.media_list.forEach(function(mediaElement) {
twilio_client.media(mediaElement.sid).delete;
console.log("Twilio media instance deleted");
});
}
});
}
I am building a web application using Parse.com as my backend. I have run into a problem when trying to store the response from Facebook's user profile API in a Parse database.
FB.api('/me', function(response) {
// Print Response to Console
console.log(response);
// Create New Parse Object
var Facebook = Parse.Object.extend("Facebook");
var userFacebook = new Facebook();
for(var element in response) {
userFacebook.set(element, response[element]);
}
// Store Data on Parse
userFacebook.save(null, {
success: function(user) {
console.log("User Facebook data saved.");
},
error: function(user, error) {
console.log("Facebook data storage error: " + error.code + " (" + error.message + ")");
}
});
The API response prints correctly to the console, and I receive two error messages, one after the other:
Failed to load resource: the server responded with a status of 404
(Not Found) https://api.parse.com/1/classes/Facebook/myID
and
Facebook data storage error: 101 (object not found for update)
In my Parse account, a database titled "Facebook" is created. However, it only contains a header for each of the elements in the response object (e.g., first_name, gender, email, etc.). It does not have a new row with these values.
I am really stuck on this one -- any help would be appreciated!
Note that the response object is in the form:
{
"email":"email#example.com",
"first_name":"myFirstName",
"gender":"myGender",
"id":"myID",
"last_name":"myLastName",
"link":"https://www.facebook.com/app_scoped_user_id/myID/",
"locale":"en_US",
"name":"myFullName",
"timezone":-7,
"updated_time":"2014-03-12T04:57:39+0000",
"verified":true
}
The object in for each loop
for(var element in response) {
userFacebook.set(element, response[element]);
}
FB.api('/me', function(response) {
// Print Response to Console
console.log(response);
// Create New Parse Object
var Facebook = Parse.Object.extend("Facebook");
var userFacebook = new Facebook();
for(var element in response) {
userFacebook.set(element, response[element]);
}
// Store Data on Parse
userFacebook.save(null, {
success: function(user) {
console.log("User Facebook data saved.");
},
error: function(user, error) {
console.log("Facebook data storage error: " + error.code + " (" + error.message + ")");
}
});
The "id" column on a Parse.Object is protected and cannot be set via the API. In my case, Facebook's API response includes an "id" key, which is colliding with the Parse.Object "id" key.
If you are running into the same problem, you simply need to change the name of the "id" column. You can do this with the following code:
for(var element in response) {
if(element=="id") {
userFacebook.set("userID", response[element]);
} else {
userFacebook.set(element, response[element]);
}
}
Thanks to Hector Ramos at Parse for helping me solve the problem! And thanks to Wayne for getting me part of the way!
I am trying to call a Google Cloud Endpoint API (developed on App Engine) via Google Apps Script. The endpoint is up and running, honestly I don't know which URL I should use but through Google Chrome Web Tools it looks like the URL is something like:
https://myapp.appspot.com/_ah/api/myendpointapi/v1/myEndPointMethod/
Along with API parameters directly included in the URL, separeted by slashes:
https://myapp.appspot.com/_ah/api/myendpointapi/v1/myEndPointMethod/param1value/param2value/...
Now, in order to call that API from Google App Script I am using the following code snippet:
function myFunction() {
var params =
{
"param1" : "param1value",
"param2" : "param2value",
};
var result = UrlFetchApp.fetch('https://myapp.appspot.com/_ah/api/myendpointapi/v1/myEndPointMethod/', params);
DocumentApp.getUi().alert(result);
}
However I always get a 404 error. If I have to be honest I don't even know if UrlFetchApp is the correct way of calling the API. I noticed this thread on StackOverflow but no one answered. What's the correct URL to use? Many thanks.
EDIT: Now I am trying with an API method which does not require any parameter. I found a way to call a specific URL (using method='get' as suggested by the answer below) but now I get a 401 error because it says I am not logged in. I believe I need to use some kind of OAuth parameter now. Any idea? I tryed using OAuthConfig but no luck with that as well :( From App Engine logs I can see the following error:
com.google.api.server.spi.auth.GoogleIdTokenUtils verifyToken: verifyToken: null
com.google.api.server.spi.auth.AppEngineAuthUtils getIdTokenEmail:
getCurrentUser: idToken=null
function myFunction() {
var result = UrlFetchApp.fetch('myurl', googleOAuth_());
result = result.getContentText();
}
function googleOAuth_() {
var SCOPE = 'https://www.googleapis.com/auth/drive';
var NAME = 'myAPIName';
var oAuthConfig = UrlFetchApp.addOAuthService(NAME);
oAuthConfig.setRequestTokenUrl('https://www.google.com/accounts/OAuthGetRequestToken?scope='+SCOPE);
oAuthConfig.setAuthorizationUrl('https://www.google.com/accounts/OAuthAuthorizeToken');
oAuthConfig.setAccessTokenUrl('https://www.google.com/accounts/OAuthGetAccessToken');
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:NAME, oAuthUseToken:'always'};
}
UrlFetchApp is the only way to call a Google Cloud Endpoints API at the moment. The second parameter to UrlFetchApp.fetch is a special key-value map of advanced options. To pass POST parameters, you need to do the following:
UrlFetchApp.fetch(url, {
method: 'post',
payload: {
"param1" : "param1value",
"param2" : "param2value",
}
});
I was fighting a similar (not the same) problem, when testing feasibility of a GCM backed by EndPoints server. Basically testing if it is possible to get the Google Spreadsheet Appscript to send notification to an Android device. Please bear with me, the following explanation may be a bit convoluted;
Starting with a standard 'Cloud Messaging for Android', backed by the 'App Engine Backend with Google Cloud Messaging', I managed to build a test system that would send messages between Android devices (Github here).
Here is a VERY sparse EndPoints server code that handles register / un-register Android devices, as well as reporting registered devices and sending a message to a list of registered devices.
WARNING: This is not a production quality code, it is stripped of any logging, error handling in order to keep it short.
#Api( name = "gcmEP", version = "v1",
namespace = #ApiNamespace(ownerDomain = "epgcm.example.com", ownerName = "epgcm.example.com", packagePath = "" )
)
public class GcmEP {
#ApiMethod(name = "registToken")
public void registToken(#Named("token") String token) {
if (ofy().load().type(TokenRec.class).filter("token", token).first().now() == null) {
ofy().save().entity(new TokenRec(token)).now();
}
}
#ApiMethod(name = "unregToken")
public void unregToken(#Named("token") String token) {
TokenRec record = ofy().load().type(TokenRec.class).filter("token", token).first().now();
if (record != null) {
ofy().delete().entity(record).now();
}
}
#ApiMethod(name = "listTokens")
public CollectionResponse<TokenRec> listTokens() {
return CollectionResponse.<TokenRec>builder().setItems(ofy().load().type(TokenRec.class).list()).build();
}
#ApiMethod(name = "sendMsg")
public void sendMsg(#Named("message") String message) throws IOException {
if (message != null && message.length() > 0) {
Sender sender = new Sender(System.getProperty("gcm.api.key"));
Message msg = new Message.Builder().addData("message", message).build();
for (TokenRec record : ofy().load().type(TokenRec.class).list()) {
Result result = sender.send(msg, record.getToken(), 4);
if (result.getMessageId() != null) {
// handle CanonicalRegistrationId
} else {
// handle errors, delete record
}
}
}
}
}
Android code for registration and message sending is shown here, even if it is not relevant.
GcmEP mRegSvc;
String mToken;
// register device on EndPoints backend server
private void registerMe() {
new Thread(new RegisterMe(this)).start();
}
private class RegisterMe implements Runnable {
Activity mAct;
public RegisterMe(Activity act) { mAct = act; }
public void run() {
String senderId = null;
if (mAct != null) try {
if (mRegSvc == null) {
mRegSvc = new GcmEP
.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null).setRootUrl(UT.ROOT_URL).build();
}
senderId = getString(R.string.gcm_defaultSenderId);
mToken = InstanceID.getInstance(mAct).getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
mRegSvc.registToken(mToken).execute();
GcmPubSub.getInstance(mAct).subscribe(mToken, "/topics/global", null); // subscribing to all 'topics' from 'mToken'
} catch (IOException e) { e.printStackTrace(); }
}
}
// send message to EndPoints backend server
new Thread(new Runnable() {
#Override
public void run() {
if (mRegSvc != null) try {
mRegSvc.sendMsg("hello").execute();
} catch (IOException e) { e.printStackTrace(); }
}
}).start();
// receive GCM message
public class GcmListenSvc extends GcmListenerService {
#Override
public void onMessageReceived(String senderId, Bundle data) {
Log.i("_X_", data.getString("message"));
}
}
What is relevant, thought, there is also an APIs Explorer created for the project, that can be used to send messages to your Android device from any browser.
If you use this Explorer, you can see the GET, POST requests for your EndPoints backend server, i.e.
list all registered devices:
GET https://epgcm.appspot.com/_ah/api/gcmEP/v1/tokenrec?fields=items
send a message to all registered devices:
POST https://epgcm.appspot.com/_ah/api/gcmEP/v1/sendMsg/Hello%20World!
Now, you can use this knowledge to send messages to your Android device from an AppScript code as shown:
Version 1: Get list of registered devices and send a GCM message to all of them (or a filtered set).
function sendMsg() {
var msg = 'test from CODE.GS';
var url = 'https://epgcm.appspot.com/_ah/api/gcmEP/v1/tokenrec?fields=items';
var params = { method : 'get'};
var response = UrlFetchApp.fetch(url, params);
var data = JSON.parse(response.getContentText());
var regIds = [];
for (i in data.items)
regIds.push(data.items[i].token);
var payload = JSON.stringify({
'registration_ids' : regIds,
'data' : { 'message' : msg }
});
var params = {
'contentType' : 'application/json',
'headers' : {'Authorization' : 'key=AIza............................'},
'method' : 'post',
'payload' : payload
};
url = 'https://android.googleapis.com/gcm/send';
UrlFetchApp.fetch(url, params);
}
This version relies on code from an old YouTube video, and I don't know if the call to 'android.googleapis.com' is still supported (but it works).
Version 2: Use the EndPoints's 'sendMsg' directly.
function sendMsg() {
var msg = 'test from CODE.GS';
var params = { method : 'post'};
var url = 'https://demoepgcm.appspot.com/_ah/api/gcmEP/v1/sendMsg/' + encodeURIComponent(msg.trim());
UrlFetchApp.fetch(url, params);
}
I have to admit I've never written a line of JavaScript code before, so it may not be up-to-par, but I made it work as a 'proof of concept'.
I would like to get feedback about this problem from people-who-know, since there is so little published info on this specific issue.