From 042be8b03fca615dcb4fa7d656c334c99b9cdb4e Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 18 Jun 2024 13:41:13 -0400 Subject: [PATCH 1/2] Update facebook and firebase samples --- facebook/sample/Sample.csproj | 4 ++++ firebase/macios/README.md | 3 +++ firebase/macios/sample/Sample.csproj | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/facebook/sample/Sample.csproj b/facebook/sample/Sample.csproj index 08571ae..867cd5d 100644 --- a/facebook/sample/Sample.csproj +++ b/facebook/sample/Sample.csproj @@ -64,6 +64,10 @@ + + false + false + false false diff --git a/firebase/macios/README.md b/firebase/macios/README.md index 9cf11f6..52e4c06 100644 --- a/firebase/macios/README.md +++ b/firebase/macios/README.md @@ -14,6 +14,9 @@ For more details, reference the [Get Started (iOS)][2] page. 1. Download your `GoogleService-Info.plist` and replace `Platforms/iOS/GoogleService-Info.plist` with it. 2. Change the `` value in `Sample.csproj` to your Firebase iOS app identifier. +To deploy this sample to a physical iOS device you will need to open `native/MauiFirebase.xcodeproj` +in Xcode and configure the signing team/settings in Targets -> MauiFirebase -> Signing & Capabilities. +A provisioning profile that supports Apple Notification Service (APNs) must also be configured and installed. [0]: https://firebase.google.com/docs/analytics/get-started?platform=ios [1]: https://firebase.google.com/docs/cloud-messaging/ios/client diff --git a/firebase/macios/sample/Sample.csproj b/firebase/macios/sample/Sample.csproj index fa07849..95c3edf 100644 --- a/firebase/macios/sample/Sample.csproj +++ b/firebase/macios/sample/Sample.csproj @@ -17,7 +17,7 @@ enable - Firebase Sample + Firebase.Sample com.companyname.firebase.sample From a4ecc36aee38e6c268141acaf759274f19f7b4a3 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Tue, 18 Jun 2024 16:00:58 -0400 Subject: [PATCH 2/2] Add Firebase Auth --- README.md | 1 + .../Firebase.MaciOS.Binding/ApiDefinitions.cs | 85 ++++++++++ firebase/macios/README.md | 9 +- .../MauiFirebase.xcodeproj/project.pbxproj | 20 ++- .../native/MauiFirebase/MauiFIRAuth.swift | 152 ++++++++++++++++++ firebase/macios/sample/AppTabbedPage.xaml | 5 + firebase/macios/sample/AppTabbedPage.xaml.cs | 4 +- .../macios/sample/FIRAnalyticsPage.xaml.cs | 3 +- firebase/macios/sample/FIRAuthPage.xaml | 49 ++++++ firebase/macios/sample/FIRAuthPage.xaml.cs | 39 +++++ .../macios/sample/FIRMessagingPage.xaml.cs | 2 +- 11 files changed, 359 insertions(+), 10 deletions(-) create mode 100644 firebase/macios/native/MauiFirebase/MauiFIRAuth.swift create mode 100644 firebase/macios/sample/FIRAuthPage.xaml create mode 100644 firebase/macios/sample/FIRAuthPage.xaml.cs diff --git a/README.md b/README.md index bc6099c..26474e5 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ Install prerequisites: - [Android SDK](https://developer.android.com/tools) - [Android Studio](https://developer.android.com/studio) - [Objective-Sharpie](https://aka.ms/objective-sharpie) + - [Xamarin.iOS](https://download.visualstudio.microsoft.com/download/pr/ceb0ea3f-4db8-46b4-8dc3-8049d27c0107/3960868aa9b1946a6c77668c3f3334ee/xamarin.ios-16.4.0.23.pkg) - [Visual Studio](https://visualstudio.microsoft.com/downloads/) or [Visual Studio Code](https://code.visualstudio.com/download) - [Xcode](https://developer.apple.com/xcode/) - [Xcode Command Line Tools](https://developer.apple.com/download/all/?q=command%20line%20tools) (```xcode-select --install```) diff --git a/firebase/macios/Firebase.MaciOS.Binding/ApiDefinitions.cs b/firebase/macios/Firebase.MaciOS.Binding/ApiDefinitions.cs index 297fc4f..4ae52fc 100644 --- a/firebase/macios/Firebase.MaciOS.Binding/ApiDefinitions.cs +++ b/firebase/macios/Firebase.MaciOS.Binding/ApiDefinitions.cs @@ -1,6 +1,7 @@ #nullable enable using System; using Foundation; +using ObjCRuntime; namespace Firebase { @@ -55,6 +56,90 @@ interface MauiFIRApp void Configure (string googleAppId, string gcmSenderId); } + // @interface MauiFIRAuth : NSObject + [BaseType (typeof(NSObject))] + interface MauiFIRAuth + { + // -(void)setAuthStateListener:(void (^ _Nonnull)(MauiFIRAuthUser * _Nullable))callback; + [Static] + [Export ("setAuthStateListener:")] + [Async] + void SetAuthStateListener (Action callback); + + // -(void)createUser:(NSString * _Nonnull)email password:(NSString * _Nonnull)password callback:(void (^ _Nonnull)(MauiFIRAuthResult * _Nullable, NSError * _Nullable))callback; + [Static] + [Export ("createUser:password:callback:")] + [Async] + void CreateUser (string email, string password, Action callback); + + // -(void)signIn:(NSString * _Nonnull)email password:(NSString * _Nonnull)password callback:(void (^ _Nonnull)(MauiFIRAuthResult * _Nullable, NSError * _Nullable))callback; + [Static] + [Export ("signIn:password:callback:")] + [Async] + void SignIn (string email, string password, Action callback); + + // -(NSError * _Nullable)signOut __attribute__((warn_unused_result(""))); + [Static] + [Export ("signOut")] + NSError SignOut(); + } + + // @interface MauiFIRAuthResult : NSObject + [BaseType (typeof(NSObject))] + [DisableDefaultCtor] + interface MauiFIRAuthResult + { + // @property (readonly, nonatomic, strong) MauiFIRAuthUser * _Nullable user; + [NullAllowed, Export ("user", ArgumentSemantic.Strong)] + MauiFIRAuthUser User { get; } + } + + // @interface MauiFIRAuthUser : NSObject + [BaseType (typeof(NSObject))] + [DisableDefaultCtor] + interface MauiFIRAuthUser + { + // @property (readonly, copy, nonatomic) NSString * _Nullable uid; + [NullAllowed, Export ("uid")] + string Uid { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable displayName; + [NullAllowed, Export ("displayName")] + string DisplayName { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable email; + [NullAllowed, Export ("email")] + string Email { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable refreshToken; + [NullAllowed, Export ("refreshToken")] + string RefreshToken { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable providerId; + [NullAllowed, Export ("providerId")] + string ProviderId { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable tenantId; + [NullAllowed, Export ("tenantId")] + string TenantId { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nullable phoneNumber; + [NullAllowed, Export ("phoneNumber")] + string PhoneNumber { get; } + + // @property (readonly, nonatomic) BOOL isAnonymous; + [Export ("isAnonymous")] + bool IsAnonymous { get; } + + // @property (readonly, nonatomic) BOOL isEmailVerified; + [Export ("isEmailVerified")] + bool IsEmailVerified { get; } + + // @property (readonly, copy, nonatomic) NSURL * _Nullable photoUrl; + [NullAllowed, Export ("photoUrl", ArgumentSemantic.Copy)] + NSUrl PhotoUrl { get; } + } + // @interface MauiFIRMessaging : NSObject [BaseType (typeof(NSObject))] interface MauiFIRMessaging diff --git a/firebase/macios/README.md b/firebase/macios/README.md index 52e4c06..d82a2cf 100644 --- a/firebase/macios/README.md +++ b/firebase/macios/README.md @@ -1,5 +1,5 @@ # Firebase Slim Binding -This folder contains a slim binding for the Firebase SDK which demonstrates simple [Analytics][0] and [Messaging][1] usage. +This folder contains a slim binding for the Firebase SDK which demonstrates simple [Analytics][0], [Auth][1], and [Messaging][2] usage. ### Build and Run ```shell @@ -9,7 +9,7 @@ This folder contains a slim binding for the Firebase SDK which demonstrates simp ### Configure The included sample requires some modification to fully function. You will need to log in to a Firebase developer account and configure an app to interface with this sample. -For more details, reference the [Get Started (iOS)][2] page. +For more details, reference the [Get Started (iOS)][3] page. 1. Download your `GoogleService-Info.plist` and replace `Platforms/iOS/GoogleService-Info.plist` with it. 2. Change the `` value in `Sample.csproj` to your Firebase iOS app identifier. @@ -19,5 +19,6 @@ in Xcode and configure the signing team/settings in Targets -> MauiFirebase -> S A provisioning profile that supports Apple Notification Service (APNs) must also be configured and installed. [0]: https://firebase.google.com/docs/analytics/get-started?platform=ios -[1]: https://firebase.google.com/docs/cloud-messaging/ios/client -[2]: https://firebase.google.com/docs/ios/setup +[1]: https://firebase.google.com/docs/auth/ios/start +[2]: https://firebase.google.com/docs/cloud-messaging/ios/client +[3]: https://firebase.google.com/docs/ios/setup diff --git a/firebase/macios/native/MauiFirebase.xcodeproj/project.pbxproj b/firebase/macios/native/MauiFirebase.xcodeproj/project.pbxproj index 44b7578..8c9c8ae 100644 --- a/firebase/macios/native/MauiFirebase.xcodeproj/project.pbxproj +++ b/firebase/macios/native/MauiFirebase.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ D051A4C32BD33D9100EC7F28 /* MauiFIRApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D051A4C22BD33D9100EC7F28 /* MauiFIRApp.swift */; }; D051A4C52BD33DB100EC7F28 /* MauiFIRMessaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D051A4C42BD33DB100EC7F28 /* MauiFIRMessaging.swift */; }; D051A4C72BD33DBB00EC7F28 /* MauiFIRAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D051A4C62BD33DBB00EC7F28 /* MauiFIRAnalytics.swift */; }; + D0A597A92C22007900C52635 /* MauiFIRAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A597A82C22007900C52635 /* MauiFIRAuth.swift */; }; + D0A597AB2C2200A800C52635 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = D0A597AA2C2200A800C52635 /* FirebaseAuth */; }; D0BECB2A2BD833BA00CDDD0A /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = D0BECB292BD833BA00CDDD0A /* FirebaseAnalytics */; }; D0BECB2C2BD833BA00CDDD0A /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = D0BECB2B2BD833BA00CDDD0A /* FirebaseMessaging */; }; D0BECB2E2BD8381100CDDD0A /* FirebaseInstallations in Frameworks */ = {isa = PBXBuildFile; productRef = D0BECB2D2BD8381100CDDD0A /* FirebaseInstallations */; }; @@ -22,6 +24,7 @@ D051A4C22BD33D9100EC7F28 /* MauiFIRApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MauiFIRApp.swift; sourceTree = ""; }; D051A4C42BD33DB100EC7F28 /* MauiFIRMessaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MauiFIRMessaging.swift; sourceTree = ""; }; D051A4C62BD33DBB00EC7F28 /* MauiFIRAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MauiFIRAnalytics.swift; sourceTree = ""; }; + D0A597A82C22007900C52635 /* MauiFIRAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MauiFIRAuth.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -32,6 +35,7 @@ D0BECB2E2BD8381100CDDD0A /* FirebaseInstallations in Frameworks */, D0BECB2C2BD833BA00CDDD0A /* FirebaseMessaging in Frameworks */, D0BECB2A2BD833BA00CDDD0A /* FirebaseAnalytics in Frameworks */, + D0A597AB2C2200A800C52635 /* FirebaseAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -62,6 +66,7 @@ D051A4C22BD33D9100EC7F28 /* MauiFIRApp.swift */, D051A4C42BD33DB100EC7F28 /* MauiFIRMessaging.swift */, D051A4C62BD33DBB00EC7F28 /* MauiFIRAnalytics.swift */, + D0A597A82C22007900C52635 /* MauiFIRAuth.swift */, ); path = MauiFirebase; sourceTree = ""; @@ -105,6 +110,7 @@ D0BECB292BD833BA00CDDD0A /* FirebaseAnalytics */, D0BECB2B2BD833BA00CDDD0A /* FirebaseMessaging */, D0BECB2D2BD8381100CDDD0A /* FirebaseInstallations */, + D0A597AA2C2200A800C52635 /* FirebaseAuth */, ); productName = MauiFirebase; productReference = D051A4B82BD33D4600EC7F28 /* MauiFirebase.framework */; @@ -164,6 +170,7 @@ D051A4C72BD33DBB00EC7F28 /* MauiFIRAnalytics.swift in Sources */, D051A4C52BD33DB100EC7F28 /* MauiFIRMessaging.swift in Sources */, D051A4C32BD33D9100EC7F28 /* MauiFIRApp.swift in Sources */, + D0A597A92C22007900C52635 /* MauiFIRAuth.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -302,9 +309,11 @@ buildSettings = { BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -312,7 +321,7 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -341,9 +350,11 @@ buildSettings = { BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -351,7 +362,7 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -409,6 +420,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + D0A597AA2C2200A800C52635 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = D0BECB282BD833BA00CDDD0A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; D0BECB292BD833BA00CDDD0A /* FirebaseAnalytics */ = { isa = XCSwiftPackageProductDependency; package = D0BECB282BD833BA00CDDD0A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/firebase/macios/native/MauiFirebase/MauiFIRAuth.swift b/firebase/macios/native/MauiFirebase/MauiFIRAuth.swift new file mode 100644 index 0000000..f3d11d6 --- /dev/null +++ b/firebase/macios/native/MauiFirebase/MauiFIRAuth.swift @@ -0,0 +1,152 @@ +// +// MauiFIRAuth.swift +// MauiFirebase +// +// Created by Peter Collins on 6/18/24. +// + +import UIKit +import FirebaseAuth + +@objc(MauiFIRAuth) +public class MauiFIRAuth : NSObject { + + static var authStateDidChangeListenerHandle: AuthStateDidChangeListenerHandle? + + @objc(setAuthStateListener:) + public static func setAuthStateListener(callback: @escaping (MauiFIRAuthUser?) -> Void) -> Void { + let auth = Auth.auth() + + if (authStateDidChangeListenerHandle != nil) { + auth.removeStateDidChangeListener(authStateDidChangeListenerHandle!) + } + + authStateDidChangeListenerHandle = auth.addStateDidChangeListener { auth, user in + callback(MauiFIRAuthUser(user: user!)) + } + } + + @objc(createUser:password:callback:) + public static func createUser(email: String, password: String, callback: @escaping (MauiFIRAuthResult?, NSError?) -> Void) -> Void { + Auth.auth().createUser(withEmail: email, password: password) { authResult, authError in + + var far: MauiFIRAuthResult? = nil; + + if (authResult != nil) { + far = MauiFIRAuthResult(authResult: authResult!) + } + + callback(far, authError as NSError?) + } + } + + @objc(signIn:password:callback:) + public static func signIn(email: String, password: String, callback: @escaping (MauiFIRAuthResult?, NSError?) -> Void) -> Void { + Auth.auth().signIn(withEmail: email, password: password) { authResult, authError in + var far: MauiFIRAuthResult? = nil; + + if (authResult != nil) { + far = MauiFIRAuthResult(authResult: authResult!) + } + + callback(far, authError as NSError?) + } + } + + @objc + public static func signOut() -> NSError? { + let firebaseAuth = Auth.auth() + do { + try firebaseAuth.signOut() + } catch let signOutError as NSError { + return signOutError + } + return nil + } +} + +@objc(MauiFIRAuthUser) +public class MauiFIRAuthUser : NSObject +{ + var _user: User? + + internal init(user: User) { + _user = user; + } + + @objc + public var uid: String? { + get { return _user?.uid } + } + + @objc + public var displayName: String? { + get { return _user?.displayName } + } + + @objc + public var email: String? { + get { return _user?.email } + } + + @objc + public var refreshToken: String? { + get { return _user?.refreshToken } + } + + @objc + public var providerId: String? { + get { return _user?.providerID } + } + + @objc + public var tenantId: String? { + get { return _user?.tenantID } + } + + @objc + public var phoneNumber: String? { + get { return _user?.phoneNumber } + } + + @objc + public var isAnonymous: Bool { + get { return _user?.isAnonymous ?? false } + } + + @objc + public var isEmailVerified: Bool { + get { return _user?.isEmailVerified ?? false } + } + + @objc + public var photoUrl: URL? { + get { return _user?.photoURL } + } +} + + +@objc(MauiFIRAuthResult) +public class MauiFIRAuthResult : NSObject +{ + var _authResult: AuthDataResult? + + internal init(authResult: AuthDataResult) { + _authResult = authResult + } + + @objc + public var user: MauiFIRAuthUser? { + get { + var r: MauiFIRAuthUser? = nil + + let authResultUser = _authResult?.user as User? + + if (authResultUser != nil) { + r = MauiFIRAuthUser(user: authResultUser!) + } + + return r; + } + } +} diff --git a/firebase/macios/sample/AppTabbedPage.xaml b/firebase/macios/sample/AppTabbedPage.xaml index 06ba294..00ce256 100644 --- a/firebase/macios/sample/AppTabbedPage.xaml +++ b/firebase/macios/sample/AppTabbedPage.xaml @@ -9,6 +9,11 @@ + + + + + diff --git a/firebase/macios/sample/AppTabbedPage.xaml.cs b/firebase/macios/sample/AppTabbedPage.xaml.cs index ce34697..df4cadf 100644 --- a/firebase/macios/sample/AppTabbedPage.xaml.cs +++ b/firebase/macios/sample/AppTabbedPage.xaml.cs @@ -11,7 +11,7 @@ public AppTabbedPage() InitializeComponent(); } - public static async Task ConfigureFirebase(ContentPage page) + public static void ConfigureFirebase(ContentPage page) { if (!FirebaseConfigured) { @@ -22,7 +22,7 @@ public static async Task ConfigureFirebase(ContentPage page) } catch (Exception ex) { - await page.DisplayAlert("Unable to configure Firebase app!", ex.ToString(), "OK"); + page.DisplayAlert("Unable to configure Firebase app!", ex.ToString(), "OK"); } } } diff --git a/firebase/macios/sample/FIRAnalyticsPage.xaml.cs b/firebase/macios/sample/FIRAnalyticsPage.xaml.cs index a31b277..b5aaf03 100644 --- a/firebase/macios/sample/FIRAnalyticsPage.xaml.cs +++ b/firebase/macios/sample/FIRAnalyticsPage.xaml.cs @@ -13,7 +13,8 @@ async void OnAnalyticsClicked (object sender, EventArgs e) { try { - await AppTabbedPage.ConfigureFirebase(this); + AppTabbedPage.ConfigureFirebase(this); + MauiFIRAnalytics.LogEvent("OnAnalyticsClicked", new Foundation.NSDictionary("param1", "value1")); var appInstanceId = await MauiFIRAnalytics.GetAppInstanceIdAsync(); await DisplayAlert($"Logged event to app ID {appInstanceId}", "", "OK"); diff --git a/firebase/macios/sample/FIRAuthPage.xaml b/firebase/macios/sample/FIRAuthPage.xaml new file mode 100644 index 0000000..66caced --- /dev/null +++ b/firebase/macios/sample/FIRAuthPage.xaml @@ -0,0 +1,49 @@ + + + + + + +