Goals:
Test environment used:
Prerequisites:
Steps needed to associate notifications
Step 1: Configure the distribution of the app on the developer.apple.com portal and on https://appstoreconnect.apple.com/apps/ <appId>
Step 2: Add the management code on the Delphi application and import the firebase sdk for iOS

$(Firebase_6_28)\FirebaseAnalytics\nanopb.xcframework\ios-armv7_arm64\nanopb.framework;$(Firebase_6_28)\FirebaseAnalytics\GoogleUtilities.xcframework\ios-armv7_arm64\GoogleUtilities.framework;$(Firebase_6_28)\FirebaseAnalytics\GoogleDataTransport.xcframework\ios-armv7_arm64\GoogleDataTransport.framework;$(Firebase_6_28)\FirebaseAnalytics\GoogleAppMeasurement.framework;$(Firebase_6_28)\FirebaseAnalytics\FirebaseInstallations.xcframework\ios-armv7_arm64\FirebaseInstallations.framework;$(Firebase_6_28)\FirebaseAnalytics\FirebaseCoreDiagnostics.xcframework\ios-armv7_arm64\FirebaseCoreDiagnostics.framework;$(Firebase_6_28)\FirebaseAnalytics\FirebaseCore.xcframework\ios-armv7_arm64\FirebaseCore.framework;$(Firebase_6_28)\FirebaseAnalytics\FirebaseAnalytics.framework;$(Firebase_6_28)\FirebaseMessaging\FirebaseMessaging.xcframework\ios-armv7_arm64\FirebaseMessaging.framework;$(Firebase_6_28)\FirebaseMessaging\FirebaseInstanceID.xcframework\ios-armv7_arm64\FirebaseInstanceID.framework;$(Firebase_6_28)\FirebaseAnalytics\PromisesObjC.framework\ios-armv7_arm64\PromisesObjC.framework;$(Firebase_6_28)\FirebaseMLModelInterpreter\Protobuf.xcframework\ios-armv7_arm64\Protobuf.framework

unit iOSapi.FirebaseCommon;
{*******************************************************}
{ }
{ CodeGear Delphi Runtime Library }
{ }
{ Copyright(c) 2010-2021 Embarcadero Technologies, Inc. }
{ All rights reserved }
{ }
{*******************************************************}
interface
uses
Macapi.ObjectiveC,
iOSapi.CocoaTypes, iOSapi.Foundation;
const
FIRInstanceIDErrorUnknown = 0;
FIRInstanceIDErrorAuthentication = 1;
FIRInstanceIDErrorNoAccess = 2;
FIRInstanceIDErrorTimeout = 3;
FIRInstanceIDErrorNetwork = 4;
FIRInstanceIDErrorOperationInProgress = 5;
FIRInstanceIDErrorInvalidRequest = 7;
FIRInstanceIDAPNSTokenTypeUnknown = 0;
FIRInstanceIDAPNSTokenTypeSandbox = 1;
FIRInstanceIDAPNSTokenTypeProd = 2;
type
FIRInstanceIDError = NSUInteger;
FIRAppClass = interface(NSObjectClass)
['{B8962096-555F-498E-B102-8EC66E871EF2}']
{class} procedure configure; cdecl;
end;
FIRApp = interface(NSObject)
['{FFF4B247-25C6-47B8-BBC5-893D2170EFA5}']
end;
TFIRApp = class(TOCGenericImport<FIRAppClass, FIRApp>) end;
FIRInstanceIDClass = interface(NSObjectClass)
['{4A9F1C85-AEDE-4284-A7DC-0EF9111504B1}']
{class} function instanceID: pointer; cdecl;
end;
FIRInstanceID = interface(NSObject)
['{2967A1F9-98F5-40E6-8BDA-A25D3C699ED3}']
function token: NSString; cdecl;
end;
TFIRInstanceID = class(TOCGenericImport<FIRInstanceIDClass, FIRInstanceID>) end;
implementation
uses
System.Sqlite, System.ZLib,
iOSapi.StoreKit;
const
libSystemConfiguration = '/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration';
procedure ClangRTLoader; cdecl; external '/usr/lib/clang/lib/darwin/libclang_rt.ios.a';
procedure FirebaseAnalyticsLoader; cdecl; external 'FirebaseAnalytics';
procedure FirebaseCoreLoader; cdecl; external 'FirebaseCore';
procedure FirebaseCoreDiagnosticsLoader; cdecl; external 'FirebaseCoreDiagnostics';
procedure FirebaseInstallationsLoader; cdecl; external 'FirebaseInstallations';
procedure FoundationLoader; cdecl; external {$IFDEF IOS32}libFoundation{$ELSE}framework 'Foundation'{$ENDIF};
procedure GoogleAppMeasurementLoader; cdecl; external 'GoogleAppMeasurement';
procedure GoogleDataTransportLoader; cdecl; external 'GoogleDataTransport';
procedure GoogleUtilitiesLoader; cdecl; external 'GoogleUtilities';
procedure nanopbLoader; cdecl; external 'nanopb';
procedure PromisesObjCLoader; cdecl; external 'PromisesObjC';
//procedure PromisesObjCLoader; cdecl; external 'PromisesObjC';
procedure SystemConfigurationLoader; cdecl; external {$IFDEF IOS32}libSystemConfiguration{$ELSE}framework 'SystemConfiguration'{$ENDIF};
procedure StoreKitLoader; cdecl; external {$IFDEF IOS32}libStoreKit{$ELSE}framework 'StoreKit'{$ENDIF};
end.
unit iOSapi.FirebaseMessaging;
{*******************************************************}
{ }
{ CodeGear Delphi Runtime Library }
{ }
{ Copyright(c) 2010-2021 Embarcadero Technologies, Inc. }
{ All rights reserved }
{ }
{*******************************************************}
interface
uses
Macapi.ObjectiveC,
iOSapi.CocoaTypes, iOSapi.Foundation;
const
FIRMessagingErrorUnknown = 0;
FIRMessagingErrorAuthentication = 1;
FIRMessagingErrorNoAccess = 2;
FIRMessagingErrorTimeout = 3;
FIRMessagingErrorNetwork = 4;
FIRMessagingErrorOperationInProgress = 5;
FIRMessagingErrorInvalidRequest = 7;
FIRMessagingMessageStatusUnknown = 0;
FIRMessagingMessageStatusNew = 1;
FIRMessasingAPNSTokenTypeUnknown = 0;
FIRMessasingAPNSTokenTypeSandbox = 1;
FIRMessasingAPNSTokenTypeProd = 2;
type
FIRInstanceIDAPNSTokenType = NSInteger;
FIRMessagingAPNSTokenType = NSInteger;
FIRMessagingError = NSUInteger;
FIRMessagingMessageStatus = NSInteger;
TFIRMessagingConnectCompletion = procedure(error: NSError) of object;
FIRMessagingMessageInfoClass = interface(NSObjectClass)
['{FDAC534F-3D79-4FF6-824E-50DC7423662A}']
end;
FIRMessagingMessageInfo = interface(NSObject)
['{4D70F5C5-3635-405F-895C-F41C8D1FD76B}']
function status: FIRMessagingMessageStatus; cdecl;
end;
TFIRMessagingMessageInfo = class(TOCGenericImport<FIRMessagingMessageInfoClass, FIRMessagingMessageInfo>) end;
FIRMessagingRemoteMessageClass = interface(NSObjectClass)
['{EF45D074-C7A5-4DB2-BCD1-53B8650419F4}']
end;
FIRMessagingRemoteMessage = interface(NSObject)
['{6E2F8E14-FD8D-4B5D-8026-A607BE0B8F9C}']
function appData: NSDictionary; cdecl;
end;
TFIRMessagingRemoteMessage = class(TOCGenericImport<FIRMessagingRemoteMessageClass, FIRMessagingRemoteMessage>) end;
FIRMessaging = interface;
FIRMessagingDelegate = interface(IObjectiveC)
['{264C1F0E-3EA9-42AC-9802-EF1BC9A7E321}']
procedure applicationReceivedRemoteMessage(remoteMessage: FIRMessagingRemoteMessage); cdecl;
[MethodName('messaging:didReceiveMessage:')]
procedure didReceiveMessage(messaging: FIRMessaging; remoteMessage: FIRMessagingRemoteMessage); cdecl;
[MethodName('messaging:didRefreshRegistrationToken:')]
procedure didRefreshRegistrationToken(messaging: FIRMessaging; fcmToken: NSString); cdecl;
[MethodName('messaging:didReceiveRegistrationToken:')]
procedure didReceiveRegistrationToken(messaging: FIRMessaging; fcmToken: NSString); cdecl;
end;
FIRMessagingClass = interface(NSObjectClass)
['{62AF9A4C-681E-4BCD-9063-6209CAE08296}']
{class} function messaging: pointer; cdecl;
end;
FIRMessaging = interface(NSObject)
['{A721C3D4-82EB-4A7B-A5E5-42EF9E8F618E}']
function APNSToken: NSData; cdecl;
procedure connectWithCompletion(handler: TFIRMessagingConnectCompletion); cdecl;
function delegate: Pointer; cdecl;
procedure disconnect; cdecl;
procedure sendMessage(msg: NSDictionary; receiver: NSString; messageID: NSString; ttl: Int64); cdecl;
procedure setAPNSToken(apnsToken: NSData; tokenType: FIRMessagingAPNSTokenType); cdecl;
procedure setDelegate(delegate: Pointer); cdecl;
function shouldEstablishDirectChannel: Boolean; cdecl;
procedure setShouldEstablishDirectChannel(value: Boolean); cdecl;
procedure subscribeToTopic(topic: NSString); cdecl;
procedure unsubscribeFromTopic(topic: NSString); cdecl;
end;
TFIRMessaging = class(TOCGenericImport<FIRMessagingClass, FIRMessaging>) end;
function kFIRInstanceIDTokenRefreshNotification: NSString; cdecl;
implementation
uses
iOSapi.FirebaseCommon,
Macapi.Helpers;
function kFIRInstanceIDTokenRefreshNotification: NSString;
begin
Result := StrToNSStr('com.firebase.iid.notif.refresh-token');
end;
procedure FirebaseInstanceIDLoader; cdecl; external 'FirebaseInstanceID';
procedure FirebaseMessagingLoader; cdecl; external 'FirebaseMessaging';
procedure ProtobufLoader; cdecl; external 'Protobuf';
end.
interface
uses xxxx
,System.PushNotification
{$IFDEF ANDROID},FMX.PushNotification.Android {$ENDIF}
{$IFDEF IOS}
, FMX.PushNotification.iOS
, FMX.PushNotification.FCM.iOS
{$ENDIF}
..
..
..
uses System.NetEncoding,
{$IFDEF IOS}
,iOSapi.Foundation
,iOSapi.CoreTelephony
{$ENDIF}
{$IFDEF ANDROID}
,Androidapi.Helpers,
Androidapi.JNIBridge,
Androidapi.Jni,
Androidapi.JNI.JavaTypes,
FMX.Platform.Android,
Androidapi.JNI.Os,
FMX.Helpers.Android
{$ENDIF}
;
TfmxMain = class(TForm)
....
private
{ Private declarations }
o : TMyBridgeObject;
// PUSH NOTIFICATION ID
FDeviceId: string;
FDeviceToken: string;
function HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
procedure btnInitializePushClick(Sender: TObject);
{$IF (defined(ANDROID)) OR (defined(IOS))}
procedure OnServiceConnectionChange(Sender: TObject; PushChanges: TPushService.TChanges);
procedure OnReceiveNotificationEvent(Sender: TObject; const ServiceNotification: TPushServiceNotification);
{$ENDIF}
public
{ Public declarations }
....
end;
{$IF defined(ANDROID) OR defined(IOS)}
procedure TfmxMain.OnReceiveNotificationEvent(Sender: TObject;
const ServiceNotification: TPushServiceNotification);
var
MessageText: string;
begin
MemoLog.Lines.Add('-----------------------------------------');
MemoLog.Lines.Add('DataKey = ' + ServiceNotification.DataKey);
MemoLog.Lines.Add('Json = ' + ServiceNotification.Json.ToString);
MemoLog.Lines.Add('DataObject = ' + ServiceNotification.DataObject.ToString);
MemoLog.Lines.Add('---------------------------------------');
end;
procedure TfmxMain.OnServiceConnectionChange(Sender: TObject;
PushChanges: TPushService.TChanges);
var
PushService: TPushService;
begin
PushService := TPushServiceManager.Instance.GetServiceByName
(TPushService.TServiceNames.FCM);
if TPushService.TChange.DeviceToken in PushChanges then
begin
FDeviceToken := PushService.DeviceTokenValue
[TPushService.TDeviceTokenNames.DeviceToken];
MemoLog.Lines.Add('Firebase Token: ' + FDeviceToken);
// Log.d('Firebase device token: token=' + FDeviceToken);
end;
if (TPushService.TChange.Status in PushChanges) and
(PushService.Status = TPushService.TStatus.StartupError) then
MemoLog.Lines.Add('Error: ' + PushService.StartupError);
end;
{$ENDIF}
procedure TfmxMain.btnIniutializePushClick(Sender: TObject);
{$IF (defined(ANDROID) OR defined(IOS))}
var
PushService: TPushService;
ServiceConnection: TPushServiceConnection;
Notifications: TArray<TPushServiceNotification>;
begin
PushService :=
TPushServiceManager.Instance.GetServiceByName(TPushService.TServiceNames.FCM);
ServiceConnection := TPushServiceConnection.Create(PushService);
ServiceConnection.Active := True;
ServiceConnection.OnChange := OnServiceConnectionChange;
ServiceConnection.OnReceiveNotification := OnReceiveNotificationEvent;
FDeviceId :=
PushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceId];
MemoLog.Lines.Add('DeviceID: ' + FDeviceId);
MemoLog.Lines.Add('Ready to receive!');
// Checks notification on startup, if application was launched fromcold start
// by tapping on Notification in Notification Center
Notifications := PushService.StartupNotifications;
if Length(Notifications) > 0 then
begin
MemoLog.Lines.Add('-----------------------------------------');
MemoLog.Lines.Add('DataKey = ' + Notifications[0].DataKey);
MemoLog.Lines.Add('Json = ' + Notifications[0].Json.ToString);
MemoLog.Lines.Add('DataObject = ' +
Notifications[0].DataObject.ToString);
MemoLog.Lines.Add('-----------------------------------------');
end;
end;
{$ELSE}
begin
end;
{$ENDIF}
Step 3: Configure the project on firebase:



At this point, compile the application and run it on your device.
Inside memoLog you will find a JSON containing the deviceID, you have to copy it to test sending the first message from firebase.
Step 5) Send a test push message from the firebase console or your server

Copy the server key and perform a rest call as follows:

The JSON must also contain the “notification” object if you want the notification to arrive even when your app is not active:
{
"to": "d-fmNb_zQ6u39hIOXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"priority" : "high",
"message_id":"2205",
"time_to_live": 200,
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
},
"data":{
"body":"Ciao",
"title":"Cisco"
}
}
that’s all