Xamarin.iOS application branding using Github and App Center

Github & App center opportunities by making the release processes easier

The combination of these two powerful services has made the deployment for mobile application easier than ever, from building to testing and releasing in just few steps. I was amazed how repetitive process can be simplified and automated without any trouble for the developers. That is why I found it challenging and decided to create this article. 

What are we going to do next?

For this article I choose to make modification on the application that was already on my repository for a while, you can look at this article for more details. The aim of the application is to represent just one place in Postcard format. Looking from that point of view there was no point to create so many repositories for the same application and a real time saver would be if the same application is able to be used for various places. I was thinking for a way to reuse the same code, but yet create many more different apps.

Why we need to brand an application?

At the first sight this may looks irrational but there are many use case scenarios where this is required, here are some of them:

  1. You want to sell same product for different companies, but all of them have different name, different logo and colors that are typical for the brand.
  2. You want to create multiple environments from the same application ALPHA, BETA, PRODUCTION they all are the same app but may work with another API.

Lets get started

Lets navigate to App Center, and choose Build section from the Menu. Choose to edit already existing build definition. In the built configuration there is a section called Build scripts. By using this section we can execute custom scripts in our application. As in the official documentation is stated three type of scripts are available: post-clone, pre-build and post-build. The post-clone script runs immediately after the repository was cloned but before we do anything else on our end. The pre-build script runs before the actual build starts. The post-build script runs after the build has finished and we have copied all the necessary artifacts to the output directory.

Where to create these scripts?

Create these scripts in the same folder where your .sln file is, or .cspoj file if Android. Note that there is already a collection of scripts that you may find useful.

The full code which I am modifying can be find on Github. It is an Xamarin.iOS application referring to the above script changes are in appcenter-pre-build.sh file.

Firstly lets take a look of the essential configuration information changes, that are under info.plist file.

  •  Change App Name – Changing application name identifies what name users will see (from Postcard Zagreb -> Postcard Bitola)
plutil -replace CFBundleName -string "$BUNDLE_NAME" $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Info.plist
  • Change the App bundle identifier – Changing the identifier means that we are creating unique application for any of the brands. (com.stretchyheader.ios.zagreb -> com.stretchyheader.ios.bitola) 
plutil -replace CFBundleIdentifier -string $BUNDLE_IDENTIFIER $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Info.plist
  • Change version name – Changing version name means that our branded app does not need to be the same version name(1.0, 2.0 …) as the original app
plutil -replace CFBundleShortVersionString -string $BUNDLE_VERSION $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Info.plist

Further more the changes should be applied as well as on the constants for the application. This usually includes Colors, Translations specific for application, Rules that tend to be modified during different scenario’s, variables that are used in the entire application.

Note: While developing application we have to be sure that we always use the same resource. e.g.: Using class for color palette, not hardcoding the colors. 

In this case Branding folder files are what is going to be modified.

  • Change color palette – Static content where Colors are placed
find $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/ColorPalette.cs -type f -exec sed -i '' "s/PrimaryColor\ =\ \(.*\)/PrimaryColor\ =\ UIColor.FromRGB($PRIMARY_COLOR_R,$PRIMARY_COLOR_G,$PRIMARY_COLOR_B);/g" {} \;
	
find $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/ColorPalette.cs -type f -exec sed -i '' "s/AccentColor\ =\ \(.*\)/AccentColor\ =\ UIColor.FromRGB($ACCENT_COLOR_R,$ACCENT_COLOR_G,$ACCENT_COLOR_B);/g" {} \;
  • Change content – Dynamic data can be really complex to modify, for that purpose I chose a powerful command plutil as helper
plutil -convert xml1 $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.json -o $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.plist
	

plutil -replace headerImage -string $POSTCARD_HEADER_IMAGE $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.plist
	

plutil -replace title -string $POSTCARD_NAME $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.plist
	

plutil -replace description -string "$POSTCARD_DESCRIPTION" $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.plist
	

plutil -convert json -o $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.json  $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.plist
	

rm $APPCENTER_SOURCE_DIRECTORY/StretchyHeader.iOS/Branding/AppResources.plist 

In many of the scripts you may noticed that the environment variables are marked with $. Same as in the code, we do not hard code the values because we want to use the scripts for many postcards. Here is where we define the variables.

After applying all this changes and rebuilding the app from App Center, here is the product we get. A brand new postcard 🙂

Postcard place using Stretchy header in Xamarin.iOS

A year ago I have published a public repository on how to create a stretchy header using Xamarin.iOS application. This time I decided to go a little further with this solution, by preparing it to be able to be rebranded. For now let’s look at how to implement this sassy component.

The app is postcard for places and for this demo I chose to write about my favorite city by far. Full implementation can be previewed on (Github Code).

Create TableView with source and delegate separate

Lets start with creating a TableView and presenting the data with TableViewDataSource and TableViewDelegate by providing custom cells that will contain the Header and the Content for the postcard. The Heather is chosen to be a photo and the Content is information, both about the city.

TableViewDataSource

public class TableViewDataSource : UITableViewDataSource
{
    private const int numberOfRowsInSection = 1;

    public override UITableViewCell GetCell(UITableView tableView, 
                                            NSIndexPath indexPath)
    {
         return tableView.DequeueReusableCell(TableViewContentCell.Key) as
                               TableViewContentCell ?? 
                               TableViewContentCell.Create();
    }

    public override nint RowsInSection(UITableView tableView,
                                       nint section) 
    {
         return numberOfRowsInSection;
    }
}

TableViewDelegate

public class TableViewDelegate : UITableViewDelegate
{
   public override nfloat EstimatedHeight(UITableView tableView,
                                          Foundation.NSIndexPath indexPath)
   {
        return tableView.RowHeight;
   }

   public override nfloat GetHeightForRow(UITableView tableView, 
                                          Foundation.NSIndexPath indexPath)
   {
        return UITableView.AutomaticDimension;
   }
}

Add the Stretchy Header to the TableView

To achieve a transparency at the top of the screen the header has to be added as subview in the TableView. By doing this, the header goes above the content in the TableView and in this scenario they are overlapping. Next step is to push down the content by managing the ContentOffset and ContentInset of the TableView. In this demo the Header size is 300. After these steps proper position of the elements in the view is achieved.

TableView.DataSource = new TableViewDataSource();


TableView.Delegate = new TableViewDelegate();


tableViewHeaderCell = TableView.DequeueReusableHeaderFooterView(
                                 TableViewHeaderCell.Key) 
                                 as TableViewHeaderCell ?? 
                                 TableViewHeaderCell.Create();

tableViewHeaderCell.UserInteractionEnabled = true;

TableView.TableHeaderView = null;


TableView.AddSubview(tableViewHeaderCell);

To achieve the effect of stretching the header rectangle, a variable called effectiveHeight have to be created. The effectiveHeight is fixed size which is the initial height of the header. What is important for the header is to change the content mode to Aspect Fill so it will scale the content to fit the size of itself by changing the aspect ratio of the content.

The following code represents the ability of the header to adapt its size to the changes done while scrolling. As proposed the header is the default size when there is no movement. By scrolling, the header height is supposed to increase by adding the difference between the offset and the effectiveHeight. If the absolute value of the offset is smaller than the effectiveHeight no changes will occur. And simple as that we are done with the logic.

public void UpdateHeaderView()
{
    var headerRect = new CGRect(0,
                                -effectiveHeight,
                                UIScreen.MainScreen.Bounds.Width,
                                TABLE_HEADER_VIEW_HEIGHT);

    if (TableView.ContentOffset.Y < -effectiveHeight)
    {
           headerRect.Y = TableView.ContentOffset.Y;
           headerRect.Height = (-TableView.ContentOffset.Y);
    }

    tableViewHeaderCell.Frame = headerRect;
       
 }

Invoke method in scroll event of the TableView

As said above, this happens on scrolling the TableView. So, next create an event which will listen to table scroll and invoke it on the Scrolled method in the TableViewDelegate, than attach it on the TableView.

public delegate void DidScrollEventHandler();

public event DidScrollEventHandler DidTableScrolled;

public override void Scrolled(UIScrollView scrollView)
{
    DidTableScrolled();
}

MainTableViewController

tableViewDelegate.DidTableScrolled += UpdateHeaderView;

After doing all these steps, what is left is to compile, run your app, and watch this beautiful effect happening.