Adding a guided tour to your iOS App: Objective-C

Ayush Khare
Healint-Engineering&data
3 min readMay 21, 2021

--

There are times when it is helpful to the user to get a nudge in the right direction. Hence we decided to include CoachMarks in our app.

What are we trying to achieve?

We will be implementing this with the open source library: Instructions. Since the library is written in Swift so first we need to implement an Objective C wrapper for us to use it.

Basic Usage:

Instructions provides a very simple way to use if you are familiar with table/collection views.

Example:

CoachMarksControllerDataSource requires 3 mandatory methods:

// number of coach marks to be displayed for each flow of tour func numberOfCoachMarks(for coachMarksController: CoachMarksController) -> Int { 3 return 1 4}// lets you customise how a coach mark will appear and position     itself func coachMarksController(_ coachMarksController: CoachMarksController, 3                          coachMarkAt index: Int) -> CoachMark { 4    return coachMarksController.helper.makeCoachMark(for: pointOfInterest) 5}// similar to "cellForRowAtIndexPath", lets you customize how a coach marks will lookfunc coachMarksController(
_ coachMarksController: CoachMarksController,
coachMarkViewsAt index: Int,
madeFrom coachMark: CoachMark
) -> (bodyView: UIView & CoachMarkBodyView, arrowView: (UIView & CoachMarkArrowView)?) {
let coachViews = coachMarksController.helper.makeDefaultCoachViews(
withArrow: true,
arrowOrientation: coachMark.arrowOrientation
)

coachViews.bodyView.hintLabel.text = "Hello! I'm a Coach Mark!"
coachViews.bodyView.nextLabel.text = "Ok!"
return (bodyView: coachViews.bodyView, arrowView: coachViews.arrowView)
}

Now let’s get started with the Objective C usage of this library and try to make it as simple and scalable as possible. We followed MVVM pattern for this.

Let’s look at an example of a base builder which will be used as a parent for each coach mark which will be view controller specific.

Now we just need to use this as a base builder for each of our screen/feature specific instructions flow. Let’s look at an example:

Now that our Model is done, let’s have a look at our ViewModels. We will following the same approach of having a base class.

Now let’s have a look at how we can take advantage of this base class in our subclasses

Let’s create a CoachMarkManager class that will fetch and return the respective models for each view controller where we want the instructions to be displayed.

Here’s how we can use the above class to get our CoachMark object with all the necessary information:

- (void)viewDidLoad {
[super viewDidLoad];
self.coachMarkManager = [[CoachMarkManager alloc] init];
[self.coachMarkManager fetchInstructionsForViewController:self withCompletion:^(CoachMark * _Nonnull coachMark) {
self.coachMark = coachMark;
}];
}

Now that we have our CoachMark object, let’s try to get our viewModel which will be transforming the model information into values that can be displayed into a view.

Let’s create a factory class that will return us the corresponding view model builders based on view controllers.

Here’s how we can use this factory to get the corresponding viewModel for our viewController

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.coachMarkViewModelBuilder = [CoachMarkViewModelBuilderFactory viewModelBuilderForCoachMark:self.coachMark];
// start the tour
[self.coachMarkViewModelBuilder startTourIfValid];
}

Now what’s remaining is the View class. Let’s create one:

We can use our model to initialise and store InstructionsManager object that will be access from our viewModel to start/stop the tour when required

coachMark.instructionsManager = [[InstructionsManager alloc]
initWithParentViewController:self.viewController
clickHandler:^(NSInteger index) {
coachMark.actionHandler(index);
} dismissHandler:nil];

// here clickHandler is defined to know when a coachMark was tapped for tacking purpose

And that’s it, we are ready to show the first set of instructions to our users. Using MVVM we can easily scale to include more instruction flows for different viewControllers. We just need to create a new Model and ViewModel and we are good to go!

--

--