Xamarin - Other

How do you connect localhost in the emulator or physical device ?

Application (Android)

AndroidManifest.xml

<application
    android:networkSecurityConfig="@xml/network_security_config"
</application>


network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
	<domain-config cleartextTrafficPermitted="true">
		<domain includeSubdomains="true">IP address</domain>
	</domain-config>
</network-security-config>


Android Studio Emulator

  1. Open emulator.
  2. Click on to open Extended Controls.
  3. Go to Settings > Proxy.
  4. Uncheck Use Android Studio HTTP proxy settings.
  5. Choose Manual proxy configuration.
  6. Key in your IP address as host name and the port your backend is running on as port. (e.g. 192.168.1.86:8080)
  7. Key in the proxy authentication if enabled.
  8. Click Apply.


Physical Device (Android)

  1. Open your Android’s Settings.
  2. Tap Wi-Fi.
  3. Tap and hold the Wi-Fi Network Name.
  4. Select Modify Network.
  5. Click Advanced Options.
  6. Tap Manual.
  7. Change your proxy’s settings. Enter the hostname and proxy port. (e.g. 192.168.1.86:8080)
  8. Tap Save.


Physical Device (iPhone)

  1. Open your iPhone settings.
  2. Tap Wi-Fi.
  3. Select the info icon on the right side.
  4. Scroll down to the HTTP Proxy Configure Proxy.
  5. Tap Manual.
  6. Edit your proxy settings. (e.g. 192.168.1.86:8080)
  7. Turn on authentication if network IP is not whitelisted in the dashboard and add your Username and Password.
  8. Tap back to Wi-Fi selection window. This will save your proxy settings.


How to know the current OS / platform of the executing code (Android / iOS)

if(Device.RuntimePlatform == Device.iOS)
{
  //iOS stuff
}
else if(Device.RuntimePlatform == Device.Android)
{
  //Android stuff
}
else{
  //Other stuff
}

Invoke Method

BeginInvokeOnMainThread - async

InvokeOnMainThread - sync

iOS XCode Launch Screen Storyboard not displaying

  1. Open storyboard with XCode.
  2. Click on the Storyboard.
  3. Under Show the File inspector section, checked the Use as Launch Screen.
  4. Under Show the Attributes inspector section, checked the Is Initial View Controller.
  5. Save.

JDK path

Window

C:\Program Files\Java\jdk1


MacOS

/Library/Java/JavaVirtualMachines

Open App-Settings in Xamarin Forms

.NET standard application

using System; 

namespace MyApp.Helpers 
{ 
  public interface IAppSettingsHelper 
  { 
    void OpenAppSettings(); 
  } 
}


Android App

using System;
using Android.Content;
using MyApp.Droid;
using MyApp.Helpers;
using Xamarin.Forms;
using Application = Android.App.Application;

[assembly: Dependency(typeof(AppSettingsInterface))]
namespace MyApp.Droid
{
  public class AppSettingsInterface : IAppSettingsHelper
  {
    public void OpenAppSettings()
    {
      var intent = new Intent(Android.Provider.Settings.ActionApplicationDetailsSettings);
      intent.AddFlags(ActivityFlags.NewTask);
      string package_name = "my.android.package.name";
      var uri = Android.Net.Uri.FromParts("package", package_name, null);
      intent.SetData(uri);
      Application.Context.StartActivity(intent);
    }
  }
}


iOS App

using System;
using MyApp.Helpers;
using MyApp.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;

[assembly: Dependency(typeof(AppSettingsInterface))]
namespace MyApp.iOS
{
  public class AppSettingsInterface : IAppSettingsHelper
  {
    public void OpenAppSettings()
    {
      var url = new NSUrl($"app-settings:");
      UIApplication.SharedApplication.OpenUrl(url);
    }
  }
}


Permission Error Codes & Logs

Error Code 0 : Unknown

The permission hasn't been granted or requested and is in an unknown state.


Error Code 1 : Denied

The user has denied the permission.


Error Code 2 : Disabled

The permission is disabled for the app.


Error Code 3 : Granted

The user has granted permission.


Error Code 4 : ERROR_CODE_NO_FILL

The permission is in a restricted state.

Registering A Uri Scheme

WinRT / UWP

Go to your package.appxmanifest and go to declarations. Add a new protocol and in the Name: section add in your uri scheme.


iOS

Open up your info.pList in an XML editor and add in the following key and values, replacing my-app with your Uri Scheme.

<key>CFBundleURLTypes<key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>myapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>my-app</string>
    </array>
  </dict>
</array>


Android

Open up your AndroidManifest.xml and add in an intent as below, replacing my-app with your Uri Scheme.

<application android:label="My App" android:icon="@drawable/icon">
  <activity android:icon="@drawable/icon" android:label="My App" android:name="mobile">
    <intent-filter>
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="my-app" />
    </intent-filter>
  </activity>
</application>


Pass through parameters

Along with the Uri Scheme you can also pass through parameters or more information. Much like a traditional Uri which contains a domain name then some form of identifier and query string parameters, you can do much the same.


If you do pass through parameters like so, how do you get hold of them in the app?


twitter://compose?text=Hello


There is a place in each platform that allows you to retrieve the value.


WinRT / UWP

In your App.xaml.cs in your native WinRT / UWP app, override the following.

protected override void OnActivated(IActivatedEventArgs args)
{
     if (args.Kind == ActivationKind.Protocol)
     {
         ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs;
         var url = eventArgs.Uri.AbsoluteUri;
     }
     base.OnActivated(args);
}


iOS

In your AppDelegate.cs you just need to override OpenUrl.

public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{

}


Android

In the MainActivity.cs file, or the activity that will be loaded when the app starts, in your OnCreate, you can capture the data as shown below.

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    var data = Intent?.Data?.EncodedAuthority;
}


Problems with Uri Scheme

Uri Scheme is an older concept and is not actively promoted as the best way forward today. UWP unfortunately as no other way, however iOS has Universal Links and Android has App Links. While you are still free to use Uri Scheme there are some challenges you may face.


  1. You are not guaranteed the Uri Scheme you choose. Another app can be installed after yours and take over your Uri Scheme. Uri Schemes are registered on who was the last to take the name.
  2. iOS Apps can no longer tell if an app is installed via a Uri Scheme unless the Uri Scheme is listed in the info.pList.
  3. It doesn’t have a fallback if the app isn’t installed, whereas other options such as universal links have a website fallback if the app is not installed.

SQLite Database with Your Apps

Create a DataStore that lets you initialize and configure your SQLite db and its tables.

public sealed class SqliteDataStore
{
    private readonly SQLiteAsyncConnection database;
    private static Lazy<SqliteDataStore> lazy = null;

    private SqliteDataStore(string path)
    {
        database = new SQLiteAsyncConnection(path);
    }

    public SQLiteAsyncConnection Database => database;

    public static SqliteDataStore Instance
    {
        get
        {
            if (lazy is null)
            {
                throw new InstanceNotCreatedException();
            }
            return lazy.Value;
        }
    }

    private static void CreateSharedDataStore(string path)
    {
        lazy ??= new Lazy<SqliteDataStore>(() => new SqliteDataStore(path));
    }

    public static async Task InitializeDataStoreAsync()
    {
        var databasePath = Path.Combine(
                        Environment.GetFolderPath(
                        Environment.SpecialFolder.LocalApplicationData),
                        DataStoreConstants.DatabaseName
                        );
        CreateSharedDataStore(databasePath);
        await CreateAllTablesAsync();
    }

    private static async Task CreateAllTablesAsync()
    {
        await SqliteDataStore.Instance.Database.CreateTableAsync<GroceryModel>();
    }
}


Create a Constants class that will hold any constants you need for your Database.

public static class DataStoreConstants
{
    public static readonly string DatabaseName = "sqlite.db3";
}


Create a custom exception in case the lazy initialization is failing because of some usage issues. (You can add more)

public class InstanceNotCreatedException : Exception
{
    public InstanceNotCreatedException() : base()
    {
    }


    public InstanceNotCreatedException(string message) : base(message)
    {
    }


    public InstanceNotCreatedException(string message, Exception innerException) : base(message, innerException)
    {
    }
}


Create an interface that lets you access your DataStore and implement the most commonly used SQLite methods.

using System.Collections;
using System.Linq.Expressions;


public interface ISqliteService
{
    Task<bool> ExecuteAsync(string query, params object[] args);


    Task<bool> InsertDataAsync<T>(T Data);


    Task<bool> DeleteDataAsync<T>(object primaryKey);


    Task<bool> UpdateDataAsync<T>(T Data);


    Task<bool> InsertAllDataAsync<T>(IEnumerable<T> Data);


    Task<bool> InsertOrReplace<T>(T Data);


    Task<bool> DeleteAllDataAsync<T>();


    Task<bool> UpdateAllDataAsync(IEnumerable Data);


    Task<List<T>> GetAllDataAsync<T>() where T : new();


    Task<T> GetDataByPkAsync<T>(object pk) where T : new();


    Task<T> GetDataAsync<T>(Expression<Func<T, bool>> predicate) where T : new();


    Task<List<T>> GetAllDataQueryAsync<T>(Expression<Func<T, bool>> predicate = null) where T : new();
}


Create an implementation of this interface which you can then use through your dependency injection service.

public class SqliteService : ISqliteService
{
    // <summary>
    // Deletes all the data from a specific table
    // </summary>
    // <typeparam name="T"> Table that you'd like to clear </typeparam>
    // <returns> a boolean that represents if all the data was deleted or not </returns>
    public async Task<bool> DeleteAllDataAsync<T>()
    {
        return await SqliteDataStore.Instance.Database.DeleteAllAsync<T>() != 0;
    }

    // <summary>
    // Deletes the specific row whos primary key is passed as an argument
    // </summary>
    // <typeparam name="T"> Table that you'd like to delete data from </typeparam>
    // <param name="primaryKey"> primary key of that row entry </param>
    // <returns> a boolean that represents if the deletion was sucessful </returns>
    public async Task<bool> DeleteDataAsync<T>(object primaryKey)
    {
        return await SqliteDataStore.Instance.Database.DeleteAsync<T>(primaryKey) != 0;
    }

    // <summary>
    // Creates an SQLiteCommand of the given command text(SQL) with arguments
    // </summary>
    // <param name="query">the SQLite command you'd like to fire</param>
    // <param name="args">required arguments for that command</param>
    // <returns></returns>
    public async Task<bool> ExecuteAsync(string query, params object[] args)
    {
        return await SqliteDataStore.Instance.Database.ExecuteAsync(query, args) != 0;
    }

    // <summary>
    // Get all data of type
    // </summary>
    // <typeparam name="T"> Table that you want to query </typeparam>
    // <returns> list of your data if exists or an empty collection </returns>
    public async Task<List<T>> GetAllDataAsync<T>() where T : new()
    {
        return await SqliteDataStore.Instance.Database.Table<T>().ToListAsync();
    }

    // <summary>
    // Get data that matches the pk
    // </summary>
    // <typeparam name="T"> Table that you want to query </typeparam>
    // <returns> data if exists or null </returns>
    public async Task<T> GetDataByPkAsync<T>(object pk) where T : new()
    {
        return await SqliteDataStore.Instance.Database.GetAsync<T>(pk);
    }

    // <summary>
    // Get the first item that macthes the expression.
    // </summary>
    // <typeparam name="T"> Table that you want to query </typeparam>
    // <returns> data if exists or null </returns>
    public async Task<T> GetDataAsync<T>(Expression<Func<T, bool>> predicate) where T : new()
    {
        try
        {
            return await SqliteDataStore.Instance.Database.GetAsync<T>(predicate);
        }
        catch (Exception)
        {
            //Todo: Log this exception.
            return default;
        }
    }

    // <summary>
    // Insert all data into the specific table
    // </summary>
    // <typeparam name="T"> Table that you'd like to insert into </typeparam>
    // <param name="Data"> Data to insert </param>
    // <returns> a boolean that represents if the data was inserted successfully </returns>
    public async Task<bool> InsertAllDataAsync<T>(IEnumerable<T> Data)
    {
        return await SqliteDataStore.Instance.Database.InsertAllAsync(Data, typeof(T), true) != 0;
    }

    // <summary>
    // Insert single row into a specific table
    // </summary>
    // <typeparam name="T"> Table that you'd like to insert into </typeparam>
    // <param name="Data"> Data to insert </param>
    // <returns>
    // a boolean, integer tuple. The boolean represents if the transaction was successful and
    // the integer returns the integer autoincrement primary key if any.
    // </returns>
    public async Task<bool> InsertDataAsync<T>(T Data)
    {
        return await SqliteDataStore.Instance.Database.InsertAsync(Data, typeof(T)) != 0;
    }

    // <summary>
    // Insert single row into a specific table
    // </summary>
    // <typeparam name="T"> Table that you'd like to insert/replace into </typeparam>
    // <param name="Data"> Data to insert </param>
    // <returns>
    // a boolean, integer tuple. The boolean represents if the transaction was successful and
    // the integer returns the integer autoincrement primary key if any.
    // </returns>
    public async Task<bool> InsertOrReplace<T>(T Data)
    {
        return await SqliteDataStore.Instance.Database.InsertOrReplaceAsync(Data, typeof(T)) != 0;
    }

    // <summary>
    // Updates all specified objects
    // </summary>
    // <param name="Data"> </param>
    // <returns> a boolean that represents if the update was successful </returns>
    public async Task<bool> UpdateAllDataAsync(IEnumerable Data)
    {
        return await SqliteDataStore.Instance.Database.UpdateAllAsync(Data, true) != 0;
    }

    // <summary>
    // Updates the specified object
    // </summary>
    // <typeparam name="T"> Table that you'd like to update </typeparam>
    // <param name="Data"> Data you'd like to update </param>
    // <returns> a boolean that represents if the update was successful </returns>
    public async Task<bool> UpdateDataAsync<T>(T Data)
    {
        return await SqliteDataStore.Instance.Database.UpdateAsync(Data, typeof(T)) != 0;
    }

    // <summary>
    // Get all data of type with a predicate LINQ query
    // </summary>
    // <typeparam name="T"> Table that you want to query </typeparam>
    // <typeparam name="predicate"> LINQ predicate query </typeparam>
    // <returns>
    // list of your data if exists or an empty collection based on the result of predicate search
    // </returns>

    public async Task<List<T>> GetAllDataQueryAsync<T>(Expression<Func<T, bool>> predicate = null) where T : new()
    {
        return await SqliteDataStore.Instance.Database.Table<T>().Where(predicate).ToListAsync();
    }
}


Initialize your SQLite DB, in your App class before setting the MainPage. (Especially if your default MainPage uses SQLite)

App()
{      
    InitializeComponent();
    SqliteDataStore.InitializeDataStoreAsync().SafeFireAndForget();
    MainPage = new MainPage();
}


If your MainPage does not use SQLite, then I recommend you instead Initialize it in the OnStart method.

protected override async void OnStart()
{
    base.OnStart();
    await SqliteDataStore.InitializeDatabaseAsync();
}


Example GroceryModel

public class GroceryModel
{
    [PrimaryKey,AutoIncrement]
    public int Id { get; set; }

    public string? Name { get; set; }

    [OneToMany(CascadeOperations = CascadeOperation.All)]
    public List<string> OneToManyAll { get; set; }
}

System.Net.WebException: Error: TrustFailure (Authentication failed, see inner exception.)

Uri uri = new UriBuilder(url).Uri;
WebClient webClient = new WebClient();
ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback
(
  delegate { return true; }
);    
webClient.DownloadFileAsync(uri);

Visual Studio for Mac - /usr/bin/codesign failed with exit code 1

Check whether the provisioning profile is selected or not.

Visual Studio for Mac - No SDK found at specified location

  1. In Visual Studio for Mac, navigate to Preferences > Projects > SDK Locations > Android.
  2. In the Android page, click the Locations tab to view and set the SDK location to ~/Library/Developer/Xamarin/android-sdk-macosx.
  3. Click OK and restart Visual Studio.