Cross Platform - James Montemagno https://montemagno.com/tag/cross-platform/ https://montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png Cross Platform - James Montemagno https://montemagno.com/tag/cross-platform/ Hugo -- gohugo.io Thu, 05 Nov 2026 09:24:41 GMT 60 More AI Agents, More CI: Reducing GitHub Actions Costs & Build Time https://montemagno.com/reducing-github-actions-costs-ai-agents/ https://montemagno.com/reducing-github-actions-costs-ai-agents/ Sat, 24 Oct 2026 19:00:00 GMT

AI coding agents are pretty awesome at helping me move projects forward. They can investigate an issue, make a change, and get a pull request ready while I am working on something else. But there is another part of that workflow that has to keep up: CI.

More agents means more branches, more pull requests, more updates to those pull requests, and more GitHub Actions jobs. Add Windows, Android, iOS, macOS, x64, and ARM64 into the mix, and one small change can turn into a whole lot of build machines doing work.

I could see it in my own usage throughout the year. The chart for my top repositories kept climbing, with a particularly noticeable jump toward the fall. That is the value of the Actions usage, not what I actually paid: much of it is included or covered by GitHub's free usage rules. Still, the growing compute footprint was a good reason to take a closer look.

Qualitative sketch of the supplied January-to-October repository usage chart. Several anonymized repository lines rise over the year, with the largest increases toward August through October. Dollar values and exact proportions are intentionally omitted; these are gross usage trends, not net billed charges.
The trend that prompted the audit. Qualitative redrawing of my top-repository usage chart, with amounts, exact proportions, and repository identities omitted. Gross usage value is not the amount billed; October is a partial month.

That was the problem I wanted to dig into across my apps. Not "how do I stop testing things?" Instead: how do I stop paying for, and waiting on, work that doesn't need to happen?

I used the GitHub Copilot app to analyze my GitHub usage export, inspect the workflows, and work through changes across repositories. Some wins were straightforward. One of the most useful discoveries was deleting caches we had just tried adding. That is right: a cache hit is not automatically a performance improvement!

The build matrix is a multiplier

Let's say an agent pushes three updates to a pull request, and each update starts four platform jobs. That is twelve jobs, before retries, release validation, or another agent working on another feature.

Parallel execution makes this easy to miss. Four five-minute jobs might finish in roughly five minutes if they start together, but they still consume about twenty runner-minutes. Queueing can make the wait longer without adding the same amount of execution time. A release waiting for approval is another thing entirely.

Those are different measurements:

Measurement What I want to learn
Runner execution time How much compute did the jobs actually use?
Pull request feedback time How long until I can act on a useful result?
Billed usage What remains chargeable after GitHub's billing rules and allowances?

The goal is to improve the right number, not just make a workflow page look shorter.

Start with the export, then follow it into the logs

My first prompt in the GitHub Copilot app was:

can you do an analysis of my actions across my repos running on windows and mac runners and see where we can optimize and cut costs

Then I attached the GitHub summarized usage CSV. My next message was simply:

Herei sm y usage report

Yes, typos and all. The useful part was supplying the data, not writing a magical prompt.

We looked at an October 1-24, 2026 snapshot containing 1,100 export records, reconciled it with GitHub's live billing API, and inspected 66 workflows across 14 repositories associated with Windows, macOS, or paid Actions usage. We also fetched 748 job records, not 748 independent workflow runs. That was complete for two paid hotspots, with selected representative runs elsewhere, not a complete performance census of every repository.

The export told us where to look. Workflow files and job logs told us what to change.

We separated setup, restore, compilation, tests, packaging, and cache transfer. We also checked whether a job really needed that operating system, whether superseded runs were still working, and whether "nothing changed" checks were allocating a native runner just to exit.

If your export includes gross usage, discounts, and net charges, keep those separate. An avoided dollar of nominal compute is not necessarily a dollar off your bill. I am deliberately not sharing my billing totals or repository-level billing breakdown here.

One more important limitation: a busy partial month is not a reliable annual forecast. It is an excellent place to find waste, though.

Put each job on the runner it actually needs

First things first: an Android build does not automatically need a Mac just because the same application also targets iOS.

For my FIRE calculator Android workflow, we moved from macos-15 to ubuntu-24.04, installed the Android-specific .NET MAUI workload, and configured the required JDK and Android SDK dependencies. We kept the required check name, tests, signing configuration, artifacts, and gates.

The hosted Android job completed in 2 minutes 35 seconds with all 447 tests passing. That confirms the Linux path worked; it is not a matched before-and-after speed benchmark. The signed AAB credential path was not exercised, and we did not dispatch a release to prove it.

Why move it? Here are the relevant GitHub standard runner list rates, checked October 24, 2026:

Runner billing SKU USD per minute Relative to Linux x64
Linux x64, 2-core $0.006 1.00x
Windows x64, 2-core $0.010 1.67x
macOS, M1 or Intel $0.062 10.33x
Published per-minute rates normalized to Linux x64 at 1: Windows x64 is 1.67 and macOS is 10.33.
Published list-rate comparison, not my bill or a hardware-performance comparison. Runner capabilities and runtimes differ.

At equal billed runtime, moving eligible work from that macOS SKU to Linux x64 reduces its nominal compute price by about 90.3%. That is a rate comparison, not a measured reduction in my total bill.

In fact, this example is a public repository: standard GitHub-hosted runners are free for public repositories. For private repositories, included allowances and discounts affect marginal savings. Larger runners have different rules and are not free just because a repository is public. GitHub also rounds each job's partial minutes up to whole minutes.

The practical lesson is still useful: keep Apple builds on a compatible Mac, Windows-specific packaging on Windows, and move portable work only after proving it works elsewhere.

Don't start an expensive machine to say "nothing changed"

Another discovery was in My Stream Timer. Its required Core test and x64/ARM64 matrix checks still needed to report results, including when a change did not affect the app.

Simply skipping an entire workflow is not a safe substitute for understanding your branch rules. Required checks can end up waiting for a result that never arrives.

Instead, we kept the check names and matrix shape. No-change checks run on Ubuntu; actual Windows work still runs on Windows.

A change detector routes unchanged app inputs to a Linux no-op required check and changed inputs to Windows builds and tests. Detection errors fail rather than reporting success.
Keep the required-check contract. Route the work, not the truth of the result.

There is a gotcha here: a failed change detector must not become a successful no-op. We checked invalid outputs, upstream failures, and cancellation as well as the happy path. Hosted Windows packages for both architectures, tests, and package validation passed. Seven live routing fixtures exercised the no-change and failure/cancellation paths.

This proves the routing behavior. We did not collect a controlled per-run latency benchmark, so I am not attaching a made-up number to it.

Cancel superseded CI, not releases

When an agent pushes a fix while the previous build is still running, do you need both ordinary CI runs to finish?

Usually, I want feedback for the newest commit. Workflow- and pull-request-scoped concurrency lets us stop spending time on an obsolete revision without having different workflows cancel each other.

Here is a small illustrative example for ordinary pull request CI, not a drop-in release policy:

on:
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
  cancel-in-progress: true

We added scoped cancellation to ordinary build workflows in Tiny Clips and OutroKit. Release publishing and deployment workflows were not the target.

Cancellation cannot refund work that already ran. Its payoff depends on how often updates overlap and how much execution remains when the new commit arrives. Measure that instead of assuming every canceled run saves its full duration.

Measure the cache, including moving the cache

This was my favorite part of the investigation.

In Tiny Clips, we tried separate NuGet download caches for direct x64 builds, direct ARM64 builds, Store builds, and tests. That sounds pretty reasonable. However, downloading and unpacking a cache takes time too.

For the direct builds, warm cache-plus-restore paths were 16 seconds slower for x64 and 10 seconds slower for ARM64 than direct restore without the cache. Even the Store cache, which improved over its cold-fill path, was about seven seconds slower warm than restore alone.

We removed all three build caches.

The test cache was different:

Test dependency setup path Observed time
Restore without the cache 32 seconds
Cold cache path, including its save 44 seconds
Warm cache path, including cache overhead 21 seconds
Tiny Clips test setup took 32 seconds without a cache, 44 seconds on the cold cache path, and 21 seconds warm. Warm saved 11 seconds versus no cache.
Observed setup timings, not total workflow durations. One cold/warm pair is useful evidence, not a repeated controlled benchmark.

That is an 11-second warm setup improvement, about 34% of this setup phase. Not a 34% faster application build! The retained test cache was about 0.45 GiB, compared with roughly 3.80 GiB across the initial cache experiment.

The decision rule is pretty simple:

Compare cache lookup, download, extraction, remaining restore, and post-job save against the no-cache restore. A green "cache hit" message is not the finish line.

We kept full platform coverage. Tiny Clips validation included Windows packages for both architectures and the Store flavor, 2,388 Core test cases with zero failures and four skips, 218 passing App tests, plus 398 Mac tests and a Mac App Store build.

In OutroKit, we retained fresh restores in each job and scoped NuGet download caches by OS, architecture, and dependencies. The full Linux, Windows, Intel Mac, and Apple Silicon Mac test matrix remained. All six jobs passed, and the pack job reused the exact build cache. That establishes correctness and reuse, not a controlled speedup measurement.

Follow-up: where does the Windows cache live?

After that experiment, Awake Coding's post on Windows .NET builds and NuGet cache placement gave us another thing to measure: where NuGet reads and writes locally, not whether to download an archive from actions/cache. Those are separate decisions!

We ran four fresh GitHub-hosted Windows x64 jobs each for Tiny Clips and my file converter: two default-location jobs and two with NuGet's global packages and HTTP caches under RUNNER_TEMP. Both caches started empty; remote Actions cache transfer was off. Within each app, source, SDK, workspace, toolchain, and commands stayed the same. We tested both cache paths together, not each variable's contribution independently.

Default caches were on C:, while temporary files and the workspace were on D:. All jobs used the same Windows image, but one temporary-location job in each pilot landed on newer EPYC 9V45/NVMe hardware, versus EPYC 7763/SAS for the other three. The faster-hardware jobs are excluded below. These are independent machines in the same broad CPU/storage class, not repeated paired lab measurements.

Cold-cache pilot Default restore, two jobs Temporary-location restore, one comparable-class job Whole experimental job, default → temporary
Tiny Clips 51-56 seconds 13 seconds 227-237 → 183 seconds
File converter 53-61 seconds 10 seconds 357-368 → 259 seconds

That is a promising cold-restore signal across WinUI and MAUI! Other phases barely changed: Tiny Clips' build/package step went from 138-144 seconds to 137; the converter's compilation from 48-52 to 46. Converter workload installation stayed around 20-22 seconds and tests around 41-43. This is separate from the isolated-SDK improvement described below; that setup was unchanged between the converter arms.

All four Tiny Clips jobs passed direct x64 build and MSIX inspection; this pilot did not run tests or ARM64 builds. All four converter jobs passed the Windows app build, native x64/ARM64 checks, and 15 Bridge plus 297 Core tests, with three expected Apple-only skips per job. Neither pilot exercised Store packaging or release/signing paths. Whole-job times include provisioning and diagnostics, not a promise of production CI duration. The Tiny Clips report and converter report preserve the experiment details.

Here is an illustrative GitHub-hosted Windows step, before SDK setup, remote cache restore, or any NuGet-consuming command:

- name: Put NuGet caches under runner temp
  if: runner.os == 'Windows' && runner.environment == 'github-hosted'
  shell: pwsh
  run: |
    $packages = Join-Path $env:RUNNER_TEMP 'nuget-packages'
    $http = Join-Path $env:RUNNER_TEMP 'nuget-http'
    New-Item -ItemType Directory -Force -Path $packages, $http | Out-Null
    "NUGET_PACKAGES=$packages" >> $env:GITHUB_ENV
    "NUGET_HTTP_CACHE_PATH=$http" >> $env:GITHUB_ENV

Verify your actual paths and disks first: RUNNER_TEMP does not guarantee a faster D: drive, especially on self-hosted machines. Preserve existing deliberate local/NVMe cache placement. If you also use actions/cache, point its package path at ${{ env.NUGET_PACKAGES }} and version the key when changing the cache layout, retaining OS, architecture, and dependency scoping. Do not remotely persist the HTTP cache or credential paths.

Faster feedback is not automatically a smaller bill. The comparable Tiny Clips jobs all round up to four runner-minutes; the converter observations round from six/seven to five. These public repositories use free standard hosted compute, so neither is a measured dollar saving. These pilots did not change production workflows. Tiny Clips' retained test cache was already on D:, and the converter uses a remote package cache in production: neither warm-path benefit was measured here.

My takeaway: try local placement for cold restores, then measure the whole job and any remote transfer separately. It does not overturn the decision to remove slower remote build caches, and these pilots are not included in the cost estimate below.

Fix the environment, not just the cache

Across other cross-platform apps, Windows MAUI workload installation was an obvious hotspot: roughly six and a half to seven minutes in representative jobs.

Tempting solution? Cache the workload folders and skip installation.

Not so fast. The logs showed MSI-managed components and machine-wide Windows Installer state. Restoring a directory is not equivalent to installing that toolchain correctly. We deferred workload-folder caching rather than trade a slow reliable build for a fast mysterious one.

But that did not mean giving up on setup. In my file converter, the preinstalled SDK was reconciling unrelated Visual Studio Android, Apple, and WebAssembly workloads when we only wanted maui-windows. Instead of caching that machine-wide state, we used actions/setup-dotnet with DOTNET_INSTALL_DIR pointing to a fresh directory under runner.temp. The isolated SDK uses file-based workload installation; we still install and verify the pinned workload explicitly. No registry edits, deleting installer markers, or pretending a folder restore is an installation.

The observed Windows workload-install step went from 5 minutes 55 seconds to 20 seconds, and the whole job from 10 minutes 39 seconds to 3 minutes 48 seconds. All seven hosted checks passed, including the Windows app build and native shell checks. These are observations across workflow revisions, not a controlled benchmark attributing every second to SDK isolation; Store packaging was not dispatched.

Observed Windows timings across workflow revisions: workload installation fell from 5 minutes 55 seconds to 20 seconds; whole job duration fell from 10 minutes 39 seconds to 3 minutes 48 seconds. Installation is part of the job, not an additional saving.
Observed hosted runs, not a controlled benchmark. Workload installation is included in total job time; do not add the two reductions together. Store packaging was not exercised.

Some optimizations should stay on the drawing board

Apple pipelines had a similar lesson: a small test body can sit inside a much longer compile, install, simulator startup, and test operation. Profile the whole operation before deciding that deleting tests is the answer.

In one iOS cache experiment, the warm run avoided recompiling unchanged Swift files and cut a portable test step from about two minutes to 17 seconds. Awesome, right? Except the whole workflow went from 11 minutes 15 seconds cold to 14 minutes 53 seconds warm. Setup and native-test overhead outweighed the compilation win. One pair is not a controlled benchmark, but it certainly does not justify claiming overall savings.

We rejected that iOS cache experiment rather than keep the overhead and complexity. The final proposal keeps earlier simulator startup and disables Homebrew's automatic update/cleanup, with all 52 tests preserved. Its uncached run took 11 minutes 27 seconds, near the audit median, so I am counting zero overall savings from it. Source-built media caching in my file converter also has no verified hosted warm-hit benefit yet.

For the converter, I also explicitly chose faster automatic PR checks with full Mac proof on demand. The initial full Mac job took 9 minutes 17 seconds; compile-only checks took 3 minutes 10 seconds in one run and 4 minutes 15 seconds in the latest. Those are observational timings on differing runner images, and this is an intentional change in coverage frequency, not a cache speedup or identical work running faster. Automatic Mac compilation/static guards remain; the manual media, sandbox, and runtime proof must run for the exact release candidate before Store approval. Store signing and approval boundaries stay separate.

The media cache also correctly missed when the runner image changed. That is a safety check doing its job, not evidence of a warm-cache speedup. A reusable media cache must contain verified unsigned source-built outputs, never signing credentials or signed app bundles.

The prompts were a starting point, not the quality gate

After the analysis, my next actual prompt was:

Let's send PRs for all of these please with optimizatons

In the GitHub Copilot app, that became separate, isolated coding sessions for the repositories. Each could inspect its own workflow, make changes, and bring back a pull request and hosted CI evidence. The useful part was being able to coordinate the results: keep the good cache, remove the slower ones, preserve the required checks, and challenge optimizations that were not safe.

For a repeatable version of this process, here are suggested follow-up prompts, not quotations from my original conversation:

Analyze this GitHub Actions usage export alongside the workflows
and representative job logs. Separate runner execution time,
feedback latency, gross usage, discounts, and net billed charges.
Identify the highest-impact changes and the evidence for each.
Do not publish the raw export or private billing details.
For each proposed optimization, preserve required check names,
platform coverage, signing boundaries, and release gates unless
I explicitly approve a behavior change. Explain what can run on
Linux, what must stay native, and how failures propagate.
Measure no-cache, cold-cache, and warm-cache setup, including
lookup, transfer, extraction, restore, and post-job save time.
Remove caches that do not beat the no-cache path. Report sample
size and limitations; do not claim overall workflow speedups
from setup-only measurements.
Validate with hosted CI and report exactly what passed, what was
not exercised, and whether the PR is open or merged. Do not
dispatch a signed release just to test an optimization.

I like this approach because the agent is doing the investigation and implementation, but the acceptance criteria are still concrete. "It should be faster" is not enough.

So, how much time and money does this save?

Putting the changes together, a reasonable planning scenario is roughly 45-51% less nominal compute cost and 10-20% fewer runner-minutes for the example workload below. That combines cheaper runners, less redundant execution, and the test cache that actually helped. It is an estimate informed by our tests, not a measured reduction across my account.

Illustrative combined estimate, with the original workload indexed to 100: nominal compute cost falls to about 49-55, and runner execution time falls to about 80-90. This models runner migration, avoided redundant execution, and test setup caching; it is not measured account savings or pull request latency.
Combined planning scenario, not my actual workload or bill. Lower is better; baseline = 100. Simulator/media-cache gains, Windows SDK isolation, and on-demand Mac proof are excluded from this model.

The assumptions: 100 five-minute jobs split 40 Linux, 20 Windows, and 40 Mac; move 20 eligible Mac jobs to Linux at equal runtime, then avoid 10-20% of execution proportionally through cancellation/no-change routing, net of routing overhead. Twenty of the original Linux jobs also get the observed test-setup cache benefit, assuming 80% warm hits and including cold-cache penalties, scaled down with the avoided work. That works out to about 52-102 runner-minutes saved per 100 jobs. The job mix and avoidance rate are illustrative assumptions, not measurements of my fleet.

Actual billed savings depend on free usage, allowances, and per-job rounding, which this approximate model does not calculate. Less total execution also does not mean every PR finishes 10-20% sooner. Still, it gives us a useful target to compare with the next usage report, without counting unproven optimizations as wins!

Make the next build smarter

The big takeaway for me is not to use fewer agents or stop building cross-platform apps. It is to make the CI graph as intentional as the code those agents are changing.

Start with your usage export. Follow the expensive work into job logs. Put portable work on the right runner, stop obsolete builds, keep required checks truthful, and make caches earn their place.

Then let the agents help with the cleanup, and ask them to bring back the evidence. More pull requests do not have to mean proportionally more wasted CI.

]]>
Cross Platform Settings for Everyone! https://montemagno.com/cross-platform-settings-for-everyone/ 598df8f2c121b60019bf3039 Wed, 12 Mar 2014 20:40:55 GMT If you haven’t figure it out by now, I simply love portable class libraries. I have been blogging about them, talking about them on Channel 9, and I even did a 18 city roadshow discussing PCLs and Azure Mobile Services.

On my GitHub I have started to build out a lot of applications, sometimes using MvvmCross and sometimes not. One of my most starred repos is my MvvmCross plugin for saving settings on Android, iOS, WP, and Win 8. My idea originally was simple: Use the native settings management system across all platforms. This means SharedPreferences, NSUserDefaults, IsolatedStorageSettings, and ApplicationDataContainer. I didn’t want to do anything too crazy to re-invent the wheel, so this means you can only store data values that the platform allows such as:

  • Boolean
  • Int64
  • String
  • Single
  • DateTime

Doing anything more then this to me means you should use some sort of blob storage such as Akavache. Since my library is very simple this means you can still use a PreferenceActivity on Android to get a nice native settings screen (this was my main motivation really). So it is great that I have MvvmCross support, but what about standard Xamarin projects?

Today, I am pleased to introduce a brand new GitHub repo called “Xam.PCL.Plugins”. This is a repo that is open source and will house all of my PCL NuGet Plugins for cross platform development. To start things off I am introducin my very first plugin, which as you might have guessed is cross platform settings! It is already available on NuGet for your app consumption :)

All you need to do is install the NuGet into both your PCL and client projects and you will be on your way! If you are interested in creating more plugins with me or converting MvvmCross plugins over simply fork my repo and send a pull request. You can use my settings project as an example of how to structure your project to work properly with PCLs. Additionally, PCLStorage is a great repo to also look at for starting development for a more advanced PCL.

]]>
Portable Class Library Tips and Tricks https://montemagno.com/portable-class-library-tips-and-tricks/ 598df8f2c121b60019bf303b Mon, 24 Feb 2014 17:38:00 GMT There has been a recent explosion of PCL use since Microsoft and Xamarin added official support for Xamarin.Android and Xamarin.iOS. It is really amazing to have one project that is shared completely across Android, iOS, and all of the Windows platforms. I have blogged before about how to get “fake” PCL support, but I wanted to give an updated post on how to get up and running with a bunch of tips as well.

It is important to remember that there is nothing special you need to do to get PCL support. If you copied over dlls or added xml files you should remove all of this and just use the Stable branches of Xamarin to get official support.

What is a PCL and what are profiles?

A PCL is a portable class library which is a project template inside of VS and XS which allows you to select platform targets that the project will be compatible with. This is amazing because you have one project that works everywhere. PCLs have actually been around since VS 2010, but they have always been locked into Windows specific platforms, but that all changed last November when Microsoft and Xamarin announced a major partnership which included PCL support for Xamarin.Android and Xamarin.iOS apps. PCLs use these things called “profiles”, which are actually supposed to be hidden from developers, but you will see them everywhere. Profile numbers are a peek into what platforms the PCL supports and how NuGet handles PCL support when installing. There are several parts to a profile such as net45, wp8, and now monotouch and monoandroid. PCLs work a bit interestingly which is that the DLss that are in the .NET subset are actually more like stub methods or interfaces that you are developing against. The DLL that gets used is the DLL that is included in the base project. This still puzzles me a bit and I am not a Microsoft PCL compiler expert like Daniel Plaisted, so you can ask him tons of questions on Twitter.

Tips & Tricks

  1. Remember that PCLs are platform independent. Not all APIs will be available to you.

  2. Don’t try to put platform specific code in a PCL, that is now what they are for. Abstract all platform specific code with interface that you implement on each platform

  3. PCL’s are great for large or small libraries, try to break your project up into small re-usable components. For instance I have a Settings PCL that is cross platform compatible that I reuse on every project. Growing a large PCL is fine, but try to think about how you can reuse code on more projects

  4. Choose only the targets you want to support. The more you select the smaller the subset of .NET you will get. If you select Windows Phone which doesn’t have HTTPClient you will not get access to it.

  5. Use NuGet to fill in the gaps. If you need to use a specific PCL profile be sure to search NuGet to see if there is something to fill in the missing functionality such as HTTPClient or PCL Storage.

  6. Recommendation is to leave out Silverlight and Windows Phone if you are not currently developing for these platforms as they will limite your APIs.

  7. This means use Profile 7 or Profile 78 for your projects based on your needs.

Setup

Here is a short guide on how to create your first PCL:

Visual Studio

Inside of Visual Studio it is easy to create a PCL by simply selecting File->New Project -> C# -> Portable Class Library.

When you select this you will see a dialog pop up asking you what platforms you wish to support.

That’s it you are good to go. As you can see they hide the “Profile” from you, but if you expand your References folder and select the .NET Subset DLL you will see in the path a specific profile that you are using.

Xamarin Studio

Similar to Visual Studio you will create a new PCL by selecting File->New Project -> C# -> Portable Library:

Now instead of popping up the target dialog like Visual Studio does you need to actually edit the project properties to specify the correct PCL Profile:

PCL/NuGet Issues

All PCLs and NuGets are not created equal and you will need to do some testing that is for sure. One issue that a lot of people might run into is with Json.NET that has an official Xamarin Component and also an official NuGet as well. While the NuGet does have official support for Xamarin.iOS and Xamarin.Android and issue does comes up if you are using profiles that include .NET 4.5, which is that Json.NET will install a newer version that is including System.Runtime.Dynamic, which is not supported in Xamarin.iOS. This means that that NuGet in general just will not work. There are a few work around for this and I recommend simply installing the Json.NET component in both the Android and iOS project and then manually referencing this DLL (iOS or Android it doesn’t matter) in your PCL. The only thing you will really miss is the DeserializeAsync, which you can create your self easily by wrapping it in a Task. This may change in the future as I have been working with James to get this fixed, but this can be the case with any NuGet out there so be careful.

I hope this helps you get up and running with PCLs. I love PCLs because they are awesome so always feel free to reach out to me on Twitter with questions.

]]>
Setting up your Cross Platform VM Environment (WP, Win8, Xamarin) https://montemagno.com/setting-up-your-cross-platform-vm-environment-wp/ 598df8f2c121b60019bf3042 Wed, 04 Dec 2013 19:09:00 GMT I am not going to lie, I have always been a Windows guy. There is just something about OSX and iOS that really bothered me. It as more of a feeling than specifics, but this means that I have always ran Windows on a PC. I never had to worry about VMs or complex cases. To be honest everything was perfect, I had VS 2013, Genymotion (android emulator), Windows Phone, Windows 8, and then for iOS I just used Xamarin.iOS and it’s VS integration to debug to a Mac nearby. I didn’t really have to interface with it at all. However, that has all changed as now my main device is this beautiful Mac Book Pro Retina with super specs (16 GB RAM, 512GB SSD, latest i7) and I love it. Well I love the hardware, still trying to get used to OSX. Anyways, I demo a lot and I still love developing inside of VS. Luckily, I can do all of this on the same machine using VMs, either VMWare or Parallels. Each have their own perks, price, and features, but they both seem to be great products for $60. However I did run into some hard times setting up my VM environment to get these following goals met:

  1. Windows 8.1

  2. Windows Phone SDK + Emulator (hyperv)

  3. Xamarin.iOS & Xamarin.Android

  4. Genymotion for Android emulator(virtual box)

The biggest issue here is that running VS + Xamarin is completely fine and easy to setup, however setting up awesome emulators is not. The main reason is that it requires nested virtualization. Each product has their own ways of dealing with it, but they aren’t easy to get setup.

Parallels:

To be honest this was pretty easy to get setup for the WP emulator. They have an easy to follow KB article to follow. Ensure that you make the VM have 2 processors and then you must manually setup each WP emulator to only have 1!! This is important as it can lead to crashes if you don’t (and I saw many crashes of the VM). The setup of Parallels with Windows was really simple, the only thing that bothers me is it tries to integrate to deeply with the host OSX with shared folders and apps. You can turn it all off, but you can just tell that there is too much going on.

VMWare:

After contacting VMWare via Twitter they sent me their KB article on how to set up hyper-v inside the VM. No matter what I did I could never get the WP SDK to install the WP emulators. I have always liked VMWare and the latest version is really nice. I like the setup process, settings screens and how it handles the integration with the host OSX. It feels more a self contained application running which I appreciate.

What about Genymotion?

In each VM platform to enable Genymotion it would require you to disable HyperV, reboot, and then run it. This would kill you WP emulator and is a pain to manage when trying to debug. Another option is to run Genymotion on your Mac and then Tunnel the connection from PC to mac, or in my case I find it easy to just share the code folder on the VM with my Mac, open it in Xamarin Studio and debug and run on the Mac. So this happens to work just fine with VMWare, however Parallels has a bug that VirtualBox can not run side by side with it. It will kill Parallels. So if you have a fast MAC the x86 android emulator is your next best step as it isn’t horrible.

Verdict:

I get the question of what is better between VMWare and Parallels and what should be used. If you need to do Windows Phone development then I recommend Parallels. I happen to actually like how VMWare looks and feels though so hopefully they will catch up. They aren’t that expensive so having them both installed isn’t so bad.

]]>