Merging the Code-Base: Combining Existing Windows Store and Phone Apps into Universal Apps

  • Thread starter Thread starter Larry Lieberman
  • Start date Start date
L

Larry Lieberman




This post was written by Microsoft MVP Can Bilgin.
-----
This week we're featuring guest blog posts from some of the members of our Microsoft developer MVP program. Microsoft MVPs are passionate, expert community leaders who share their real-world knowledge and experience building solutions with Microsoft platforms.
Pavel Yosifovish, CTO at CodeValue; Can Bilgin, Senior Solutions Architect at CompuSight Corp.; and Rob Miles, a university lecturer, have all recently had experience implementing universal Windows apps, so we thought it would be especially useful to showcase the things they've learned and how they've been able to leverage much of the same code to deliver experiences across multiple device types.
The other two guest blogs in this series can be found here and here.

With the release of Windows Phone 8.1 and Windows 8.1, developers now have a single runtime that they can use to create “universal Windows apps.” This not only provides a little relief for management of the media assets used in apps for the two separate platforms, but also increases the reusability of feature sets, source code and ultimately know-how.
In order to make use of this opportunity, recently my team and I at CompuSight were tasked with combining our Adobe EchoSign e-signature app for the Windows Store and Windows Phone 8 into a universal app. Even though the implementation logic was inherently the same (considering the fact that the apps were heavily dependent on a service layer and the design scheme was an ordinary MVVM approach), the implementations differed on certain aspects like data management, platform interactions (e.g. content sharing, making use of the device peripherals etc.). However, the tools and templates provided for this very same purpose made this transition less painful.
In this post, I will share the steps we took to achieve this, highlighting some important moments from this implementation.
Preparing the Development Environment


The initial step was to create the universal app solution in our code repository. We decided to take the Windows 8 project and combine the two in this solution. Before the code restructuring the project looked something like the following: main client project, a Windows Runtime component for notifications and some maintenance work, good old unit tests, and the WCF service client implementation.

Figure 1 - Solution Explorer




The steps to take to convert this solution to a universal app solution were:
  1. Create a branch on source control (since this is a one way operation)
  2. Retarget the solution to Windows 8.1
  3. Add Windows Phone project (Conversion to Universal App)



Figure 2 - Retargeting the Windows Store


After retargeting the project, a simple compile, re-run of the unit tests, and a manual deploy and test was enough to verify that the app was “mostly” working.




Figure 3 - Add Windows Phone Project


After these steps, we had the windows store app retargeted to windows 8.1, an empty Windows Phone app created, and a shared project was in place where we could dump the code that is going to be shared between both projects.


NOTE: When you create a new universal app project the WINDOWS_APP compilation constant is automatically added to the Windows 8.1 project, and WINDOWS_PHONE_APP to the windows phone project. However, this is not the case when you choose to use an existing project. WINDOWS_APP constant might not be added to the windows project, you have to go to the build tab of project properties and add it manually. (Right Click on the Windows Store App project, select Properties and navigate to the Build tab) This might cause a lot of headache and bug chasing sessions in later steps.

Figure 4 - Add Conditional Compilation Symbol

Windows Phone 8 Project Conversion Work


Now that the universal app solution was ready, we had to do a little work to move the Windows Phone app to the new solution.
Following the same strategy, first we retargeted the phone app to Windows Phone 8.1 (this will create a Silverlight app). In essence, this step was for damage control so we could see which part of the app needs major work.

NOTE: The retarget operation is, unlike Windows Store Apps, on the project level, which means updating the referenced assemblies is your responsibility. After the retarget operation, you might (actually most definitely, if you have referenced projects or third-party packages) receive the following generic error if the referenced components are not supported on 8.1.
“Error 5 The "GenerateResource" task failed unexpectedly.
System.InvalidOperationException: Item named 'themes/generic.xaml' of
type 'System.IO.UnmanagedMemoryStream' cannot be added to the resource
file because it is not serializable.”


Once we made the required changes in the implementation, we were ready to move the code one step at a time to the universal app project.
Let the Sharing Begin


Before merging the projects, the main items moved to the shared project from the Windows Store app code were:
1) Template Generated items such as SuspensionManager, NavigationHelper, RelayCommand and ObservableDictionary. We chose to regenerate these items creating a new universal hub app, using the default template in Visual Studio 2013. The idea behind this was that both windows phone and windows store app projects were using these or similar implementations from the previous project templates. The easiest way to merge and reuse these items was to regenerate them for a universal app project.
2) Common XAML value converters such as BooleanNegationConverter, BooleanToVisibilityConverter, LowerToUpperCaseConverter, NullCheckToVisibilityConverter etc.
3) Resources files – You do not need to use resx files for globalization for Windows Phone 8.1 but you can reuse resw files for language specific resources just like Windows Store apps.
4) Data Model – The typed data implementation that is used to access the service layer which includes the data transfer model converters.
5) View Model – Completely transferable between the two apps as long as the required abstraction is in place between the data model and the business logic.
6) User Controls that can be shared between the projects (e.g. we were using a third party charting library for windows phone and an in-house developed one for Windows Store app; after the conversion the third party component was dropped).
7) Visual Assets such as images/icons that can be reused between the projects.
Identifying Platform Differences


As one can imagine, not everything goes according to plan while moving the assets to shared project. Even though this whole concept of universal apps is about platform convergence, it’s a work in progress which means we still have differences: some features are supported on one platform, or we sometimes have to do the same task differently because of different hardware capabilities.
Conditional Compilation


In these kind of cases, you, as a developer, can always resort to using the conditional compilation symbols we mentioned earlier (i.e. WINDOWS_APP, WINDOWS_PHONE_APP). These symbols when used together with #if, #else, #elif, #endif, let the compiler know which part of the code was designated for Windows Phone or Windows Store apps. This approach helps the developer keep the code in one place (i.e. shared project) and it is much easier to visualize code paths this way.

this.Page.Loaded += (sender, e) =>
{
#if WINDOWS_PHONE_APP
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#else
// Keyboard and mouse navigation only apply when occupying the entire window
if (this.Page.ActualHeight == Window.Current.Bounds.Height &&
this.Page.ActualWidth == Window.Current.Bounds.Width)
{
// Listen to the window directly so focus isn't required
Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated +=
CoreDispatcher_AcceleratorKeyActivated;
Window.Current.CoreWindow.PointerPressed +=
this.CoreWindow_PointerPressed;
}
#endif
};

Code Snippet 1 – Navigation Helper Page Loaded event handler


The code above is an excerpt from the Navigation Helper class. It makes use of the hardware button for the phone implementation while it is implementing a pointer pressed handler to handle the additional mouse button click events.
Let’s have a look at the following code. Series of event subscriptions regarding the use of the charms. Charms are only available in windows, and these events mostly will not be fired on a phone moreover they might not even be accessible.

//Search contract
var searchPane = SearchPane.GetForCurrentView();
searchPane.SuggestionsRequested += OnSearchPaneSuggestionsRequested;

//Settings contract
var settingsPane = SettingsPane.GetForCurrentView();
settingsPane.CommandsRequested += CommandsRequested;

//Sharing contract
var dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested += OnDataRequested;
Code Snippet 2 – App.xaml.cs Implementation for Windows 8.1


However, one thing here that could be accessible on Windows Phone 8.1 is the share contract and data transfer manager. Following the previous example the solution would be to make use of conditional compilation constants.

#if WINDOWS_APP
//Search contract
var searchPane = SearchPane.GetForCurrentView();
searchPane.SuggestionsRequested += OnSearchPaneSuggestionsRequested;

//Settings contract
var settingsPane = SettingsPane.GetForCurrentView();
settingsPane.CommandsRequested += CommandsRequested;
#endif

//Sharing contract
var dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested += OnDataRequested;

Code Snippet 3 – App.xaml.xs Implementation for Shared Project


NOTE: Files that are moved to the shared project, conditional sections can be visualized according to the platform that is selected from the drop-down on top-left corner.

Figure 5 - Platform Selection Drop-Down



Partial Classes


Same separation can also be implemented using partial class approach. Taking the same example from the previous section, the app class can be implemented as partial on platform dedicate projects as partial and shared implementation would be moved to the partial class that is created in the shared project.
If the app class is implemented in the shared project, we would create the partial class in both the windows phone and windows store app and implement a RegisterForContracts methods according the platform requirements. The implementation in the shared project, then, would look like below.

public partial class App
{
protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}

//
// Implemented in respective platform specific projects
RegisterForContracts();

Frame rootFrame = Window.Current.Content as Frame;

Code Snippet 4 – Partial Class Implementation

Portable Class Libraries


Another option we had at our disposal was the portable class libraries. It seemed like a perfect fit for the data transfer objects layer. This layer was originally included in the WCF client for windows phone and windows store app on separate projects and was referenced in the Data Model layer (which was moved to the shared project). However, because of the reasons that will be explained, we had no choice but to drop the use of service model communication for the Windows Phone project.
Portable class libraries can target different platforms such as desktop apps, Windows Store apps, Windows Phone apps, and Silverlight apps. The target options and supported features can be found on MSDN.
In order to reuse the data objects for the serialization (even though a soap service client was not used for windows phone, we still have to serialize/de-serialize JSON for RESTful calls), we decided to include this data definition layer in a portable library.
WCF vs REST


Don’t let the title mislead you; we still used WCF to expose a REST interface. The title refers to the fact that (currently) Windows Phone 8.1 apps do not offer the WCF namespaces. The only option left for the developers is to either implement the serialization and transport layer themselves or simply use a REST implementation with HttpClient. We decided to go the easy way since we already had a REST interface exposed at our disposal.


NOTE: The new and improved HttpClient in Windows Runtime supports many additional features. For complex scenarios such as network re-try, authentication/authorization brokering, logging and telemetry; developers now have the chance to use IHttpFilter interface to create modular code and create filter chains while making web requests. Another interesting feature in the Windows.Web.Http.Filters namespace is the HttpCacheControl which allows developers to set simple read and write cache control behaviors on the request channel.





Figure 6 - Service Clients Pre-Migration


Before we got into this endeavor, our service communication layer consisted of 3 main components: service proxy implementation, Common layer for data and service contracts and a singleton wrapper/provider layer that uses the actual service proxy. The wrapper implementation provided us with the much needed abstraction for using a WCF client for Windows Store app, and a REST client for Windows Phone. In this case, the provider simply would use the ISignatureService which was initialized according to the platform in the shared project (i.e. Partial implementation of the provider on platform dependent projects)



Figure 7 - Service Clients Post-Migration


A simple implementation used for the rest calls, was to extend the http client. The implementation can be enhanced with a third-party JSON serialization library but DataContractJsonSerializer is also available on the Phone projects.


public class SignatureClient : HttpClient
{
private static string BuildApiUrl(params string[] requestParameters)
{
string restUrl = string.Empty;
// TODO: Build the REST url according to the request
return restUrl;
}

public Task<EchoSignUser> GetUser(string userId)
{
return GetDto<EchoSignUser>(BuildApiUrl("GetUser", userId))
.ContinueWith(task => task.Result);
}

public async Task<T> GetDto<T>(string requestUri) where T : DtoEntity
{
var taskResponse = await GetAsync(requestUri);
var httpContent = taskResponse.Content;
using (var stream = await httpContent.ReadAsStreamAsync())
{
var obj = new DataContractJsonSerializer(typeof (T));
var result = obj.ReadObject(stream) as T;
return result;
}
}
}

Code Snippet 5 – Simple HttpClient implementation for a rest service channel.
Additional Features on Windows Phone


One of the main changes on Windows Phone solution was how the cross-app sharing was implemented. Before the migration, the only way for the app to take part in sharing was to use the share link task.

var shareLinkTask = new ShareLinkTask();
shareLinkTask.Title = "Completed Agreement for Contoso";
shareLinkTask.LinkUri = new Uri("http://agreementlink", UriKind.Absolute);
shareLinkTask.Message = "Agreement for Contoso signed by John Smith";
shareLinkTask.Show();
Code Snippet 6 – Share Link Task implementation on Windows Phone 8

However, now just like in the Windows Store app you can make use of the data transfer manager. The only difference is that the user cannot use the charm on Windows Phone, so an app bar button should be added to initialize the sharing task.


public void OnShareClicked(object sender, RoutedEventArgs args)

{

DataTransferManager.ShowShareUI();

}
Code Snippet 7 – Initialize Share on Windows Phone 8.1

Just like the share contract, the camera task and file pickers were replaced with contract implementations in the WinRT APIs.


m_CameraTask = new CameraCaptureTask();

m_PhotoChooseTask = new PhotoChooserTask();

m_CameraTask.Completed += (s, e) => PhotoTaskComplete(s, e, "Capture");

m_PhotoChooseTask.Completed += (s, e) => PhotoTaskComplete(s, e, "Image");
Code Snippet 8 – Chooser tasks for Windows Phone 8

And the file picker can be used now in Windows Phone as well as the Windows Store app.


async void btnNewDocument_Click(object sender, RoutedEventArgs e)

{

var openPicker = new FileOpenPicker

{

ViewMode = PickerViewMode.List;

};

// Add the filter extensions

var files = await openPicker.PickMultipleFilesAsync();

await ViewModel.AddAgreementDocuments(files.ToList());

}
Code Snippet 9 – File open picker
Also the capture task is almost identical to the Windows Store app implementation, making use of the Windows.Media.Capture namespace.


async void btnCapture_Click(object sender, RoutedEventArgs e)

{

var cameraCaptureUI = new MediaCapture();

var imgFormat = ImageEncodingProperties.CreateJpeg();

// create storage file in local app storage

StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(

"AgreementDocument.jpg", CreationCollisionOption.GenerateUniqueName);

await cameraCaptureUI.CapturePhotoToStorageFileAsync(imgFormat, file);

var list = new List<StorageFile> { file };

await CreateAgreementViewModel.AddAgreementDocuments(list);

}
Code Snippet 10 – Media Capture

Impressions


Overall, it has been an exciting journey converging our Windows Phone and Windows Store apps for Adobe EchoSign e-signature. It was not mentioned but many new features and controls replaced the custom implementation in the new apps. For instance, the hub control replaced custom GridView template on Windows Store app and panorama control on Windows Phone app. The PDF control for displaying completed e-signature documents replaced the launching a URL for download. WebView control being used to display EchoSign web console.






04d06506a4efd234e84e610b47697637._.gif


Continue reading...
 
Back
Top