diff --git a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/AuthenticationViewController.cs b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/AuthenticationViewController.cs index e06c1c8e..a1c909c3 100644 --- a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/AuthenticationViewController.cs +++ b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/AuthenticationViewController.cs @@ -1,7 +1,6 @@ using LocalAuthentication; namespace StoryboardTable; - /* * https://developer.apple.com/ios/human-interface-guidelines/user-interaction/authentication/ * @@ -19,137 +18,102 @@ namespace StoryboardTable; /// partial class AuthenticationViewController : UIViewController { - LAContextReplyHandler replyHandler; + LAContextReplyHandler? replyHandler; /// String to use for display - string BiometryType = ""; + string BiometryType = string.Empty; - public AuthenticationViewController(IntPtr handle) : base(handle) + public AuthenticationViewController (IntPtr handle) : base (handle) { } - public override void ViewWillAppear(bool animated) + public override void ViewWillAppear (bool animated) { - base.ViewWillAppear(animated); + base.ViewWillAppear (animated); // bind every time, to reflect deletion in the Detail view - unAuthenticatedLabel.Text = ""; + unAuthenticatedLabel.Text = string.Empty; - var context = new LAContext(); - var buttonText = ""; - if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var authError1)) - { // has Biometrics (Touch or Face) - if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0)) - { + var context = new LAContext (); + var buttonText = string.Empty; + if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var authError1)) { // has Biometrics (Touch or Face) + if (UIDevice.CurrentDevice.CheckSystemVersion (11, 0)) { context.LocalizedReason = "Authorize for access to secrets"; // iOS 11 BiometryType = context.BiometryType == LABiometryType.TouchId ? "Touch ID" : "Face ID"; buttonText = $"Login with {BiometryType}"; - } - else - { // no FaceID before iOS 11 + } else { // no FaceID before iOS 11 buttonText = $"Login with Touch ID"; } - } - else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out var authError2)) - { + } else if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthentication, out var authError2)) { buttonText = $"Login"; // with device PIN BiometryType = "Device PIN"; - } - else - { + } else { // Application might choose to implement a custom username/password buttonText = "Use unsecured"; BiometryType = "none"; } - AuthenticateButton.SetTitle(buttonText, UIControlState.Normal); + AuthenticateButton.SetTitle (buttonText, UIControlState.Normal); } - partial void AuthenticateMe(UIButton sender) + partial void AuthenticateMe (UIButton sender) { - var context = new LAContext(); + var context = new LAContext (); NSError AuthError; - var localizedReason = new NSString("To access secrets"); + var localizedReason = new NSString ("To access secrets"); // because LocalAuthentication APIs have been extended over time, need to check iOS version before setting some properties context.LocalizedFallbackTitle = "Fallback"; // iOS 8 - if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0)) - { + if (UIDevice.CurrentDevice.CheckSystemVersion (10, 0)) { context.LocalizedCancelTitle = "Cancel"; // iOS 10 } - if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0)) - { + if (UIDevice.CurrentDevice.CheckSystemVersion (11, 0)) { context.LocalizedReason = "Authorize for access to secrets"; // iOS 11 BiometryType = context.BiometryType == LABiometryType.TouchId ? "TouchID" : "FaceID"; } //Use canEvaluatePolicy method to test if device is TouchID or FaceID enabled //Use the LocalAuthentication Policy DeviceOwnerAuthenticationWithBiometrics - if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError)) - { - Console.WriteLine("TouchID/FaceID available/enrolled"); - replyHandler = new LAContextReplyHandler((success, error) => - { + if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError)) { + replyHandler = new LAContextReplyHandler ( (success, error) => { //Make sure it runs on MainThread, not in Background - this.InvokeOnMainThread(() => - { - if (success) - { - Console.WriteLine($"You logged in with {BiometryType}!"); - - PerformSegue("AuthenticationSegue", this); - } - else - { - Console.WriteLine(error.LocalizedDescription); + this.InvokeOnMainThread ( () => { + if (success) { + PerformSegue ("AuthenticationSegue", this); + } else { //Show fallback mechanism here unAuthenticatedLabel.Text = $"{BiometryType} Authentication Failed"; - //AuthenticateButton.Hidden = true; } }); - }); //Use evaluatePolicy to start authentication operation and show the UI as an Alert view //Use the LocalAuthentication Policy DeviceOwnerAuthenticationWithBiometrics - context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason, replyHandler); + context.EvaluatePolicy (LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason, replyHandler); } - else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out AuthError)) - { - Console.WriteLine("When TouchID/FaceID aren't available or enrolled, use the device PIN"); - replyHandler = new LAContextReplyHandler((success, error) => - { + else if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthentication, out AuthError)) { + replyHandler = new LAContextReplyHandler ( (success, error) => { //Make sure it runs on MainThread, not in Background - this.InvokeOnMainThread(() => - { - if (success) - { - Console.WriteLine($"You logged in with {BiometryType}!"); - - PerformSegue("AuthenticationSegue", this); - } - else - { - Console.WriteLine(error.LocalizedDescription); + this.InvokeOnMainThread ( () => { + if (success) { + PerformSegue ("AuthenticationSegue", this); + } else { //Show fallback mechanism here unAuthenticatedLabel.Text = "Device PIN Authentication Failed"; AuthenticateButton.Hidden = true; } }); - }); //Use evaluatePolicy to start authentication operation and show the UI as an Alert view //Use the LocalAuthentication Policy DeviceOwnerAuthenticationWithBiometrics - context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, localizedReason, replyHandler); - } - else - { + context.EvaluatePolicy (LAPolicy.DeviceOwnerAuthentication, localizedReason, replyHandler); + } else { // User hasn't configured a PIN or any biometric auth. // App may implement its own login, or choose to allow open access unAuthenticatedLabel.Text = "No device auth configured"; - var okCancelAlertController = UIAlertController.Create("No authentication", "This device does't have authentication configured.", UIAlertControllerStyle.Alert); - okCancelAlertController.AddAction(UIAlertAction.Create("Use unsecured", UIAlertActionStyle.Default, alert => PerformSegue("AuthenticationSegue", this))); - okCancelAlertController.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine("Cancel was clicked"))); - PresentViewController(okCancelAlertController, true, null); + var okCancelAlertController = UIAlertController.Create ("No authentication", "This device does't have authentication configured.", UIAlertControllerStyle.Alert); + okCancelAlertController.AddAction(UIAlertAction.Create ("Use unsecured", UIAlertActionStyle.Default, alert => PerformSegue ("AuthenticationSegue", this))); + okCancelAlertController.AddAction(UIAlertAction.Create ("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine ("Cancel was clicked"))); + PresentViewController (okCancelAlertController, true, null); } } } diff --git a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/DetailViewController.cs b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/DetailViewController.cs index fd436caa..9e5f2b4f 100644 --- a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/DetailViewController.cs +++ b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/DetailViewController.cs @@ -2,7 +2,7 @@ public partial class DetailViewController : UIViewController { - object detailItem; + object? detailItem; public DetailViewController (IntPtr handle) : base (handle) { @@ -21,7 +21,7 @@ public partial class DetailViewController : UIViewController void ConfigureView () { // Update the user interface for the detail item - if (IsViewLoaded && detailItem != null) + if (IsViewLoaded && detailItem is not null) detailDescriptionLabel.Text = detailItem.ToString (); } diff --git a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/GlobalSuppressions.cs b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/GlobalSuppressions.cs new file mode 100644 index 00000000..5b3e7a67 --- /dev/null +++ b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:StoryboardTable.SecretTableSource.GetCell(UIKit.UITableView,Foundation.NSIndexPath)~UIKit.UITableViewCell")] diff --git a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/MasterViewController.cs b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/MasterViewController.cs index 87ca58fd..fc4d0278 100755 --- a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/MasterViewController.cs +++ b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/MasterViewController.cs @@ -12,6 +12,6 @@ public partial class MasterViewController : UITableViewController base.ViewWillAppear (animated); // bind every time, to reflect deletion in the Detail view - TableView.Source = new SecretTableSource(); + TableView.Source = new SecretTableSource (); } } diff --git a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretItem.cs b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretItem.cs index 8ec038c6..fc2f1cd0 100644 --- a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretItem.cs +++ b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretItem.cs @@ -3,7 +3,7 @@ namespace StoryboardTable; /// /// Represents a Chore. /// -/// +/// public class SecretItem { public SecretItem () @@ -11,7 +11,7 @@ public class SecretItem { } public int Id { get; set; } - public string Name { get; set; } - public string Notes { get; set; } + public string Name { get; set; } = string.Empty; + public string Notes { get; set; } = string.Empty; public bool Done { get; set; } } diff --git a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretTableSource.cs b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretTableSource.cs index ceaf1aed..fe02df78 100644 --- a/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretTableSource.cs +++ b/dotnet/ios/ios11/FaceIDSample/LocalAuthentication/SecretTableSource.cs @@ -9,25 +9,35 @@ public class SecretTableSource : UITableViewSource { public SecretTableSource () { tableItems = new List { - new SecretItem() {Name="Gift list", Notes="iPhone X, Apple Watch, HomePod", Done=false}, - new SecretItem() {Name="Reminders", Notes="buy flowers", Done=false} - }.ToArray(); + new SecretItem () {Name="Gift list", Notes="iPhone X, Apple Watch, HomePod", Done=false}, + new SecretItem () {Name="Reminders", Notes="buy flowers", Done=false} + }.ToArray (); } public override nint RowsInSection (UITableView tableview, nint section) - { - return tableItems.Length; - } + => tableItems.Length; + public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath) { // in a Storyboard, Dequeue will ALWAYS return a cell, UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier); // now set the properties as normal - cell.TextLabel.Text = tableItems[indexPath.Row].Name; - cell.DetailTextLabel.Text = tableItems[indexPath.Row].Notes; + + // Beginning in iOS 14, UITableViewCell has to use cell.ContentConfiguration + // to add text + if (UIDevice.CurrentDevice.CheckSystemVersion(14, 0)) { + var content = cell.DefaultContentConfiguration; + content.Text = tableItems[indexPath.Row].Name; + content.SecondaryText = tableItems[indexPath.Row].Notes; + cell.ContentConfiguration = content; + } else { + cell.TextLabel.Text = tableItems[indexPath.Row].Name; + cell.DetailTextLabel.Text = tableItems[indexPath.Row].Notes; + } + return cell; } - public override void RowSelected(UITableView tableView, Foundation.NSIndexPath indexPath) + public override void RowSelected (UITableView tableView, Foundation.NSIndexPath indexPath) { - tableView.DeselectRow(indexPath, true); + tableView.DeselectRow (indexPath, true); } }