0

I am following the official Xamarin guide for cross-platform file handling using .net-standard-2.0 PCL project. The App is intended to build for Android and Windows.

It raises me a question here. The interface code is as below.

public interface ISaveAndLoad {
    void SaveText (string filename, string text);
    string LoadText (string filename);
}

However, the code given for UWP is written as

[assembly: Dependency(typeof(SaveAndLoad_WinApp))]
namespace WindowsApp
{
    // https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758325.aspx
    public class SaveAndLoad_WinApp : ISaveAndLoad
    {
        public async Task SaveTextAsync(string filename, string text)
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            StorageFile sampleFile = await localFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
            await FileIO.WriteTextAsync(sampleFile, text);
        }
        public async Task<string> LoadTextAsync(string filename)
        {
            StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
            StorageFile sampleFile = await storageFolder.GetFileAsync(filename);
            string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
            return text;
        }
    }
}

The code will not compile as SaveAndLoad_WinApp does not implement the interface ISaveAndLoad.

I know that I can change the interface to Task and Task<string>, so it will compile.

However the problems goes back to Android. UWP is using this Windows.Storage.FileIO thing which does not exists on Android. While Android is using System.IO, it does not have a Async method that can be awaited. That means I cannot share the interface as UWP require async Task<string> but Android only returns a string instead of a Task.

If I am not using async in the UWP method, FileIO won't work vice versa. async void for SaveText works fine as the Interface states void. However, it won't allow me to make async string for LoadText as async method must return a Task<T>, Task or void.

It seems that the way to go will be implement async on Android too while letting the code run synchronous. However, I feel like I have done something wrong as the Xamarin sample is showing that it should work.

Am I misunderstanding anything here or missing an implementation? Or it is actually wrong in the Xamarin documentation?


UPDATE #1 I have put an async void for SaveText and realize that there's a problem that the thread is not awaited. That it fast forwarded to the next function. So I actually have to await the function in the PCL for UWP. However the implementation on Android is a void that cannot be awaited. So it is actually causing an Error... Is there any suggestion on how to implement this scenario?

Here is the code I put in my PCL

public static void Save(){
    DependencyService.Get<ISaveAndLoad>().SaveText("temp.txt", input.Text);
}
Eric Lam
  • 329
  • 1
  • 5
  • 13

1 Answers1

0

The code will not compile as SaveAndLoad_WinApp does not implement the interface ISaveAndLoad.

You could download the document sample WorkingWithFiles, you could find it here. In this sample the ISaveAndLoad interface has a little difference :

public interface ISaveAndLoad
{
    Task SaveTextAsync (string filename, string text);
    Task<string> LoadTextAsync (string filename);
    bool FileExists (string filename);
}

UWP is using this Windows.Storage.FileIO thing which does not exists on Android. While Android is using System.IO, it does not have a Async method that can be awaited.

You could refer to SaveAndLoad_Android.cs class in the sample project, use StreamWriter.WriteAsync() method :

public class SaveAndLoad_Android : ISaveAndLoad
{
    #region ISaveAndLoad implementation

    public async Task SaveTextAsync (string filename, string text)
    {
        var path = CreatePathToFile (filename);
        using (StreamWriter sw = File.CreateText (path))
            await sw.WriteAsync(text);
    }

    public async Task<string> LoadTextAsync (string filename)
    {
        var path = CreatePathToFile (filename);
        using (StreamReader sr = File.OpenText(path))
            return await sr.ReadToEndAsync();
    }

    public bool FileExists(string filename)
    {
        return File.Exists(CreatePathToFile(filename));
    }

    #endregion

    string CreatePathToFile(string filename)
    {
        var docsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        return Path.Combine(docsPath, filename);
    }
}

Then in your PCL, use SaveTextAsync method like this :

var fileService = DependencyService.Get<ISaveAndLoad> ();
...
saveButton.Clicked += async (sender, e) => 
{
    loadButton.IsEnabled = saveButton.IsEnabled = false;
    // uses the Interface defined in this project, and the implementations that must
    // be written in the iOS, Android and WinPhone app projects to do the actual file manipulation

    await fileService.SaveTextAsync (fileName, input.Text);
    loadButton.IsEnabled = saveButton.IsEnabled = true;
};
York Shen
  • 9,014
  • 1
  • 16
  • 40