Apple Sign-In Button with MAUI Embedding in Uno

In Uno Platform projects, implementing a "Sign in with Apple" Button is possible through MAUI embedding (more on which in our Overview). Below is a simple example of how you can add the Apple Sign-In Button to your project. We will inject it into the visual tree from the code-behind and handle the authorization using Apple's Authentication Services.

Sign in with Apple ID

General Usage

  1. Activate the Apple Sign-In Capability:

    • If you are using Visual Studio, open the Entitlements.plist file and locate the "Sign In With Apple" checkbox. After checking it, ensure you save your changes.
    • Alternatively, you can open the Entitlements.plist file in any text editor and add the following within the <dict> tag:
    <key>com.apple.developer.applesignin</key>
    <array>
        <string>Default</string>
    </array>
    
  2. Import Necessary Namespaces:

    using AuthenticationServices;
    using Foundation;
    using UIKit;
    
  3. In the constructor of your Page or control, create the Apple Sign-In Button:

    Important

    Ensure that you wrap platform-specific code in #if __IOS__ directives. For more details, refer to the Platform-specific C# documentation.

    var appleSignInButton = new ASAuthorizationAppleIdButton(ASAuthorizationAppleIdButtonType.Default, ASAuthorizationAppleIdButtonStyle.WhiteOutline);
    appleSignInButton.TouchUpInside += HandleAuthorizationAppleIDButtonPress;
    appleSignInButton.CornerRadius = 50;
    
    // Retain the delegate to prevent garbage collection
    _appleSignInDelegate = new AuthorizationControllerDelegate(this);
    
    Note

    It's important to retain a reference to the delegate (_appleSignInDelegate) to avoid garbage collection issues. This ensures that the authorization process is completed without interruption.

  4. Inject the Apple Sign-In button into the visual tree using VisualTreeHelper.AdaptNative:

    var adaptedAppleButton = VisualTreeHelper.AdaptNative(appleSignInButton);
    var borderWrapper = new Border
    {
        MinHeight = 50,
        MinWidth = 250,
        HorizontalAlignment = HorizontalAlignment.Center,
        Child = adaptedAppleButton,
    };
    
    m_MainStackPanel.Children.Add(borderWrapper);
    
  5. Handle the button's TouchUpInside event to initiate Apple Sign-In:

    private void HandleAuthorizationAppleIDButtonPress(object sender, EventArgs e)
    {
        var appleIDProvider = new ASAuthorizationAppleIdProvider();
        var request = appleIDProvider.CreateRequest();
        request.RequestedScopes = new[] { ASAuthorizationScope.FullName, ASAuthorizationScope.Email };
    
        var authorizationController = new ASAuthorizationController(new ASAuthorizationRequest[] { request });
        authorizationController.Delegate = _appleSignInDelegate;  // Use the retained delegate
        authorizationController.PresentationContextProvider = new PresentationContextProvider();  // Set the presentation context provider
        authorizationController.PerformRequests();
    }
    

Custom Delegate for Authorization

To handle the result of the sign-in process, create a custom delegate:

public class AuthorizationControllerDelegate : ASAuthorizationControllerDelegate
{
    private readonly MyUserControl _parent;

    public AuthorizationControllerDelegate(MyUserControl parent)
    {
        _parent = parent;
    }

    public override void DidComplete(ASAuthorizationController controller, ASAuthorization authorization)
    {
        System.Diagnostics.Debug.WriteLine("Authorization successful.");

        try
        {
            var appleIdCredential = authorization.GetCredential<ASAuthorizationAppleIdCredential>();

            var userIdentifier = appleIdCredential?.User;
            // Handle successful authorization, retrieve user details
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"Authorization failed: {ex.Message}");
        }
    }
}

Managing Delegate in MVVM

If you're using an MVVM architecture, the delegate should handle sign-in logic indirectly by passing the necessary data to the ViewModel. This keeps the code clean and follows MVVM principles. For example:

  • Instead of passing the user control (MyUserControl) to the AuthorizationControllerDelegate, pass a reference to the ViewModel or a dedicated service.
  • In the delegate, you can call methods on the ViewModel to handle the authorization flow, such as storing user information or navigating to another page.

Example:

public class AuthorizationControllerDelegate : ASAuthorizationControllerDelegate
{
    private readonly MyViewModel _viewModel;

    public AuthorizationControllerDelegate(MyViewModel viewModel)
    {
        _viewModel = viewModel;
    }

    public override void DidComplete(ASAuthorizationController controller, ASAuthorization authorization)
    {
        System.Diagnostics.Debug.WriteLine("Authorization successful.");

        try
        {
            var appleIdCredential = authorization.GetCredential<ASAuthorizationAppleIdCredential>();
            var userIdentifier = appleIdCredential?.User;

            // Pass the user info to the ViewModel for further processing
            _viewModel.HandleSuccessfulAuthorization(userIdentifier);
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"Authorization failed: {ex.Message}");
        }
    }
}

Presentation Context Provider

For proper handling of the presentation context on iOS, implement a PresentationContextProvider:

public class PresentationContextProvider : NSObject, IASAuthorizationControllerPresentationContextProviding
{
    public UIWindow GetPresentationAnchor(ASAuthorizationController controller)
    {
        return UIApplication.SharedApplication.KeyWindow;
    }
}

More Resources

For more details on platform-specific considerations or to improve the user experience: