Search code examples
xamarinmvvmcrossazure-mobile-services

Xamarin + MvvmCross + Azure Mobile App table request not working


I've been building a simple Xamarin app using MvvmCross framework together with Azure Mobile App service to store and retrieve data from the cloud. UI projects are platform specific.

The main view of my app shows a list of posts and this list is supposed to be retrieved from the Azure cloud. What happens is that after the splash screen, the app just shows a black screen. The call to retrieve the data seems to be fired correctly but the response is never received.

This is my posts view model:

public class PostsViewModel : MvxViewModel
    {
        readonly IPostService _postService;
        readonly IMvxNavigationService _navigationService;
        readonly MvxSubscriptionToken token;

        public PostsViewModel(IPostService postService, IMvxMessenger messenger, IMvxNavigationService navigationService)
        {
            _postService = postService;
            _navigationService = navigationService;

            token = messenger.SubscribeOnMainThread<PostsChangedMessage>
                (async mex => await LoadPosts());

            Posts = new ObservableCollection<PostViewModel>();
            AddNewPostCommand = new MvxAsyncCommand(AddNewPost);
            ShowPostDetailsCommand = new MvxAsyncCommand<PostViewModel>(ShowPostDetails);
        }

        #region Properties
        public ObservableCollection<PostViewModel> Posts { get; set; }

        private bool isLoading = true;
        public bool IsLoading
        {
            get => isLoading;
            set => SetProperty(ref isLoading, value);
        }
        #endregion

        #region Commands
        public IMvxAsyncCommand AddNewPostCommand { get; }
        private async Task AddNewPost()
        {
            await _navigationService.Navigate(typeof(PostViewModel), new Post());
        }

        public IMvxAsyncCommand<PostViewModel> ShowPostDetailsCommand { get; }
        private async Task ShowPostDetails(PostViewModel postViewModel)
        {
            await _navigationService.Navigate(typeof(PostDetailsViewModel), postViewModel);
        }
        #endregion

        #region Functions
        public override async Task Initialize()
        {
            await LoadPosts();
        }

        private async Task LoadPosts()
        {
            // Remove all counters before reloading
            Posts.Clear();

            var posts = await _postService.GetAllPosts();
            foreach(var post in posts)
            {
                var viewModel = new PostViewModel(_postService, _navigationService);
                viewModel.Prepare(post);
                Posts.Add(viewModel);
            }

            IsLoading = false;
        }
        #endregion
    }

This is my post service:

    public class PostService : IPostService
    {
        readonly IPostRepository _repository;
        readonly IMvxMessenger _messenger;

    public PostService(IPostRepository repository, IMvxMessenger messenger)
    {
        _repository = repository;
        _messenger = messenger;
    }

    public async Task<Post> AddNewPost(Post post)
    {
        // Just for testing purposes
        post.Date = DateTimeOffset.Now;
        // Temporary hard-coded author; this will be get from the user object 
        post.Author = "John Smith";

        await _repository.Save(post).ConfigureAwait(false);
        _messenger.Publish(new PostsChangedMessage(this));

        return post;
    }

    public async Task DeletePost(Post post)
    {
        await _repository.Delete(post).ConfigureAwait(false);
        _messenger.Publish(new PostsChangedMessage(this));
    }

    public Task<List<Post>> GetAllPosts()
    {
        return _repository.GetAll();
    }

This is my repository:

public class PostRepository : IPostRepository
{
    MobileServiceClient MobileService
        = new MobileServiceClient(@"https://vntestapp.azurewebsites.net", new LoggingHandler(true));

    IMobileServiceTable<Post> postTable;

public PostRepository()
{
    postTable = MobileService.GetTable<Post>();
}

public Task Delete(Post post)
{
    return postTable.DeleteAsync(post);
}

public async Task<List<Post>> GetAll()
{
    return await postTable.ToListAsync();
}

public async Task<Post> Save(Post post)
{
    await postTable.InsertAsync(post);
    return post;
}

}


Solution

  • Use Task.Run() construct for the API call if the running platform is android.

    From my practical experience of working with Mvvmcross, in android sometimes, awaited call inside async method blocks that thread and it never returns,

    In that case, if you call it inside Task.Run() it just works fine. While iOS doesn't have that problem.

    I would look for the deeper explanation to provide the reason for it.