10

I am building cross platform apps with Xamarin and MvvmCross. I need to call the server to the updates every minute (I will move to push notifications later) but I am unable to make a timer in my Core Project. I've seen MvvmCross N+42 but I believe the target projects are older which allows the timer. Below is my target framework.

Is there a better way for me to constantly call a method which calls a service?

  • .NET Framework 4.5 and Higher
  • Windows Store apps (Windows 8) and higher
  • Windows Phone 8
  • Xamarin.Android
  • Xamarin.iOS
wonea
  • 4,783
  • 17
  • 86
  • 139
  • This may be relevant: http://stackoverflow.com/questions/12555049/timer-in-portable-library – Jason Jan 16 '14 at 00:43
  • Thanks I've seen that thread but I cannot target Windows 8.1 and above due to Xamarin. –  Jan 16 '14 at 00:58
  • Right, but there are workarounds suggested before the mention of the permanent fix in 8.1. At least that's how I read it. – Jason Jan 16 '14 at 01:25
  • I tried some of the work around's by created a .NET 4.0 PCL and creating timer instances in there but they appear to crash my app I debug. I don't know if it is because I am using Xamarin w/ MVVMCross –  Jan 16 '14 at 02:08
  • 1
    I made a big error where I didn't reference the project. was a bad mistake –  Jan 16 '14 at 03:55

2 Answers2

9

@stevemorgan answer works really well. I created a Timer utility based on that code to make it more reusable. I also added a "runOnce" parameter that will stop the timer after the first tick

public class PclTimer
{
    public bool IsRunning { get; private set; }

    public TimeSpan Interval { get; set; }
    public Action Tick { get; set; }
    public bool RunOnce { get; set; }
    public Action Stopped { get; set; }
    public Action Started { get; set; }

    public PclTimer(TimeSpan interval, Action tick = null, bool runOnce = false)
    {
        Interval = interval;
        Tick = tick;
        RunOnce = runOnce;
    }

    public PclTimer Start()
    {
        if (!IsRunning)
        {
            IsRunning = true;
            Started?.Invoke();
            var t = RunTimer();
        }

        return this;
    }

    public void Stop()
    {
        IsRunning = false;
        Stopped?.Invoke();
    }

    private async Task RunTimer()
    {
        while (IsRunning)
        {
            await Task.Delay(Interval);

            if (IsRunning)
            {
                Tick?.Invoke();

                if (RunOnce)
                {
                    Stop();
                }
            }
        }
    }
}

I´m using this in MvvmCross with no issues:

timer = new Timer(TimeSpan.FromSeconds(4), 
            () => ShowViewModel<UserMatchViewModel>(), true)
            .Start();
xleon
  • 6,201
  • 3
  • 36
  • 52
3

You can always do it yourself thanks to the wonder of async/await:

public async Task RunTimer()
{
    while(_timerRunning)
    {
        // Do Work
        // ...

        await Task.Delay(_timerInterval);
    }
}

When you call it, don't await it. It will run on a background thread and you can terminate the loop by setting the _timerRunning field from elsewhere (or by killing the Task that's returned from the call).

Steve Morgan
  • 12,978
  • 2
  • 40
  • 49