From itineraries to widgets

The tale of Skyscanner app’s dynamic result page

The tale of Skyscanner app’s dynamic result page

The story so far…

At Skyscanner, we’re continually iterating on our app to help travellers find the best flights, hotels or car hire for your travels. Unsurprisingly, the search results page is one of the most important parts of the app. This is where our users spend most of their time comparing the results and changing search parameters.

This is how the reviews looked in the Netherlands
Image 3
Image 4
Users loved the change in the Netherlands where we first introduced it
Users loved the change in the Netherlands where we first introduced it
Users loved the change in the Netherlands where we first introduced it
There are some advertisements shown on specific routes with specific market setting

Design goals:

  • To iterate fast on new widgets and make it easier to get them into the list
  • Minimise the risk that creating a new widget breaks another one
  • Make experimentation easy; adding a new widget should not be hard and should not the need full support of the team behind the list.
  • Make it possible to run widget calculation codes in parallel or in a sequence.
  • Make the list configurable on the backend in order to release independently from app release cycles

From idea to production:

Our first step was just drawing. We sat down on our own and did some architecture sketches. Then we presented our ideas and reached agreement as a team.

Cards and providers

Widgetify works like this: all new content (card) types we want to show in the list have to have a provider. We can register this provider based on a feature flag in order to enable the content type. But a provider doesn’t always need to return a card. For example if you are an American citizen or a permanent resident or you are based within the US, you may only travel to Cuba if your trip qualifies under one of twelve US Government approved categories. This is a legal requirement. We start the Cuban warning provider on every search, however, it only returns a card if the search and user is conforming to the criteria.

Each card type has its own provider. In addition to the standard search results returned by the ItineraryProvider, you can see some of the other special/custom cards we already have.
Image 9

How easy it is to add new Content?

Let’s imagine you want to create a new widget that shows some HTML content. This can, for instance, be useful for growth/marketing teams since they can easily add content to your app. Let’s call the new provider HTMLCardProvider.

On top of the results, people could see this html widget helping to choose better days
#import “HTMLCardProvider.h”#import “HTMLCardViewModel.h”
@implementation HTMLCardProvider{ SKYHtmlWidgetClient *_htmlClient;}- (instancetype)init{ if (self = [super initWithType:SKYFlightsDayviewPipelineItemTypeCustomWebView parent:SKYFlightsDayviewPipelineItemTypePlaceholder]) { _htmlClient = [SKYHtmlWidgetClient new]; } return self;}-(void)start{ [[self getServiceResponse] continueWithBlock:^id _Nullable(BFTask<SKYHTMLWidgetServiceResponse *> * _Nonnull task) { // If we have a successful response, we get to this stage. In case of errors this block will not run. SKYHTMLWidgetServiceResponse *serviceResponse = task.result; HTMLCardViewModel *htmlViewModel = [[CustomWebViewCardViewModel alloc]]; self.finished = YES; [self.delegate provider:self refreshCompletedWithModels:@[htmlViewModel]]; }}- (void) registerCellForCollectionView:(UICollectionView*)collectionView{ [collectionView registerClass:[HTMLCardViewModel cellClass] forCellWithReuseIdentifier:[HTMLCardViewModel cellName]];}@end
#import “HTMLCardCell.h”@implementation HTMLCardCell{   UIWebView *_webview;}- (instancetype)initWithFrame:(CGRect)frame{   self = [super initWithFrame:frame];   if(self)   {      _webview = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];      _webview.scrollView.scrollEnabled = NO;      _webview.scrollView.bounces = NO;      _webview.delegate = self;      [self.contentView addSubview:_webview];   }   return self;}- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {   NSURL *URL = [request URL];   if ([[URL scheme] isEqualToString:@”skyscanner://”]) {   // deeplink to the appropriate page   [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[URL absoluteString]]];      return NO;   }   return YES;}#pragma mark — Layout-(void)setViewModel:(HTMLCardViewModel *)viewModel{   _viewModel = viewModel;   [_webview loadHTMLString:_viewModel.htmlString baseURL:nil];}
- (void)viewDidLayoutSubviews{ [super viewDidLayoutSubviews]; [_webview setFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];}@end


Before the project started in November, we had three “widgets” and adding a fourth took one developer at least a week. Today we have fourteen providers from three teams and one of them is an HTML provider that can be configured from the backend. This means web developers can port their features more easily and without our full support.

if ([FeatureFlag isHtmlWidgetEnabled]){   // create Provider   // register Provider}

Like what you hear? Work with us

We do things differently at Skyscanner and we’re on the lookout for more Engineering Tribe Members across our global offices. Take a look at our Skyscanner Jobs for more vacancies.

About the author

My name is Zsombor Fuszenecker and I am working on the Skyscanner app in our Budapest office. Currently our squad is responsible for the results and booking pages. We aim to help travelers with finding their preferred flights easier. I get motivated by seeing our users use and recommend the features I helped to build such as this.

Zsombor Fuszenecker, Skyscanner



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Skyscanner Engineering

We are the engineers at Skyscanner, the company changing how the world travels. Visit to see how we walk the talk!