Private packages with Azure DevOps

Recently Microsoft announced a rebranding of Visual Studio Team Services (VSTS) to Azure DevOps and as a big fan of Azure, I wanted to check out if the changes were just a new name or if it’d progressed to be a little more welcoming.
I say this because as someone with limited experience using VSTS, I always found it to be a little intimidating so tended to use simpler services like App Center for building my apps and Trello for my Kanban boards. I hoped that the change would include some UI enhancements that could help me ease into DevOps rather than being thrown into the deep end.
Thankfully the team has done some fantastic work in making Azure DevOps easier to get started with and I’ve now adopted it for managing my personal long term project.
In this post, I’m going to discuss how and most importantly why I’ve configured Azure DevOps to allow me to have confidence in the code I’m writing.
In this post, I’m going to discuss how and most importantly why I’ve configured Azure DevOps to allow me to have confidence in the code I’m writing.

One huge solution to rule them!

The project I’m working on is big, or at least it’s going to be massive. Right now its just a minimum viable product and it contains 17 projects, which I originally put into a single Git repository. This worked well for the beginning of the project but as I started to add more and more projects it became difficult to keep things separate.

It’s for this reason that I decided to create two separate solutions to make a clear separation of concerns. Ultimately I’ll probably end up splitting up Lighting Core Solution further as the project develops but for now I think two solutions provides me with enough separation.
Simple Archteicture.png

Smaller Solutions

Having two separate solutions rather than one beast makes my life significantly easier for ensuring that the Lighting Core code doesn’t become too sticky with my UI and vice-versa. It does, however, cause me some difficulties in how I should reference the dependancies as I don’t have an easy way to ensure that the UI project has all the code required to build. To solve this, I went ahead and moved all my code in Azure DevOps as a stepping stone towards fully embracing the tool.

devops1

Private Nuget Feed

With all the code hosted in Azure DevOps I have a one-stop shop for my projects development.

I went ahead and defined build processes and hooked them up so they’d be triggered everytime I pushed code to the master branch.

devops2

The build steps is very simple. I restore packages, build and then pack up the DLLs ready for release.

devops3

I’ve defined separate pack tasks for each project that I wanted to turn into a Nuget package. This task handles packaging up the results from the build ready for releasing either publicly or privately.

devops4

I’ve then defined the most basic release pipeline possible to take the results of the build pipeline and push to Nuget.

devops5

Because I’m releasing the packages privately, I host them in Azure DevOps and can access them in Visual Studio with minimal configuration required!

devops6

Wrapping up

This blog post covers at a very high-level how I’ve gone about setting up the basics of a continuous integration and deployment system for my pet project. If you want to learn how you can also configure your own CI/CD system then checkout the great tutorial over at Microsoft Docs.

 

Continuous delivery of macOS apps built with Swift

Anyone familiar with my ramblings will be aware that I mostly develop in C# using a mixture of Xamarin and .NET Core depending on what I’m building. Earlier this year I took the decision that I’d be serious about learning Swift and got started with building a simple utility app for macOS to help me find training images for some machine learning.

Screen Shot 2018-05-10 at 11.11.55

The app has mostly just sat on Github with little love since I originally published it, so this week I dusted it off (I actually just cloned it again from Github but I like the metaphor) and started implementing a few of the features that didn’t make it to the first release. The most obvious is file tagging, which makes it possible to add file system tags to exported images, for easier discovery of exported images.

Screenshot 2018-09-28 at 10.25.14

I shared a gif of the new build with a colleague, and he loved it so much that he wanted a copy. Now I could have easily have behaved like an animal and built a version on my development machine and sent over the results, but instead, I opted to listen to the sane part of my brain that was calling for me to set up a CI/CD pipeline.

Enter Microsoft’s App Center

If you’re not familiar with App Center then you’re in for a treat! App Center provides a one-stop-shop for services that app developers will likely need. This includes building, testing, distribution, analytics and crash reporting to name a few. Today I’m going to focus on the build aspect, but I’ll cover other features in upcoming posts.

Microsoft has been working hard on adding new features to App Center, and one of those new features in the preview is the ability to build Swift macOS apps. The setup process only requires a few clicks, and we’re up and running. Below a gif of the process recorded in real-time which shows how quickly I managed to get a build setup and running.

Build.gif

App Center Build Setup

To get started we have to create a new app within App Center and specify a name OS and platform as a minimum. In my case, I only really need to worry about selecting macOS as App Center currently only supports Objective-C and Swift as languages for macOS app development. Screenshot 2018-09-28 at 10.33.06.png

Setting up the build pipeline

Once we’ve clicked “Add New App”, we’ll be presented with a screen encouraging us to integrate App Center SDKs into your app. I’ll cover the advantages of this in another post as it’s not needed to use App Center. Did I mention that every feature in App Center is optional? In the post, we’re only going to use the build and distribute functionality and ignore everything else.

Screenshot 2018-09-28 at 10.40.21

Build Configuration

As mentioned earlier in the post, the code is hosted on Github which is integrated with App Center. This allows me to connect App Center to the repository and anytime I push to a branch I can have App Center automatically trigger a build.

Screenshot 2018-09-28 at 10.43.40.png

Once I’ve selected Github I’m presented with a list of all my repositories for me to select which one I wish to link to my App Center App.

Screenshot 2018-09-28 at 10.45.12.png

In this example, the repository only has one branch so I’ll select that puppy and move onto configuration.

Screenshot 2018-09-28 at 10.46.56.png

Screenshot 2018-09-28 at 10.50.24.png

Build Configuration

We want to do a few things with the build configuration. Number one, it has to sign the build for distribution using my Apple Certificates and secondly I want to increment the version number of the app automatically.

Screenshot 2018-09-28 at 10.51.15.png

Signing builds

In order to sign builds for distribution, we’ll need to upload a copy of our .p12 file and a valid provisioning profile.

Screenshot 2018-09-28 at 10.52.31.png

Incrementing build numbers

App Center has native understanding of our projects info.plist file (thanks to the work they did on supporting iOS) so incrementing the build number only requires a few button clicks to configure.

Screen Recording 2018-09-28 at 10.56 am.gif

Distribution

We’re almost finished configuring the build process but we’ve one last step to configure and that’s distribution!

By default, the distribution list is a little lonely as it’ll just be you, but as you find people excited to try your apps you can add them to lists and control what versions of the app they get. For example, you might want your VIPs to get GM access and staff to have access to betas.

Screenshot 2018-09-28 at 10.58.42.png

Adding distribution groups

To setup my VIPs distribution list I head over to the “distribution” beacon on the left hand menu and click “Add Group”.

Screenshot 2018-09-28 at 11.08.00.png

Right now I’ve only one VIP and that’s my colleague Dean but this is enough to demonstrate the functionality. It’s worth noting that I need to pop back to the build configuration to update the distribution to VIPs if I want Dean to get a copy of the builds triggered from Master.

Screenshot 2018-09-28 at 11.13.45.png

Distribution email

And with only a few clicks, my users will now get a nice email with a link to install the latest and greatest builds of my app!

Screenshot 2018-09-28 at 11.17.45.png

Conclusion

App Center is a powerful tool for app developers to streamline their development processes from building, distribution to monitoring after release. I hope this post has helped you understand how easy it can be to set up a CI/CD pipeline for macOS apps developed with Swift 4.0. If you’ve any questions or feedback then please don’t hesitate to reach out.

Stretchy UITableView Headers with Xamarin

The Yahoo News Digest app includes a couple of interesting user interface elements that I wanted to use within my own apps. The feature that I was most keen to recreate was the stretching UITableViewHeader. Its an effect seen in lots of iOS (sometimes referred to as a parallax header). As Beer Drinkin is going to support multi-tasking on iOS, I needed to ensure my implementation continues to support Storyboards and Auto Layout. Fortunately it proved very simple to get everything setup. In this blog post I’ll be sharing how I went about implementing it

beerdrinkinStretchy

Setting up the Storyboard

Adding a header view

To get started I opened my existing Storyboard and selected the UIViewController that requires the tableview header. In my case the scene (or view controller) isn’t a UITableViewController because I require a floating ‘Check in’ button to be visible at all times. Its worth noting that all the steps in the tutorial work with both UITableViewControllers and UIViewControllers.

Screen Shot 2016-02-01 at 11.36.06

Once I had the storyboard loaded, I dragged a UIView from the toolbox and made sure to insert it above the UITableViewCells as a the header view for the table. I then added a UIImageView to the new UIView and set its constraints to be 0,0,0,0. This way when the parent view (the UIView) resizes, the UIImageView will resize as well. I also made sure to set the UIImageView view mode property to Aspect Fit, which makes sure the image looks great no matter what size the view.

Screen Shot 2016-02-01 at 11.39.13

Adding some C#

Adding the resize code

If I were to have run this now, the table header would be displayed but wouldn’t resize with scroll events. To add scaling, I needed to add a code into my ViewController to setup the stretchy goodness that I wanted.

Because I use the header views height in a number of locations throughout the beer description view controller, I went ahead and created a variable rather than scattering magic numbers over my class.

[sourcecode language=”csharp”]
private nfloat headerViewHeight = 200;
[/sourcecode]

Managing the header view

To allow me to manage the table header, I needed to remove it from the UITableView and keep it as a variable for use later. To do this I created a variable in the beer description view controller.

[sourcecode language=”csharp”]
private UIView headerView;
[/sourcecode]

When we load the view controller, we’ll want to set our headerView variable and then set the UITableViews header property to null. This means the tableview has no header view to manage anymore, instead I’ve taken control of the view which allows me to ensure it resizes correctly as the table view scrolls.Despite having just removed the header view from the UITableView, I actually want to go ahead and add it to the table view hierarchy (but not as the header view property of the UITableView)

[sourcecode langauge =”csharp”]
headerView = tableView.TableHeaderView;
tableView.TableHeaderView = null;
tableView.AddSubview (headerView);
tableView.ContentInset = new UIEdgeInsets (headerViewHeight, 0, 0, 0);
tableView.BackgroundColor = UIColor.Clear;
[/sourcecode]

Listening to TableViewDidScroll

In order to successfully respond to the DidScroll event of the UITableViewSource, I’ll need to create an event in the table views delegate. This is because of an issue with the UITableView DidScroll event not firing when a delegate has been set.

[sourcecode language=”csharp”]
public override void Scrolled (UIScrollView scrollView)
{
DidScroll ();
}

public event DidScrollEventHandler DidScroll;
[/sourcecode]

We can now hook up the table DidScroll event with a small piece of logic for resizing the view.

[sourcecode language=”csharp”]
//Update Tableview
tableView.Source = new BeerDescriptionDataSource(ref cells);
var deleg = new DescriptionDelegate (ref cells);
deleg.DidScroll += UpdateHeaderView;
tableView.Delegate = deleg;

tableView.ReloadData ();
View.SetNeedsDisplay ();
//…
void UpdateHeaderView ()
{
var headerRect = new CGRect (0, -headerViewHeight, tableView.Frame.Width, headerViewHeight);
if (tableView.ContentOffset.Y < -headerViewHeight)
{
headerRect.Location = new CGPoint (headerRect.Location.X, tableView.ContentOffset.Y);
headerRect.Size = new CGSize (headerRect.Size.Width, -tableView.ContentOffset.Y);
}
headerView.Frame = headerRect;
}
[/sourcecode]

Conclusion

Its very easy to use this approach to add resizing animations to any number of controls within your UITableView. My favourite part of this solution is that it works perfectly across all iOS devices and doesn’t force me to drop support of Autolayout.

Creating a 5 star search experience

Search is a feature that can make or break your mobile app, but it can be incredibly difficult to get right. In this blog post, I’m going to share how I’m solving search with Beer Drinkin.

There are many options for us developers looking to implement search solutions into our projects. Some of us may decide to use Linq and Entity Framework to look through a table, and the more adventurous may opt to create an instance of Elastic Search, which requires a lot of work to set up and maintain. For Beer Drinkin, I’m using Microsoft’s Azure Search service as it has proved to be easy to configure and requires zero maintenance.

The reason that Beer Drinkin uses Azure Search is simple: the BreweryDB search functionality is too limited for my needs. One example of this is that the end point often returns zero results if the user misspells a search term. If I searched for “Duval” rather than “Duvel,” BreweryDB’s search would return zero beers. Even if I were to spell the search term correctly, BreweryDB would return all beers from the Duvel Moortgat brewery. Although this is minor, I would prefer that Maredsous 6 and Vedett Extra White not be returned as these do not have “Duvel” in the name.

Screen Shot 2015-12-21 at 15.36.14

Spelling Mistakes

Another issue with using the default search functionality of BreweryDB is its inability to deal with spelling mistakes or offer suggestions. Simple off-by-one-letter spelling mistakes yield no results, something that should be easy to resolve.

Screen Shot 2015-12-21 at 15.40.40

I’ve had sleepless nights worrying that on release, users fail to find results due to simple spelling mistakes. One way to address spelling mistakes is to utilize a spell checking service like WebSpellChecker.net.

The issue with a service such as WebSpellChecker is that it has no context in which to make corrections when it comes to names of products and it also doesn’t support multiple languages.

Another way to minimize spelling mistakes is to provide a list of suggestions as the user types in a search query. You’re probably familiar with this in search engines like Google and Bing. This approach to searching is intuitive to users and significantly reduces the number of spelling mistakes.

Enter Azure Search

Azure Search aims to remove the complexity of providing advanced search functionality by offering a service that does the heavy lifting for implementing a modern and feature-rich search solution. Microsoft handles all the infrastructure required to scale as it gains more users and indexes more data. Not to mention that Azure Search supports 50 languages, which use technologies from multiple teams within Microsoft (such as Office and Bing). What this equates to is Azure Search understands the languages and words of the search requests.

Some of my favorite features

Fuzzy search – Find strings that match a pattern approximately.

Proximity search – Geospatial queries. Find search targets within a certain distance of a particular point.

Term boosting –  Boosting allows me to promote results based on rules I create. One example might be to boost old stock or discounted items.

Getting Started

The first step I took was to provision an Azure Search service within the Azure Portal. I had two options for setting up the service; I could have opted for a free tier or have paid for dedicated resources. The free tier offers up to 10,000 documents and 50MB storage, which is a little limited for what I need.

Because my index already contains over 50,000 beers, I had no option but to opt for the Standard S1 service, which comes at a cool $250 per month (for Europeans, that’s €211). With the fee comes a lot more power with the use of dedicated resources, and I’m able to store 25GB of data. When paying for Search, you’ll be able to scale out to 36 units, which provides plenty of room to grow.

Creating an index

Before I could take advantage of Azure Search, I needed to upload my data to be indexed. Fortunately, with the .NET SDK the Azure Search team provides, it’s exceptionally easy to interact with the service. Using the .NET library I wrote a few weeks ago, which calls BreweryDB, I was able to iterate quickly through each page of beer results and upload them in blocks to the search service.

Screen Shot 2016-01-04 at 10.18.02.png

Uploading documents

[sourcecode language=”csharp”]
Parallel.For(1, totalPageCount, new ParallelOptions {MaxDegreeOfParallelism = 25}, index =>
{
var response = client.Beers.GetAll(index).Result;
var beersToAdd = new List();
foreach (var beer in response.Data)
{
var indexedBeer = new IndexedBeer
{
Id = beer.Id,
Name = beer.Name,
Description = beer.Description,
BreweryDbId = beer.Id,
BreweryId = beer?.Breweries?.FirstOrDefault()?.Id,
BreweryName = beer?.Breweries?.FirstOrDefault()?.Name,
AvailableId = beer.AvailableId.ToString(),
GlassId = beer.GlasswareId.ToString(),
Abv = beer.Abv
};

if (beer.Labels != null)
{
indexedBeer.Images = new[] {beer.Labels.Icon, beer.Labels.Medium, beer.Labels.Large};
}
beersToAdd.Add(indexedBeer);
}
processedPageCount++;
indexClient.Documents.Index(IndexBatch.Create(beersToAdd.ToArray().Select(IndexAction.Create)));

Console.Write( $”\rAdded {beersToAdd.Count} beers to Index | Page {processedPageCount} of {totalPageCount}”);
});
[/sourcecode]

Other data import methods

Azure Search also supports the ability to index data stored in Azure SQL or DocumentDB, which enables you to point a crawler to your SQL table and ensures it is always up to date rather than requiring you to manually manage the document index yourself. There are a few reasons you may not want to use a crawler. The best reason for not using a crawler is that it introduces the possibility of a delay between your DB changing and your search index reflecting the changes. The crawler will only crawl on a schedule, which results in an out-of-data index.

If you opt for the self-managed approach, you can add, remove, and edit your indexed documents yourself as the changes happen in your back end. This provides you with live search results as you know the data is always up to date. Using the crawler is an excellent way to get started with search and quickly get some data in place, but I wouldn’t consider it a good strategy for long-term use.

I mentioned earlier that the free tier is limited to 10,000 documents, which translates to 10,000 rows in a table. If your table has more than 10,000 rows, then you’ll need to purchase the Standard S1 tier.

Suggestions

Before we can use suggestions, we’ll need to ensure that we’ve created a suggester within Azure.

Screen Shot 2016-01-04 at 10.27.15.png

In the current service release, there is support for limited index schema updates. Any schema updates that would require re-indexing, such as changing field types, are not currently supported. Although existing fields cannot be modified or deleted, new fields can be added to an existing index at any time.

If you’ve not checked the suggester checkbox at the time of creating a field, then you’ll need to create a secondary field as Azure Search doesn’t currently support editing the fields. The Azure Search team recommends that you create new fields if you require a change in functionality.

The simplest way to get suggestions would use the following API.

[sourcecode language=”csharp”]
var response = await indexClient.Documents.SuggestAsync(searchBar.Text, “nameSuggester”);
foreach(var r in response)
{
Console.WriteLine(r.Text);
}
[/sourcecode]

Having fun with the suggestion API

The API suggestion provides properties for enabling fuzzing matching and hit highlighting. Let’s see how we might enable that functionality within our app.

[sourcecode language=”csharp”]
var suggestParameters = new SuggestParameters();
suggestParameters.UseFuzzyMatching = true;
suggestParameters.Top = 25;
suggestParameters.HighlightPreTag = “[“;
suggestParameters.HighlightPostTag = “]”;
suggestParameters.MinimumCoverage = 100;
[/sourcecode]

What do the properties do?

UseFuzzyMatching – The query will find suggestions even if there’s a substituted or missing character in the search text. While this provides a better search experiance, it comes at the cost of slower operations and consumes more resources.

Top – Number of suggestions to retreive. It must be a number between 1 and 100, with its default to to 5.

HightlightPreTag – Gets or sets the tag that is prepended to hit highlights. It MUST be set with a post tag.

HightlightPostTag – Gets or sets the tag that is prepended to hit highlights. It MUST be set with a pre tag.

MinimumCoverage – Represents the precentage of the index that must be covered by a suggestion query in order for the query to be reported a sucess. The default is 80%.

How do the results look?

Simulator Screen Shot 4 Jan 2016, 10.39.25.png

Search

The Search API itself is even easier (assuming we don’t use filtering, which is a topic for another day).

[sourcecode language=”csharp”]

var searchParameters = new SearchParameters() { SearchMode = SearchMode.All };

indexClient.Documents.SearchAsync(searchBar.Text, searchParameters);
[/sourcecode]

Special Thanks

I’d like to take a moment to thank Janusz Lembicz for helping me get started with Azure Search suggestions by answering all my questions. I  appreciate your support (especially given it was on a weekend!).

Updated BreweryDB .NET client

687474703a2f2f7777772e6272657765727964622e636f6d2f696d672f62616467652e706e67

header.png


Over the weekend I decided to look at a project which hasn’t recieved much love since I orginally wrote it earlier in the year. That project is PCL for interacting with the awesome beer database that is BreweryDB.

My orginal implementation was very simplistic in its design, only exposed a handful of the endpoint, lacked any error checking and also had a few too little unit tests. With BeerDrinkin’s development coming along nicey, I thought it time to take a look at some of its dependencies and see how I could improve them. First up was BreweryDB and so I set about improving the PCL to hopefully make it useful for other .NET developers.

Supported enpoints

This new version of BreweryDB includes many more supported endpoints. Its now possible to query almost all of BreweryDB using .NET.

  • Adjuncts
  • Beers
  • Breweries
  • Categories
  • Events
  • Features
  • Fermentables
  • FluidSizes
  • Guilds
  • SocialSites
  • Yeasts

 

Getting Started

Beers

  • GET/beers
  • GET/beer/beerId
  • GET/search/

Get all

This returns a list of all beers but will be paginated with 50 beers per page.

[sourcecode language=”csharp”]
//Will return the first page
var response = await client.Beers.GetAll();

//Will return the fourth page
var response = await client.Beers.GetAll(4);
[/sourcecode]

Get by id

This returns a single beer

[sourcecode language=”csharp”]
var response = awaitclient.Beers.Get(“cBLTUw”);
[/sourcecode]

Using request parameters

This returns a  list of beers

[sourcecode language=”csharp”]
var parameters = new NameValueCollection {{BeerRequestParameters.Name, “duvel single”}};
var response = await client.Beers.Get(parameters);
[/sourcecode]

Search by name

This returns a  list of beers

[sourcecode language=”csharp”]
var response = await client.Beers.Search(“duvel”);
[/sourcecode]


Breweries

  • GET/breweries
  • GET/brewery/breweryId
  • GET/search/

Get all

This returns a list of all breweries but will be paginated with 50 breweries per page.

[sourcecode language=”csharp”]
//Will return the first page
var response = await client.Breweries.GetAll();

//Will return the forth page
var response = await client.Breweries.GetAll(4);
[/sourcecode]

Get by Id

This returns a single Brewery

[sourcecode language=”csharp”]
var response = await client.Breweries.Get(“YXDiJk”);
[/sourcecode]

Search by name

This returns a  list of breweries

[sourcecode language=”csharp”]
var response = await client.Breweries.Search(“duvel”);
[/sourcecode]

Naturally its open source

As always, I’ve made this avaiaible on GitHub and Nuget for you to use in your own apps.