Alwyn Lombaard's blog

Mobile developer (guitar student and athlete in my spare time)

How to serialize and deserialize an iOS device token from NSData to String and back in Xamarin.iOS

Obtain the device token as usual when the user opts in to receive remote push notifications. Then convert it to a base64 string that can be stored and used later.

Here’s how to do it. In your app delegate:

public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    var tokenStringBase64 = deviceToken.GetBase64EncodedString(NSDataBase64EncodingOptions.None);
    //now you can store it for later use in local storage
}

To convert it back into an NSData object that you can send to third parties (like MixPanel and ExactTarget) that require the token as NSData:

var deviceToken = new NSData(tokenStringBase64, NSDataBase64DecodingOptions.None);

Mixpanel.SharedInstance.People.AddPushDeviceToken(deviceToken);

ETPush.PushManager.RegisterDeviceToken(deviceToken);

Xamarin iOS swipable multiple step process used for onboarding

The requirement

I recently had to build an onboarding process in the JustGiving iOS app that consists of a series of screens that can be navigated by swiping forward and backward.

Step 1 Step 2 Step 3 Step 4 Step 5

The solution

I used a UIPageViewController with a UIPageViewControllerDataSource for navigating through steps and a UIPageControl for the progress indicator.

The working sample solution can be found on GitHub

The end result

Steps

Each step is a UIViewController that implements the following interface.

public interface IMultiStepProcessStep : IDisposable
{
    int StepIndex { get; set; }
    event EventHandler<MultiStepProcessStepEventArgs> StepActivated;
    event EventHandler<MultiStepProcessStepEventArgs> StepDeactivated;
}


public class MultiStepProcessStepEventArgs
{
    public int Index { get; set; }
}

UIViewController step

A step publishes its index when it is activated or de-activated as the active step. This is done in ViewDidAppear and ViewWillDisappear.

public class MakeGoodthingsHappenStep : UIViewController, IMultiStepProcessStep
{
    public override void ViewDidAppear(bool animated)
    {
        base.ViewDidAppear(animated);
        StepActivated?.Invoke(this, new MultiStepProcessStepEventArgs { Index = StepIndex });
    }

    public override void ViewWillDisappear(bool animated)
    {
        base.ViewWillDisappear(animated);
        StepDeactivated?.Invoke(this, new MultiStepProcessStepEventArgs { Index = StepIndex });
    }

    public int StepIndex { get; set; }
    public event EventHandler<MultiStepProcessStepEventArgs> StepActivated;
    public event EventHandler<MultiStepProcessStepEventArgs> StepDeactivated;
}

UIPageViewControllerDataSource

The data source is a UIPageViewControllerDataSource that is constructed with a list of IMultiStepProcessStep steps.

public class MultiStepProcessDataSource : UIPageViewControllerDataSource
{
    private readonly List<IMultiStepProcessStep> _steps;

    public MultiStepProcessDataSource(List<IMultiStepProcessStep> steps)
    {
        if (steps == null)
        {
          throw new ArgumentNullException(nameof(steps));
        }
        if (!steps.Any())
        {
            throw new ArgumentException("steps cannot be empty.", nameof(steps));
        }
        if (steps.Any(s => !(s is UIViewController)))
        {
            throw new ArgumentException("all steps must be a UIViewController", nameof(steps));
        }

        _steps = steps;

        for (int i = 0; i < _steps.Count; i++)
        {
            var step = _steps[i];
            step.StepIndex = i;
        }
    }

    public List<IMultiStepProcessStep> Steps => _steps;

    public override UIViewController GetPreviousViewController(UIPageViewController pageViewController,
        UIViewController referenceViewController)
    {
        var step = referenceViewController as IMultiStepProcessStep;
        if (step == null)
        {
            return null;
        }

        var index = _steps.IndexOf(step);
        if (index <= 0)
        {
            return null;
        }

        return   _steps[index - 1] as UIViewController;
    }

    public override UIViewController GetNextViewController(UIPageViewController pageViewController, 
                                                           UIViewController referenceViewController)
    {
        var step = referenceViewController as IMultiStepProcessStep;
        if (step == null)
        {
            return null;
        }
        var index = _steps.IndexOf(step);
        if (index + 1 == _steps.Count)
        {
            return null;
        }

        return _steps[(step.StepIndex + 1)] as UIViewController;
    }
}   

UIPageViewController

The UIPageViewController is constructed from the data source.

public sealed class MultiStepProcessHorizontal : UIPageViewController
{
    public MultiStepProcessHorizontal(MultiStepProcessDataSource dataSource) 
        :base(UIPageViewControllerTransitionStyle.Scroll, 
              UIPageViewControllerNavigationOrientation.Horizontal)
    {
        DataSource = dataSource;
        SetViewControllers(new[] {dataSource.Steps.FirstOrDefault() as UIViewController}, 
                           UIPageViewControllerNavigationDirection.Forward, 
                           false, 
                           null);
    }
} 

UIPageControl

A UIPageControl is used to indicate which step is active.

UIPageControl

Putting it all together in the OnBoardingViewController

The event handlers for when a step is activated and de-activated are used to set the current page index and to update any other parts of the UI as needed.

private void HandleStepActivated(object sender, MultiStepProcessStepEventArgs args)
{
    _pageControl.CurrentPage =  args.Index;
}

private void HandleStepDeactivated(object sender, MultiStepProcessStepEventArgs args)
{
    //update the UI as required while transitioning between steps
}

Get the steps that form part of the process and wire them up to StepActivated and StepDeactivated events.

private List<IMultiStepProcessStep> GetSteps()
{
    var steps = new List<IMultiStepProcessStep>()
        {
            new MakeGoodthingsHappenStep(),
            new FundraiseStep(),
            new ConnectStep(),
            new DiscoverStep(),
            new GetStartedStep()
        };

    steps.ForEach(s => 
    {
        s.StepActivated += HandleStepActivated;
        s.StepDeactivated += HandleStepDeactivated;
    });

    return steps;
}

Setup and add the UIPageViewController and UIPageControl controls to the view.

private MultiStepProcessHorizontal _pageViewController;
private UIPageControl _pageControl;

private List<IMultiStepProcessStep> _steps;
public List<IMultiStepProcessStep> Steps => _steps ?? (_steps = GetSteps());

public override void LoadView()
    {
        View = new UIView();

        _pageViewController = new MultiStepProcessHorizontal(new MultiStepProcessDataSource(Steps));

        _pageControl = new UIPageControl
            {
                CurrentPage = 0,
                Pages = Steps.Count
            };

        View.Add(_pageViewController.View);
        View.Add(_pageControl);
}

An Irish Air

Recorded during my practice. My recordings are aimed at documenting my development while learning to play guitar.