This is my code:
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
public ISharedFunctions SharedFunctions => DependencyService.Get<ISharedFunctions>();
const string TAG = "MyFirebaseIIDService";
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);
SendRegistrationToServer(refreshedToken);
}
void SendRegistrationToServer(string token)
{
// Add custom implementation, as needed.
SharedFunctions.SaveFirebaseDeviceToken(token);
SharedFunctions.GetFirebaseDeviceToken();
}
}
But i get this error:
System.InvalidOperationException: 'You must call Xamarin.Forms.Forms.Init(); prior to using this property.'
How to save it with DependencyService , because i use same Shared Function for save and get the token in Android and IOS?
You can't use DependencyService to send data from platform project to shared project.
I would recommend you to use MessagingCenter to send the data:
MessagingCenter.Send<MainPage>(this, "Hi");
And in shared project:
MessagingCenter.Subscribe<MainPage> (this, "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
});
If you want to get/save some data in Xamarin.forms, you can use Xamarin.Essentials: Preferences
:
Save the value with key:
Preferences.Set("my_key", "my_value");
Get the value with key:
var myValue = Preferences.Get("my_key", "default_value");
Related
I have a question here about getting an Invalid Registration FCM error. I want to send notification to specific device through Backend .Net (EF)
First of all I saved the deviceToken to the server (Xamarin iOS).
I read the error from https://firebase.google.com/docs/cloud-messaging/http-server-ref
200 + error:InvalidRegistration Check the format of the registration
token you pass to the server. Make sure it matches the registration
token the client app receives from registering with Firebase
Notifications. Do not truncate or add additional characters.
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
//FirebasePushNotificationManager.DidRegisterRemoteNotifications(deviceToken);
//byte[] bytes = deviceToken.ToArray<byte>();
//string[] hexArray = bytes.Select(b => b.ToString("x2")).ToArray();
//DeviceToken = string.Join(string.Empty, hexArray);
string deviceTokenString;
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
deviceTokenString = BitConverter.ToString(deviceToken.ToArray());
}
else
{
deviceTokenString = deviceToken.ToString();
}
Preferences.Set("TokenDevice", deviceTokenString);
}
I saved the deviceToken to Preferences and also saved it to the server. My deviceToken code on Xamarin iOS:
1A-F5-BE-DC-E4-22-07-46-AA-C4-87-CC-08-F5-D7-09-D4-AB-43-18-26-E0-65-3B-39-4E-6F-5E-02-35-9E-3A
I don't know if this method of getting the deviceToken on Xamarin iOS is really correct at this time?
Backend I write API to send:
private static Uri FireBasePushNotificationsURL = new Uri("https://fcm.googleapis.com/fcm/send");
private static string ServerKey = "key=AAAAqOId6Is:APxxxxxxxxxxxxxxxxxxxx....";
[HttpPost]
public async void PushNotificationToFCM(string deviceTokens, string title, string body, object data, string linkdirection)
{
using (var client = new HttpClient())
{
var messageInformation = new Message()
{
notification = new NotificationFirebases()
{
title = title,
text = body
},
data = new Dictionary<string, string>()
{
{ "link", linkdirection },
},
to = deviceTokens
};
//Object to JSON STRUCTURE => using Newtonsoft.Json;
string jsonMessage = JsonConvert.SerializeObject(messageInformation);
string postBody = JsonConvert.SerializeObject(messageInformation).ToString();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", ServerKey);
var response = client.PostAsync(FireBasePushNotificationsURL, new StringContent(postBody, Encoding.UTF8, "application/json"));
var responseString = response.Result.Content.ReadAsStringAsync().Result;
}
}
public class Message
{
public string to { get; set; }
public NotificationFirebases notification { get; set; }
public object data { get; set; }
}
public class NotificationFirebases
{
public string title { get; set; }
public string text { get; set; }
}
When I test I get the error:
{\"multicast_id\":7343900550378569449,\"success\":0,\"failure\":1,\"canonical_ids\":0, \" results\":[{\"error\":\"InvalidRegistration\"}]}. I have tried with deviceToken on Android and it succeeds. Appears only with deviceToken on Xamarin iOS
Please note that I tried sending a notification in Firebase Cloud Messaging: https://console.firebase.google.com/u/0/project/.... then I got a notification, so you can see that my APNS configuration is not wrong.
Looking forward to everyone's help. I also spent more than 2 weeks searching and trying to no avail. Thank you very much.
Update Backend I write API to send:
public void PushNotificationToFCM(string deviceTokens, string title, string body, object data, string linkdirection)
{
FirebaseApp.Create(new AppOptions() {
Credential= GoogleCredential.FromFile("private_key.json")
});
// This registration token comes from the client FCM SDKs.
var registrationToken = deviceTokens;
// See documentation on defining a message payload.
var message = new Message()
{
Apns = new ApnsConfig { Aps = new Aps { ContentAvailable = true, Sound = "default" } },
Data = new Dictionary<string, string>()
{
{ "link", linkdirection },
},
Token = registrationToken,
Notification= new FirebaseAdmin.Messaging.Notification()
{
Title = title,
Body = body,
}
};
// Send a message to the device corresponding to the provided
// registration token.
string response = FirebaseMessaging.DefaultInstance.SendAsync(message).Result;
// Response is a message ID string.
Debug.WriteLine("Successfully sent message: " + response);
}
I don't know if such iOS device's TokenDevice code is correct? I tried to submit it, however getting the error: System.AggregateException: 'One or more errors occurred. (The registration token is not a valid FCM registration token)'
I uninstalled and reinstalled the app, I got a new tokenDevice. However when I send the notification I still get the same error
As an alternative workaround, you can use this NuGet package Plugin.FirebasePushNotification.
1. After Creating the iOS Project, add "GoogleService-Info.plist" file to your iOS Project and update the property to BundleResource.
2. In Info.plist and FinishedLaunching method, add following line:
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
FirebasePushNotificationManager.Initialize(options, true);
return base.FinishedLaunching(app, options);
}
3. Then add the below in App Construct.
CrossFirebasePushNotification.Current.OnTokenRefresh += (s, p) =>
{
System.Diagnostics.Debug.WriteLine($"TOKEN : {p.Token}");
};
CrossFirebasePushNotification.Current.OnNotificationReceived += (s, p) =>
{
System.Diagnostics.Debug.WriteLine("Received");
foreach (var data in p.Data)
{
System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
}
};
CrossFirebasePushNotification.Current.OnNotificationOpened += (s, p) =>
{
System.Diagnostics.Debug.WriteLine("Opened");
foreach (var data in p.Data)
{
System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
}
};
4. After getting the FCM registration token. then click Test, the targeted client device (with the app in the background) should receive the notification in the system notifications tray.
I am having problem sending notification to specific devices with FCM.
This is how I get the specific device ID
Add Class in Android Project: FirebaseMessagingServiceDelegate.cs
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseMessagingServiceDelegate : FirebaseMessagingService
{
public async override void OnNewToken(string p0)
{
base.OnNewToken(p0);
var instanceIdResult = await FirebaseInstanceId.Instance.GetInstanceId().AsAsync<IInstanceIdResult>();
var token = instanceIdResult.Token;//(Token Devices)
}
}
Each time the user opens the App: var token = instanceIdResult.Token; will be changed. So every time I have to update the token on my server? Does this mean I also create a Class on Project iOS?
And this is how I send notifications to specific device:
PageOne.xaml.cs
protected void SendNotify()
{
// This registration token comes from the client FCM SDKs.
var registrationToken = "Token Devices";
// See documentation on defining a message payload.
var message = new Message()
{
Token = registrationToken,
Notification = new Notification()
{
Title = "Test from code",
Body = "Here is your test!"
}
};
// Send a message to the device corresponding to the provided
// registration token.
string response = FirebaseMessaging.DefaultInstance.SendAsync(message).Result;
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);
}
I received the error: Notification does not contain a definition for "Body"
I searched and found this article: firebase send notification from server c#. However I want to send directly from Xamarin App.
Hoping for any help. Thank you!
I want to store the firebase push notification into sqlite when the app in any state like app is closed/background/foreground. How can i do this? Is it possible to do this?
In your FirebaseMessagingService inheriting class you have two important methods that you can use for this:
For Foreground notifications:
This method is called when your application is in foreground state
public override void OnMessageReceived(RemoteMessage message)
{
// SQLite saving code here
}
And For Background and Killed state notifications:
This method is called when your application is in background/killed state
public override void HandleIntent(Intent p0)
{
base.HandleIntent(p0);
// SQLite saving code here
}
Note: that in certain cases this function is also called in foreground notifications that might cause duplication.
When you are getting your notification in OnMessageReceived method, check app state using this method
private bool isApplicationInTheBackground()
{
bool isInBackground;
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.GetMyMemoryState(myProcess);
isInBackground = myProcess.Importance != Android.App.Importance.background;
return isInBackground;
}
If your app in background state, fetch notification information from OnMessageReceived and insert that in desired table
public override void OnMessageReceived(RemoteMessage message)
{
string orgId;
var data = message.Data.ToDictionary(i => i.Key, i => i.Value);
if (data.ContainsKey("organization"))
orgId = data["organization"];
if(isApplicationInTheBackground())
{
var db = GetCon();
db.Insert(orgId); //here should be your table.
}
}
And when you want to send/deliver saved notifications use local android push notifications.
here is the final solution for this problem, thanks to Mr.G.hakim
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(TAG, "From: " + message.From);
Log.Debug(TAG, "Notification Message Body: " + getBody);
SendNotification(getBody, message.Data);
}
public override void HandleIntent(Intent p0)// this method will fire when the app in background and closed state
{
base.HandleIntent(p0);
if (p0.Extras != null)
{
foreach (var key in p0.Extras.KeySet())
{
var value = p0.Extras.GetString(key);
Log.Debug(TAG, "Key: {0} Value: {1}", key, value);
if(key== "gcm.notification.title")
{
Log.Debug("Delay Notification Title", "" + value);
getBGTitle = value;//declared local variable
}
else if(key== "gcm.notification.body")
{
Log.Debug("Delay Notification Body", "" + value);
getBGBody = value;//declared local variable
insertData(getBGTitle,getBGBody)//call method for store SQLite Insert
}
}
}
}
Using the Notifications REST API and JavaScript, we are subscribing our Progressive Web App through FCM and then calling the registrations endpoint to register on our ANH
The registration completes fine and we can see the registration on our hub with the correct platform and a populated PNS Identifier
When we attempt to send a test message to all registered devices, we get the following error in ANH
The token obtained from the token provider is wrong
We have tried sending the entire endpoint object returned by Firebase, just the subscriptionId and various other combinations
Does the error message mean that we have subscribed using the wrong key pair or is the format of the token incorrect? There is nowhere that shows an example of what the GcmRegistrationId format should be when registering with the registrations endpoint
You can use below approach to register your Devices:
In case of GCM follow this approach:
use Nuget package for Notification Hubs.
For DeviceRegistration.cs
public class DeviceRegistration
{
public string Platform { get; set; }
public string Handle { get; set; }
public string[] Tags { get; set; }
}
For NotificationClient.cs
using Microsoft.Azure.NotificationHubs; // Namespace to be used
// Use below method to get registrationID
public async Task<string> GetRegistrationID(NotificationHubClient Hub, string handle = null)
{
string newRegistrationId = null;
// make sure there are no existing registrations for this push handle (used for iOS and Android)
if (handle != null)
{
var registrations = await Hub.GetRegistrationsByChannelAsync(handle, 100);
foreach (RegistrationDescription registration in registrations)
{
if (newRegistrationId == null)
{
newRegistrationId = registration.RegistrationId;
}
else
{
await Hub.DeleteRegistrationAsync(registration);
}
}
}
if (newRegistrationId == null)
newRegistrationId = await Hub.CreateRegistrationIdAsync();
return newRegistrationId;
}
// Use below method to upsert registration to azure
public async Task UpsertRegistration(string registrationid, DeviceRegistration deviceUpdate, NotificationHubClient Hub)
{
string[] tags = { "abc","def" }; // These are used to send notifications
DeviceRegistration deviceRegistration = new DeviceRegistration
{
Handle = newDeviceToken, // Device token given by Firebase
Platform = "gcm", // Specify gcm for android and "apns" for ios
Tags = tags
};
RegistrationDescription registration
= new GcmRegistrationDescription(deviceRegistration.Handle);
registration.RegistrationId = registrationid;
// add check if user is allowed to add these tags
registration.Tags = new HashSet<string>();
foreach (string tag in deviceUpdate.Tags)
{
registration.Tags.Add(tag);
}
await Hub.CreateOrUpdateRegistrationAsync(registration);
}
Error:(29, 44) error: cannot access AbstractSafeParcelable
class file for com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable not found
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = MyFirebaseInstanceIDService.class.getSimpleName();
#Override
public void onTokenRefresh() {
super.onTokenRefresh();
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
// Saving reg id to shared preferences
storeRegIdInPref(refreshedToken);
// sending reg id to your server
sendRegistrationToServer(refreshedToken);
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(Config.REGISTRATION_COMPLETE);
registrationComplete.putExtra("token", refreshedToken);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
private void sendRegistrationToServer(final String token) {
// sending gcm token to server
Log.e(TAG, "sendRegistrationToServer: " + token);
}
private void storeRegIdInPref(String token) {
SharedPreferences pref = getApplicationContext().getSharedPreferences(Config.SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putString("regId", token);
editor.commit();
}
}
In your Gradle file all the versions of google-play-service and firebase should all use the same version.
As you are using :
compile 'com.google.firebase:firebase-core:10.0.1'
You should use :
compile 'com.google.firebase:firebase-messaging:10.0.1' // and not 9.4.0
So if you are using google-play-services, please update the version to 10.0.1.