Author: danatxamarin

Sample: Custom Vision in Xamarin.Forms with Azure customvision.ai

When I saw Azure Cognitive Services’ customvision.ai session at Build, I needed to play with it as soon as possible. I had dreamed of the day when this level of abstraction made custom vision readily available without needing to rebuild and tune vast computer vision systems full of magic constants and poorly performing algorithms. With CustomVision.ai, it’s simple: you upload several photos of something and tag them, enabling the prediction service to identify with a great level of accuracy whether or not any of your tags appear in a sample photo. You can also train the system in realtime with new data any time you want.

customvision

I build an app called PetID which allows me to snap a photo (camera view reused from the Moments sample – cheers Pierce!). The photo is then sent as a stream to the Custom Vision Prediction endpoint. Then, magic in the cloud happens, and out pops a list of ImageTagPredictions. This list pairs up a tag and the probability that the supplied image contains the tag based on the training data you provided. For example, I uploaded about 20 photos each of my 3 pets, tagged them, and trained the system. I did all this by clicking around the UI. That’s it!

 

 

 

 

There are two NuGet packages for customvision.ai but they are not available as PCLs; they are autogenerated wrappers around the APIs which also drive the website. This means I had to do a little bit of extra work to get it to work with Forms, namely, a custom renderer for the iOS camera view and a DependencyService to abstract the prediction endpoints since I can’t call them from a Forms PCL.

Using this sample

Download the sample here: https://github.com/danwaters/PetID

You’ll need to set up a Training Key and Prediction Key at http//customvision.ai and train a few images to make this work. Then, open APIKeys.cs and add your own keys.

The code is generic enough that it should work with any prediction endpoint, not just my pets.

Conclusion

Extremely powerful vision recognition and classification is now at your fingertips in Azure. There are dozens of applications for this technology – what can you build with it?

Slow Down if you Want to Go Fast, Like Paco de Lucía

Over time, I like to think I have become better at giving a particular moment in time the full attention it deserves.

Let’s take code, for example. One of my favorite code-sayings is “Make it work, and then make it better” (Thanks Pawel). It sounds good on paper, but how often do we really take the time to make it better? Personally, I love making code better – the problem is finding the time to actually justify doing it right. Finding the time to sit there, and not do anything other than improve something you already did. There are so many demands in life, how can we find time to be perfect in everything we do? Surely we have to rush everything.

Actually, I believe the answer to this is to slow down if you want to go fast.

This is Paco de Lucía, one of Spain’s most famous musical exports. He is most known for his picado technique, which refers to the fast passages picked with his index and middle fingers. Paco’s picado was exceptionally loud and bright, with every note sounding out clearly:

How does one practice to be able to execute such complex movements with such indelible accuracy? What kind of secret is this man hiding? Was he part alien? (He passed in 2014, sadly, so we’ll never know).

As almost any musician will tell you, sure, you can feel like you are naturally talented at something, but what makes you skilled is nothing more than putting in the hard work and the practice. My interpretation: when you are talented at a thing, it only means you enjoy doing it enough that you don’t care if you suck. Then, others perceive you as talented because you can do a thing they cannot. You practiced, and now you can do it! Magic.

 

So why can’t we all play guitar like Paco?

First of all, from the age of five, Paco would practice in his room for 8-12 hours per day.  “I learned the guitar like a child learns to speak,” he said in an interview. If you are new to the guitar, you may feel like 8 hours is an eternity to be holding such a frustrating object. If you are experienced with the guitar and this is the first Paco video you’ve seen, and it makes you want to toss your guitar in the fire and give up, that’s a common reaction too.

My point in all of this is: can you imagine what type of practice you would do for 8-12 hours a day to be able to perform live at the level Paco did? If you told most amateur guitarists to sit and practice something for just 10 minutes, they would be totally off track jamming out an Iron Maiden riff within five seconds, because playing music is fun. Practicing music is an entirely different thing, and it is boring as hell. It’s also what separates the amateurs from the pros.

In music, in code, and in life, sometimes you just have to bear down and do the boring stuff with what feels like blind faith that it will pay off somehow. That faith is innately human: faith in our own natural ability to learn and overcome obstacles, to hone control over our minds and muscles, tacitly aware that real change takes plenty of time, attention and perseverance. If we slow down, we can raise our standards and capture more details, and the more we practice, the more effortless and amazing we’ll be when it’s time to show off what we can do.

My friend Henry (L), Paco de Lucia (center), me (right) in 2004

Rest in peace, Paco.

 

On Lateral Thinking

I apply a similar problem solving approach almost everywhere in life, but sometimes that approach fails when I am working on a big challenge or a very hard problem.

I noticed that when I need a new set of tools, I default almost subconsciously to a form of lateral thinking as described by Edward DeBono in 1967.

Uh… Why would I know anything about that in the first place?

In the mid 90’s, my dad, a mechanical engineer, got laid off after working at the same company for 27 years. It seemed very unexpected, and in his true entrepreneurial way, picked himself up, found a partner in crime in a former colleague. They spent months preparing, and eventually began delivering corporate training workshops based on the Six Thinking Hats in the interim while my dad looked for something with benefits.

I’m fairly sure that my dad used lateral thinking himself, because he chose me as his prime experiment subject when testing his curriculum. I was a young teenager then, and I soaked everything in like a sponge.

I encourage everyone to at least read about lateral thinking. While I rarely use the Six Thinking Hats in practice anymore, the critical skill that came from learning about them is that diversity of perspective helps you make better decisions. Knowing what the hats are for and what they represent does come in helpful when brainstorming (or thought showering).

When you’re trying to catch lightning in a bottle, lateral thinking helps you summon the lightning. It’s about the brilliant idea that started off as something others think sounded ridiculous. With lateral thinking, sometimes you really do find a great answer by coming at the problem sideways.

Lateral thinking helps you perform better in some ways. You can:

  • view failures as both disappointing and inspiring
  • arrive at surprisingly creative solutions
  • solve problems faster by considering multiple outcomes for each step
  • see the bright side of every “no” you hear

(it also slices and dices, by the way…)

Naturally, this lends itself well to the software engineer’s mindset and how your work affects the system at large. I remember on the job description for my first role at Microsoft as a technical evangelist, “a keen eye for less obvious opportunities” was a listed skill.

Agile puts perspective at the forefront, with its user stories formulated as “As a <person with a certain perspective> I want to do X so I can Y.”

About half of my work is troubleshooting technical problems with many different types and skill levels of customer engineers, and the ability to actively look for perspectives and inconspicuous solutions is something anyone can agree is a great asset.

Book: Six Thinking Hats by Edward de Bono

 

Building a simple message popup control for Xamarin.Forms

quizgif.gifA simple solution

I needed a quick way to dynamically display a centered, overlaid message in the quiz app I’m working on for my daughter’s elementary school.

This is just a message for status changes and not meant to be interactable, but it’s usually fired in C# from a ViewModel class or UI click handler because I don’t really care what page I’m on when it’s displayed.

It looks like this (the “Quiz Questions updated” block).

AbsoluteLayout is the secret sauce

I already had most of my layouts built (in XAML), and they had StackLayout roots rather than AbsoluteLayouts. I didn’t want to rebuild them in C# in order to make them inherit an AbsoluteLayout – a great suggestion by Brandon Minnick (@BrandonXamarin), but I’m too late in the process for all that refactoring.

An okay, but not perfect, compromise is this snippet:

public static class Popper
{
	public async static Task Pop (string message, AbsoluteLayout attachLayout, int showforMilliseconds = 1500)
	{
		var container = new StackLayout
		{
			HorizontalOptions = LayoutOptions.Center,
			VerticalOptions = LayoutOptions.Center,
			BackgroundColor = Color.FromHex ("#DDEFEFEF"),
			Padding = 10
		};

		var label = new Label
		{
			Text = message,
			FontAttributes = FontAttributes.Bold,
			Style = (Style)Application.Current.Resources["PopupText"]
		};

		container.Children.Add (label);

		container.Scale = 0;
		container.Opacity = 0;

		attachLayout.Children.Add (container, attachLayout.Bounds, AbsoluteLayoutFlags.PositionProportional);
		container.ScaleTo (1.0f, 100);
		container.FadeTo (1.0f, 100);

		await Task.Delay (showforMilliseconds);

		container.ScaleTo (0.0f, 250);
		await container.FadeTo (0.0f, 250);
		attachLayout.Children.Remove (container);
	}
}

What I like about this approach is that I can fire it from anywhere and attach it to any AbsoluteLayout. The message will be added to the absolute layout in the middle, animated, and then removed from the absolute layout.

What I don’t like about it is that I wish this was on my BasePage. The method makes an assumption that there is an AbsoluteLayout root to attach to.

Until I figure out another way of sharing that AbsoluteLayout, this simple solution gets me exactly what I need. Enjoy!

Introducing Xamarin.UITest Page Object Project Templates for Visual Studio

I’ve just released a Visual Studio extension containing project and item templates that will help you write tests a whole lot faster. You can download the VSIX and install the templates here.

The Page Object design pattern, which I explained in a previous post, follows a simple ethos, which is: get out of the spaghetti code and into something a little more flexible that can scale. The main idea is that you create classes which have properties and methods that model and execute the intended user actions. For example, you might construct a LoginPage and give it a method called LogIn(username, password). This method would contain the actual automation calls, hiding away that logic in an easy-to-read and easy-to-consume method.

The benefits of this approach are pretty clear:

  • No copy/pasting spaghetti code.
  • No attempting to do the same thing in two different ways – the page object is the source of truth.
  • Stringing together page object methods to create a scenario only requires the ability to follow a pattern and use Intellisense.

I’ve been writing tests this way for a couple of years now with great success.

My particular implementation of the page object model enables you to create your own pages quickly, encapsulate queries, and create fluid “user stories” that move from page to page.

Check out the templates and let me know what you think!

Upgrading to Continuous Deployment In 1 Day with Xamarin, TeamCity and HockeyApp

It’s Just So Beautiful

Every time I push a commit to my project’s GitHub remote, within about 20 minutes, my beta users get an email that says “New version of [Dan’s app] is available. Download and install now.”

A special thing about the app they’re installing is that it has been automatically verified across 6 iOS devices and versioned automatically during that 20 minutes. All I did was push the code. What would happen if your team had this same capability?

To start right away, skip to Let’s Do This – It’s only 10 steps!

What if you had this automated release pipeline set up today? If you got a critical bug report and needed to push a fix, how soon can you get that into the hands of your users?

This post contains a lot of info, but covers a lot of bases and skill levels. If you follow it closely, you’ll be up and running soon.

CI and CD speed up your dev process and increase your confidence. I learned from my work at Readify that the sooner you have a build, the cleaner your work, the better your velocity is, the more “agile” and responsive you can be to changes in requirements or bug fixes. In fact, for nearly every engagement at Readify, we nailed the devops work in the first week and it made the rest of the project smoother.

Just do it. Do it today. (Or tomorrow, if it’s late right now. But do it).

You can move to continuous integration and continuous deployment, and you can do it one day. I set it up in a couple of hours, but I’ve been doing this a lot lately, so it might take a bit longer if you are new to some or all of the tools.

Going Continuous

The following instructions create an ideal setup for a Xamarin iOS team or developer who uses GitHub, Xamarin Studio on OSX, Xamarin Test Cloud, HockeyApp, and TeamCity. You can substitute some of these technologies if you must. The general idea is the same.

The instructions for Android are almost identical, but we’ll focus on iOS for now. Once you get the iOS build running, you can always duplicate it and tell it to build/submit the apk instead.

All you are doing with a tool like TeamCity is bringing together a bunch of disparate tools and sequencing their execution. Each tool is going to provide you a command blurb of some kind, which you’re going to customize to your needs and then add to TeamCity. TeamCity is what continuously integrates new changes – the HockeyApp component adds deployment to the mix.

Installing the Stuff

Choose the CI solution that works for you, but TeamCity is a great option that I really prefer for Xamarin apps. You can get a free version of TeamCity with one build agent to practice with on your local Mac, which I highly recommend. Download TeamCity here, and then follow the setup instructions. You should be able to access your TeamCity instance at http://localhost:8111/ after you are done installing. Don’t be afraid; this part is easy! If you can’t figure out how to start TeamCity, look for this line in the installation instructions: bin/runAll.sh start

Gathering the Other Stuff

The first few steps in the build are pretty straightforward. When we submit to Xamarin Test Cloud, we’re going to need our trusty shell script command that Test Cloud creates for us. I’m going to show you how to get this working once, and then I will show you how to make it better.

  1. Have C# UITests written. Even if all your test does is wait for any element and then take a screenshot, just write the test. Use your Test Cloud hours. A basic smoke test script will save you from app crashes, and it takes less than 5 minutes, so make one!
  2. Get your upload command from Test Cloud. From http://testcloud.xamarin.com, select New Test Run and set up the test run to your liking. At the final step, you’ll get a command in a window that follows the following format. You will change the red items in a later step – for now, just save this line somewhere. Make sure you copy it exactly as-is from Test Cloud.
    mono packages/Xamarin.UITest.[version]/tools/test-cloud.exe submit
    yourAppFile.ipa YOUR_API_KEY_IS_HERE --devices 947599ea
    --series "master" --locale "en_US" --app-name "MyiOSApp"
    --user you@yourcompany.com --assembly-dir pathToTestDllFolder
    
  3. Set up an API token for your app in HockeyApp. This is done from the API Tokens page of the HockeyApp account settings area (click on your name on the top right when you’re logged in + look for API Tokens at the bottom left menu). If you haven’t set up a HockeyApp account yet, just create a free one.When you create this token, make sure the bundle id you specify for this API token precisely matches the bundle id of the app. You can find and change the Bundle ID in the Info.plist of your Xamarin iOS project. Save this token for later.

Let’s Do This

Okay, dear reader, developer with minimal devops experience. We’re going to do this together. It’s going to be fun, and a little bit difficult, and challenging. However, we’re going to learn a lot, and we’re going to transform how we build, test and deploy our Xamarin apps by the end of the day. I’ve got your back – leave a comment, or for faster answers, tweet at me @danwaters if you have any questions.

At the end of things, your TeamCity build configuration is going to consist of six Command Line steps:

  1. Restore NuGet packages for the solution (which should also include your UITest project)
  2. Auto increment the app version using PlistBuddy
  3. Use xbuild to generate the .ipa
  4. Build the UITest project and build it
  5. Upload the .ipa and .dSYM to Xamarin Test Cloud
  6. Send that bad boy to HockeyApp, and deploy out to your users.

These seven steps will get you to the peaceful bliss that is continuous deployment. It will take you a couple of tries to get each step right. If you feel the need to punch something along the way, an inner tube or other inflatable device will work great, but I’ll do my best to help keep your punching urges to a minimum. I promise that when you’ve got this running, you’ll want to hug your CI every time it increases your confidence or saves your behind.

Let’s get started!

1. Creating the Build Configuration

If you don’t have a default build agent, create one, and then create a new project and build configuration using TeamCity. Fill in the details in whatever way makes sense to you.

The next step is to set up the VCS root.

2. Configure your SSH Key

I highly recommend uploading an SSH key to your TeamCity server and using that to authenticate with GitHub, as opposed to using user credentials or one-time passwords. This takes a little time to configure the first time around, but once you’ve got it working for one repo, you don’t need to mess with it any more.

3. Configure the VCS Root

A build configuration can have several different places it goes to look for code. These are called VCS (version control system) roots, and they connect to things like GitHub.

To create a VCS root, go to your build configuration in TeamCity and choose VCS Roots on the left. By the way, just getting around TeamCity will be unfamiliar at first – the Projects menu on the top left is usually a quick way to get back. You can usually find Edit Configuration Settings when looking at any build, on the top right.

Here are a few tips for configuring a basic connection to GitHub with TeamCity:

  • Fetch URL: This format seems to work well in TeamCity:
    git@github.com/organizationName/repo.git
  • Default branch: I prefer to trigger builds on the dev branch, which means that in this field I put refs/heads/dev. Ideally you’d trigger a build like this when people merge into dev. Leave master for last stable release. If you have no idea what I’m talking about, check out this writeup from the good folks at Atlassian.
  • Username style: UserId
  • Authentication method: Uploaded key (point to the one you added in the last step)

Keep experimenting until Test Connection is successful.

4. Setting Triggers

Screen Shot 2015-11-30 at 3.08.51 PM

This is really a matter of preference on your team. The trigger I have set up is on the dev branch and will include multiple commits from each individual committer. The build I have, which tests the app in Test Cloud too, runs about 20 minutes, so including multiple commits saves lots of time if I’ve added more commits during the last build.

Since I set the default branch on the VCS root, I do not need to add any trigger rules, although for more custom scenarios you are welcome to do this.

I would also recommend disabling the trigger until you are actually ready to test the trigger. You don’t want to be making a bunch of junk commits just to test the build (which you’ll be doing a lot of), so leave this step for last.

5. Adding Build Step #1: Restore NuGet Packages

This step looks really long, but it’s not that bad. It’s just the first time we’re adding a build step, so I’ll make this detailed.

If you have a .gitignore in your repo – and you should – it means that your repository doesn’t include the binaries from your NuGet packages and you can’t build anything, so you need to do this.

A minor caveat on OSX is that any of the prebaked steps like Restore Nuget Packages or Visual Studio Build are not going to work. You have to use mono to run these commands, which is why everything is a shell command in this build. Several months ago, I had issues with nuget.exe, not existing or being findable by TeamCity, and I can’t remember if I solved this or if it just works now. Either way, anticipate that this might be a problem. For now, let’s just see if it works.

To add this build step, go to your build configuration and click Build Steps on the left, followed by Add Build Step.

Screen Shot 2015-11-30 at 3.22.22 PM

Details of the step:

  • Runner type: Command Line
  • Step name: Restore NuGet packages (or whatever you like)
  • Execute step if all previous steps finished successfully
  • Run: Custom script
  • Build script content:
    nuget restore %teamcity.build.checkoutDir%/MyProject.sln

You should not need to explicitly run nuget restore under mono (e.g. mono nuget.exe restore) but it is an option if you have problems with this syntax. The line above works for me using TeamCity on OSX.

If you’re not familiar with the %variables% these can be extremely helpful. checkoutDir points to the location on disk where the git repository is cloned for this build, and you use that as your base path for everything else. You will want to point to your .sln for this, which hopefully is in the root of your checkout directory.

Now – run the build! Let’s see if it works. Save this build step and then click the Run button on the top right. If you need to run with settings, click the ellipsis button next to it instead. You’ll see a (1) appear next to Agents on the top. If you click it, you’ll see all the currently running builds. Hopefully one is yours.

Screen Shot 2015-11-30 at 3.30.11 PM

You won’t see the “running on devices” bit yet, but soon. I promise.

Your build should be green. If you click the build status, and then click on Build Log, you can get a very verbose log of every step of the build. Get accustomed to finding the log, because you’ll be looking through it a lot.

Your build log should look a bit this, so far:

Screen Shot 2015-11-30 at 3.32.38 PM

If your build is green, congratulations! You did a ton of work just now. You got your VCS root hooked up, and got NuGet to restore your packages. But your work has just begun… let’s continue.

6. Build Step #2: Versioning the App

There are a lot of different ways to do this, different schools of thought and opinions. I just want to attach an incrementing version number to my .ipa so it can appear correctly in Test Cloud and HockeyApp. It is, in fact, the job of the build system to assign this version number, because CI is so critical in release management.

I’m just going to use the TeamCity build number for the last bit of the version, and have a major version as a configuration parameter. Remember, there are a ton of ways to do this, and this is just one such way.

First of all, let’s set our configuration parameters. One of them, we’ll read. The other, we’ll generate with a shell script and then inject into our iOS project’s Info.plist using PlistBuddy.

Go to the Parameters section of your build configuration, and then add two new parameters. Both will be environment variables.

The first environment variable, env.major_version, contains the major version of our app, which we set manually. Mine is set at 0.2, so my apps come out of TeamCity with that plus the build number, like 0.2.40. I will manually change the major version when I feel like it’s appropriate.

The second environment variable, env.ios_version, combines env.major_version with the build number and then we reference this variable later. Here’s the configuration for the two parameters:

Configuration parameters are awesome because you can use them for anything – device sets in Test Cloud, API keys, anything that you might reuse or don’t want to expose to the script directly. I’ll suggest some further uses of this later in the post.

Now that we have these two environment variables, we need to put them together.

Create a new build step, also a custom script, called Set iOS Version. For the build script content, enter the following commands:

/usr/libexec/PListBuddy -c "Set :CFBundleVersion %env.major_version%.%build.counter%" "%teamcity.build.checkoutDir%/iOS/Info.plist"
/usr/libexec/PListBuddy -c "Set :CFBundleShortVersionString %env.major_version%.%build.counter%" "%teamcity.build.checkoutDir%/iOS/Info.plist"

Ugh, that’s a bit nasty. I’m sorry. But greatness does not always come easy.

PListBuddy is a tool from Apple that should already be installed on your Mac, even though you might not be able to find it. But it’s there, in /usr/libexec. We invoke PListBuddy and set the version number to env.major_version.build.counter which results in something like 0.2.40 – three integers with a dot separator. It auto-increments with the build, and it works just fine for me.

Aside: These two commands together set two properties in the plist, both to the same value. The only difference in the two lines is CFBundleVersion versus CFBundleShortVersionString. CFBundleVersion is used for development builds in general, whereas CFBundleShortVersionString is the official version for release builds. For our purposes, these values can just be exactly the same, until you decide it should be something else. 

Test the build before moving on to the next step.

7. Build Step #3: Build the iOS App

Oh, we’re there already? Yup. This build step is another shell command, just like the last two. And guess what, it’s dead easy. You can do this.

This is all your custom script needs:

xbuild %teamcity.build.checkoutDir%/iOS/My.iOS.csproj /p:Configuration=Debug /p:Platform=iPhone /p:OutputPath=%teamcity.build.checkoutDir%/drop/ /p:BuildIpa=true

We are building to a new folder called ‘drop’ in the checkout directory. You can build to anywhere you want, but this works for now. You do need to send a developer profile signed, debug IPA to Test Cloud so that the Calabash test-server is enabled.

This line will work fine for a newer Forms app that has an iOS project in the iOS folder. Change the path as appropriate to point to the csproj for your iOS project.

Test the build before moving on to the next step.

8. Build Step #4: Build the UITest Project

Yet Another Shell Command. Assuming you have a Forms app, your UITests will be in the UITests folder. If not, change the path to point to your UITest csproj relative to the checkout directory. I chose not to put this stuff into a drop folder because it really isn’t needed.

xbuild %teamcity.build.checkoutDir%/UITests/ShredTrainer.UITests.csproj

Test the build before moving on to the next step.

9. Build Step #5: Submit to Test Cloud

Earlier in the post, I had you create your version of this command from Test Cloud:

mono packages/Xamarin.UITest.[version]/tools/test-cloud.exe submit
yourAppFile.ipa YOUR_API_KEY_IS_HERE --devices 947599ea
--series "master" --locale "en_US" --app-name "MyiOSApp"
--user you@yourcompany.com --assembly-dir pathToTestDllFolder

Now, we’re going to change it up a bit in order to fit in your environment. There are a few items that you need to change:

  • Path to test-cloud.exe: this comes down in your NuGet package with Xamarin.UITest, so you need to point to the packages folder and then again into the UITest folder, into its tools folder. You are relative to the checkout directory, so you could just do packages/Xamarin.UITest.1.2.0/tools/test-cloud.exe but you will have to come back here and hard-code the UITest version every time you update UITest. There may be another solution here that I haven’t discovered – let me know if you come up with a good idea for managing this!
  •  yourIpaFile.ipa: this should be pulled from the drop folder that you just built to in the last step.
  • YOUR_API_KEY_IS_HERE: When you generate this from Test Cloud, the API key will already exist, just make sure it corresponds to the right team. If it doesn’t, just re-generate the command from the Test Cloud frontend.
  • –devices abcdef11: This represents the devices you selected. If you don’t change this, your tests will always run on the same set of devices. This is another thing I suggest extracting to a configuration parameter, because then you can have multiple named sets and bring them into your builds.
  • –series “master”: this tells Test Cloud to add this test run to the master series, which is the default. From CI I usually like to create a smoke-tests series or something similar. You can also use test series to indicate the nature of the devices you’re testing on (e.g. broad-android-tablet-set) or the type of build (daily-build).
  • –locale “en_US”: specify any ISO-standard locale to force the devices to reboot in this language and locale.
  • –app-name “MyiOSApp”: – this is generated by the Xamarin Test Cloud upload workflow.
  • –user you@yourcompany.com: Identifies who uploaded the app.
  • assembly-dir pathToTestDllFolder: This should point to the location where the UITest binaries are after the build. Since we did a debug build, you’ll specify this folder like %teamcity.build.checkoutDir%/UITests/bin/Debug/

We can tack on a few more switches to this command:

--dsym %teamcity.build.checkoutDir%/drop/my.app.dSYM

This will upload the dsym as well, so you can get better stack traces. Change the path to the dsym as necessary. As configured, you’ll find it in the drop folder after the build step.

--category "myCategory" --category "myOtherCategory"

This line will include and execute tests that have [Category] attributes matching these names. In this case, both myCategory tests and myOtherCategory tests will run, but nothing else.

--test-param screencapture:true

This doesn’t work on iOS, but for Android, you can capture the screen recording and play it back inside of Test Cloud.

Tip: If you want to make this more readable, extract some things to configuration parameters, and use the line continuation character to break up the commands (just a backslash \ on OSX).

Test the build before you continue. If you want, you can just execute the Test Cloud step by disabling the other steps in the build on the Build Steps screen. Test Cloud will use the last build as copied to your checkout directory. It will save you time on the workflow, getting this right, to run this step by itself rather than as part of the full build.

Once your build status goes to “Running on X devices,” then you know you’ve got the integration working properly. If you have problems here, tweet me as I mentioned in the beginning of the post.

10. Build Step #6: Deploy to HockeyApp

This final command is pretty easy, and contains an example of the line continuation character I mentioned in the last step. In this case, for this build step I set the working directory to the drop folder I created, so all paths are relative to that. We only have one path, which is the path to the .ipa.

set -x
curl \
-F "status=2" \
-F "notify=1" \
-F "notes=Build %build.counter%" \
-F "notes_type=0" \
-F "ipa=@<strong>myApp.ipa</strong>" \
-F "release_type=0" \
-H "X-HockeyAppToken: <strong>myAppToken1234517257ABCDEF</strong>" \
https://rink.hockeyapp.net/api/2/apps/upload

Just replace your API token. You can expand this to include other trickery I haven’t taken the time to do yet, like set your HockeyApp release notes to the last commit message, or something like that.

You can see more about this interface from the HockeyApp docs.

notify=1 will send all of your users an email that there is a new version. Set it to 0 to turn off.

Just like in the last step, try disabling all of the steps except for this one to see if your HockeyApp deployment works on its own. You don’t want to wait for Test Cloud to test this step in case your app token is wrong or it can’t find the ipa file.

We’re Done

Yep! Amazing! Try it out for yourself. Next time you make a commit, push it, and then go look in TeamCity and watch the status of the build. If it breaks, go check out the Build Log and try to troubleshoot it yourself. Tweet at me if you need some help and I’ll try to be of assistance if I can!

Enhancements

Once you have this basic build working, there are a few parting ideas.

  • Things like the device set, test series, and other things can be extracted to configuration parameters that you can pass into the build. That way you can “store” your device sets in these parameters and then access them in TeamCity using the % syntax, quickly letting you retarget the devices you’re testing on without needing to select them again in Test Cloud.
  • Including multiple commits in a build can keep your build times down.
  • Only run smoke tests in CI. You want quick feedback, but try to stick to the 20-minute promise. Keep the CI build short enough that you aren’t losing productivity. Test the most basic scenarios. Another good option is to chain a longer build onto successful CI builds that targets more tests or more scenarios, but you consider the build mostly good as long as the CI build passes. You can move current-interest scenarios into the Quick CI build to verify them a few times and then move them into the longer build later.
  • Use categories and the –category switch when uploading Xamarin UITests. Use cucumber profiles when uploading Calabash tests.
  • If you look at the Actions menu on your build configuration, you can copy it or even use it as a template for future builds.

With these tools in place and a great build strategy, you have the flexibility and the power you need to deploy your mobile apps continuously. Experiment with different combinations of test series, category selection, and device selection to bring your ideas to life, such as “I want a smoke test every check-in on six devices, and a weekly deployed build with all the tests on more devices.”

Once you see that first successful email from HockeyApp or this system finds your first bug, you’ll never go back!

Happy Dev-Opsing!

 

A Fun Little Sliding Overlay in Xamarin.Forms using AbsoluteLayout

my iphone is a music stand

Last year I took a few Skype lessons with someone who’s fairly well known in the guitar community: Rusty Cooley. He is incredibly skilled in the art of shredding, and he put me on a path that has enabled me to drastically improve my own skills through daily practice. This practice regimen is really simple, you don’t even need a metronome, just a timer.

My iPhone timer works okay for this, but I wanted to build something a little more purpose-oriented that doesn’t involve leaving the guitar to touch the phone and then reset to playing position.

Practice UI
This actually appears to be the A Ionian scale. It’s test data!

The design I came up with is pretty rough – I’m gonna make it prettier – but that’s because it took me forever to implement just the basic page structure. You’ve got some music, and a timer, and some instructions for how to do the exercise. The exercise instructions slide up over the music, and this was the UI component I was struggling to implement. In fact, it took me so long that I actually purchased (and allowed to lapse) an Apple Developer subscription during the course of the year I’ve been working on it. Finally, progress!

Practice UI with instructions expanded
Practice UI with instructions expanded

The exercise instructions animate up and down, and when the instructions are in the ‘collapsed’ position, it is docked to the top bar of the instructions.

So how did I accomplish this little sliding overlay? Let’s take a look.

Laying out a sliding overlay using xamarin.forms

I like to use XAML to lay out UI, personally. I like marking up layout stuff declaratively and then adding codebehind. I have a ton of web dev experience that makes this a natural paradigm for me, but you could certainly accomplish this in code as well. I do use the codebehind to set some initial positions and do some necessary calculations.

The page is loaded in a X.F NavigationPage. The ContentPage itself has one child – an AbsoluteLayout with no other attributes than a name (MainLayout) and a background color. The purpose of this absolute layout is so that its child elements can be anchored to some position within this parent AbsoluteLayout.

The MainLayout has two children:

  • A StackLayout that contains all of the controls at the top of the UI – timer, start, stop, title, Next Exercise button (all in a Grid), in addition to the music image, which is in a ScrollView. This layout contains anything you want to stay fixed. This StackLayout has its AbsoluteLayout.LayoutFlags set to All and its AbsoluteLayout.LayoutBounds set to fill the view with a value of "1,1,1,1"
  • Another AbsoluteLayout named aInstructions, which is responsible for holding the sliding panel of instructions. Its AbsoluteLayout.LayoutFlags (remember, this is in reference to the parent AbsoluteLayout, MainLayout) are set to All, and its initial LayoutBounds of "1,1,1,0.7" result in a partially expanded instructions panel. We overwrite this in code anyway.

So, the parent AbsoluteLayout and its two children, the StackLayout and AbsoluteLayout for instructions, are the three main components that you need to make this work. You can put anything you want in the StackLayout and anything you want in the child AbsoluteLayout and you will get similar behavior.

The code in the OnAppearing method for this page figures out how big the title bar of the instructions panel is and then translates the entire layout to the position where the title bar is docked to the bottom.

protected override void OnAppearing ()
{
	PopulateViewModel (exercises[currentIndex]);
			
	var actualHeight = instructionsTitleLayout.Height;
	drawerExpandedPosition = aInstructions.Bounds;
	drawerCollapsedPosition = aInstructions.Bounds;

	drawerCollapsedPosition.Y = aInstructions.Height - actualHeight;
	drawerExpandedPosition.Y = 0;

	aInstructions.TranslateTo (drawerCollapsedPosition.X, drawerCollapsedPosition.Y, 200, Easing.CubicOut);

	base.OnAppearing ();
}

The line aInstructions.TranslateTo(...) is what performs the actual animation, and I just move it back and forth between the collapsed and expanded positions of the drawer.

Here’s the basic skeleton code for the layout itself, in case it’s helpful to you. This is the code inside my ContentPage.Content tag. I’ve removed most of the contained controls and attribute specifics in order to make the actual page structure more clear.

<AbsoluteLayout BackgroundColor="White" x:Name="MainLayout">
	<StackLayout Orientation="Vertical" BackgroundColor="White" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All">
		<Grid Padding="10,0,10,0">
			<!-- Buttons and exercise title here -->
		</Grid>

		<ScrollView HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand">
			<Image x:Name="imgTab">
				<!-- Music image goes here -->
			</Image>
		</ScrollView>
	</StackLayout>

	<AbsoluteLayout AbsoluteLayout.LayoutBounds="1,1,1,0.7" AbsoluteLayout.LayoutFlags="All" BackgroundColor="#CC222222" x:Name="aInstructions">
		<StackLayout Orientation="Vertical" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All">
			<StackLayout Padding="0,0,20,0" x:Name="instructionsTitleLayout" Orientation="Horizontal" AbsoluteLayout.LayoutFlags="WidthProportional,PositionProportional" AbsoluteLayout.LayoutBounds="0,0,1,AutoSize" BackgroundColor="#CC111111">
				<Button x:Name="btnDoStuff" TextColor="White" Text="Show Exercise Instructions" StackLayout.HorizontalOptions="EndAndExpand"/>
			</StackLayout>
			<ScrollView HorizontalOptions="Fill" VerticalOptions="FillAndExpand" Padding="10">
				<Label TextColor="#CCC" x:Name="lblInstructions"></Label>
			</ScrollView>
		</StackLayout>
	</AbsoluteLayout>			
</AbsoluteLayout>

I like this sliding mechanic quite a bit. Understanding AbsoluteLayout has been a fun journey. Also notice in the declaration for instructionsTitleLayout, we have set the AbsoluteLayout.LayoutFlags to both WidthProportional and PositionProportional to ensure that I can properly align the button within that view. Without WidthProportional, the title bar would not scale all the way across the screen.