📜 ⬆️ ⬇️

Cross-platform programming for modern mobile Windows platforms

Relevance


In the world of mobile operating systems, the first two places are now shared by Android and iOS. According to different metrics and estimates of different companies, we can give first place to one operating system, then another, but there is no doubt that they are in the lead. But like at any Olympiad, we still have bronze. Let's try to decide on it.

Symbian, which led in all indicators a couple of years ago, gradually left the market. Blackberry is mostly business users, and mostly in America; in the rest of the world it is not so common. Trends show that Windows Phone is third. And here everyone, be it a private developer or a company, raises the question:


')


The answer is "Yes!", And now I will tell you why. My name is Vadim Balashov, I am a mail developer for Windows 8 and for Windows Phone, and I have been developing for mobile Windows since PocketPC 2003.

The share of sales of Windows Phone is growing. In 2012, it grew faster than in 2011. Especially if you look at the data for the past few months, when Windows Phone 8 came out, the growth there was even more dynamic . Moreover, according to IDC, which estimates the number of sold devices, in some countries, Windows Phone has surpassed the 10% mark in sales, and the share of sales in 26 countries has already overtaken Blackberry share, and in 7 countries even iOS . According to the forecasts of the same IDC, the total share of sales of Windows Phone at the moment is 3.2% , and in 4 years it will be 11.4%. There is no doubt that the platform is gaining momentum.



Now let's take a look at Windows 8. The market for desktop-systems is completely different, here there are slightly different estimates by the number of users. But even 7 months after the official launch of Windows 8, that is, at the beginning of June, it already takes 5.10% ; this is already more than the most popular Mac OS X 10.8.



The share of Windows 8 continues to grow at a steady pace:



Introduction


Hopefully, there is no longer any doubt about the prospects of developing for mobile Windows, and now it will be discussed how to develop for these two platforms simultaneously.

A little about the terminology. When it comes to WP7, there will be two versions of the platform - 7.5 and 7.8. In some cases, Windows Phone 8 will also be discussed separately, in which there are some differences from the previous version.

Worse things are with the terminology on Windows 8. In this article we will focus on its touch-component, which was first called Metro UI, then - Modern UI. In this case, the tablet version of the operating system, where there is only Modern UI, is called Windows RT, and the applications are called Store Apps (applications for the Windows Store). All this variety of terms appeared after the release of Windows 8, but actually it refers to the same thing, and in this article we will simply say “Windows 8”.

Story


In October 2010, Windows Phone 7 was introduced. It was actually the first product that implemented the Metro ideology. The basics of this ideology appeared a bit earlier in the Zune Player and in the X-Box, but there it was not yet fully formulated. Windows Phone 7 came out already with the “how to do”, “how to arrange” and “what not to do” guides. Therefore, it was she who set the trend. Next, historically, Windows 8 came out, which, due to the specifics of the desktop-systems, has its own characteristics: it is a big screen, a few other tasks assigned to applications. And the last was pulled Windows Phone 8, which, of course, fully incorporated the Windows Phone 7 API for backward compatibility of old applications, but at the same time partially absorbed the API from the Windows RT core, which is the basis of Store Apps.



In the figure above, the arrow shows the total API subset for all three systems. That is, using the API from this subset, you can develop code for three systems at the same time.

The advantages of a single project are obvious: it is a single implementation of business logic, a single functionality, a single user experience with a different user interface. That is, whether it is a phone or a tablet, the user, by doing the same actions, gets the same results and, of course, is satisfied. From a product point of view, this is a smaller total development time, since all three platforms are covered at once. Of course, time is spent more than on any of the three platforms separately, but the total time is less.
Among the shortcomings: the greater complexity at the beginning of the project - it is necessary to immediately deploy the entire architecture, which will be discussed below; during the support and development of the project you have a slightly more complex architecture. Accordingly, the project is harder to maintain, harder to introduce new developers. Also, sometimes you have to compromise on the functionality: if one of the platforms does not support something at all, then you need to either independently implement the missing functionality, if possible, or change the behavior of the program so that it fits within the limitations of all platforms.

MVVM


Now a little about the MVVM pattern. My colleagues from the iOS platform have twice asked me the question: “Why not a service locator?”. MVVM is probably the next step after the “service locator” ideology, where data and data processing methods are located together. Here they are separated.



Model is only data, i.e. the simplest classes that have sets of fields that store data, and possibly some simplest field processing or fields that are calculated based on other fields, for example, the FullName field, which itself concatenates the FirstName and LastName properties.

View is how the data looks in the interface, i.e. what users see them.

ViewModel is what connects View and Model, i.e. actually data processing and presentation in the UI. The ViewModel prepares data to display it correctly in the UI and modifies the models as a result of user actions.



Expand the schema, add Data Handler , a method that will conduct data preparation. LowLevel is the lowest level functions that cannot be made cross-platform. In this article, examples of such functions are the functions of working with the disk and the network.
Consider the work of this scheme on the example of the mail application. Take a counterclockwise direction and consider the case of receiving a letter:

Suppose the user really liked the letter, he wants to mark it with a flag. We go in the opposite direction:

Since we are talking about cross-platform development and platform-specific components, the first explicit dependent component is the View. Obviously, on tablet devices and on phones, our program should look different. The second dependent element is low-level functions. The specificity of the platforms forces us to use different functions: for example, work with local storage and data transmission over the network is implemented in different ways.

Platform-independent components, respectively, are Model, ViewModel and DataHandler.

Model , i.e. A letter that is on the phone, that on the tablet is the same. It comes from the server in one form; we have a specific set of fields to work with.

ViewModel is the processing of letter data and preparation for its display. If there is a need to wrap the body of the letter in HTML - it must be done on all platforms, albeit a little differently. Also, the reaction to user behavior should be uniform for all platforms to create a unified user experience.

Data Handlers - this is the preparation of data from the form in which they are transmitted over the network or stored on disk, in the model with which the work in the program. For example, if JSON comes to us from the server, then it comes on all platforms. We need to parse it and create a model. It always happens the same way.



In the figure, blue projects are platform-dependent, and orange, platform-independent projects, in order to visually represent the structure. In fact, in a circular circuit, only the extreme elements are platform-dependent, all middle elements are independent.

We now turn to a slightly more advanced scheme.



The left side presents platform-specific projects. These are projects for Windows Phone 7, Windows Phone 8 and Windows 8. The right side of the figure shows platform-independent projects: ViewModels, implementing the same logic for all programs, Models, storing data in a single form, and DataHandlers, processing data in a unified way. In fact, these are 6 projects in one Solution. LowLevel functions are implemented in each individual project in their own way, i.e. There are 3 different implementations. Further DataHandlers work with models, models with View models, and View models should work with View, that is, with the interface. Accordingly, since the interface in the three projects is different, ViewModels each work with a specific implementation of the View in each project.

Implementation of cross-platform MVVM


Now a little more about the implementation of cross-platform. In order for the ViewModel to notify the user interface that it has changed data and that these changes can be displayed, the ViewModel must inherit from the IPropertyChange interface.



Since in applications most often there is not one ViewModel, it is impractical to implement this interface in each ViewModel and it is better to define a single class ViewModelBase.

A special case is lists. For lists, it is convenient to use ObservableCollection, which automatically notifies the UI of changes in the list items.



The advantage of this container is that you do not need to separately notify the UI that something has changed. The disadvantage is that if you do a large number of operations, then View with you is updated too often, and this may affect the performance of the application.

Now the opposite situation - when you need to get a command from the ViewModel from the View, as is the case with flagging.



Such a command must be inherited from the ICommand interface. Here is the same situation - we create a basic CommandBase command and continue to work with it, because in any program, there are even more commands than ViewModels.

Now about data handlers. Data processors are actually committed to converting the raw data that we received through the network into models. And the opposite situation - convert the changes or commands that come from ViewModels (as in the case of setting the flag) into commands to the server.

The second case is working with the repository. All received letters should be saved so that next time they do not upload them over the network. Most likely, when saving data, the wrong message storage format is used, which is when transmitting over the network. To save the models, they are serialized into a stream or into an array of bytes and transferred to low-level functions.

Low-level functions are those platform-specific functions that cannot be made platform-independent. They need to be done as little as possible. They must carry a minimum of functionality that cannot really be made cross-platform. For example, for working with a disk / network, low-level objects should provide two functions SaveBytes / SendBytes and LoadBytes / ReceiveBytes. Any other functionality (integrity check, primary processing, etc.) should be transferred to the DataHandler.

In other words, to distinguish the LowLevel function from the general functionality, it is necessary to understand what exactly cannot be left in DataHandler. This minimum will be the LowLevel function.

Platform-specific components


Consider four platform-specific tasks: working with the network, working with the storage, a separate case of working with the storage (working with settings) and scheduling flows.

The portable class library contains the HttpRequest and HttpWebRequest classes. These classes are enough to fully implement the work with the server via HTTP. However, they do not have full functionality that could be used. For example, in the API for Windows 8 there is a class HttpClient, which supports traffic compression, and plus it makes it very convenient to work with POST requests. In fact, an object is passed to the class, and the object forms a POST request. In the case of HttpRequest, a POST request must be generated in accordance with the RFC, almost manually.

Therefore, I would recommend that you work with web services divided by platform to maximize the capabilities of each platform. This will require a little more work at the beginning, but your users will be satisfied, everything will work faster, more stable, and traffic will be saved.

Local storage. Windows Phone went the way of the iPhone, and all programs have access only to their IsolatedStorage, that is, one program does not have access to the data of another program. Working with files is done using the IsolatedStorageFile class.
Windows 8, due to the fact that it is a branch that came out of the desktop operating system, does not restrict access to the disk as hard as on the phone. To work with the disk there is a class ApplicationData.

An interesting situation develops with Windows Phone 8. Inheriting part of the API from Windows RT, Windows Phone 8 has access to the storage using both IsolatedStorageFile and ApplicationData. Where to store data depends entirely on you. If you are developing for all three platforms, then my advice to you is to use the same storage for Windows Phone, that is, IsolatedStorageFile. If you do not consider Windows Phone 7 due to some peculiarities as a platform for which you will develop an application, use ApplicationData, which is for Windows 8 and for Windows Phone 8.

Separate situation - when you need to store settings. In general, the storage of settings is no different from the storage of any other data, since settings are stored in the same disk storage. However, the system provides wrappers for settings that allow you to store key-value dictionaries without any extra effort. The situation is similar to disk storage: for Windows Phone there is IsolatedStorageSettings.ApplicationSettings, for Windows 8 - ApplicationData.Current.LocalSettings. Again, an interesting situation with Windows Phone 8: The API contains both classes for working with settings, but in practice, if we try to save the settings in LocalSettings, a NotImplementedException exception will be thrown. That is, we have a link to the settings class, but in practice there is nothing behind this link.



I would recommend working with the settings on your own: make your settings dictionary, serialize it with the serializer you like, and save it to a local disk.

Now a little about the thread manager. LowLevel functions in Windows Phone, in Windows 8 are implemented in such a way that even if you send a request to the network or to a disk in a UI stream, the answer will come to you anyway in the background thread. This is done so that long operations do not block the UI. This is convenient because the developer does not need to worry about creating background threads in which to access the network. At the same time, displaying data (which was received and already processed in some way) in the user interface is possible only in the UI stream. And here the question arises: "At what point does it move from the background thread to the UI thread?".

The transition to the UI stream is done via a call to Dispatcher, to which a delegate is sent informing the UI that the data is ready for display.



The problem is that the dispatcher is platform specific. Both Windows 8 and Windows Phone contain dispatcher, and the essence of their work is precisely in the fact that they execute the delegates transmitted from the background threads in the UI stream. But at the same time they are implemented in different ways: they are located in different NameSpaces, the treatment goes to methods with different names, they take different parameters.

Consider the moments in which we can do the dispatch. You can do implicit dispatching right away in the LowLevel function, which is also platform dependent: wrap the result of data processing in the dispatcher and transfer to the DataHandler already in the UI stream. Very convenient because we have DataHandler, Model, ViewModel - no one will know anything about streams, dispatcher, and so on, everything is simple and transparent.



Quickly in terms of design, but slow in terms of use. If you suddenly have enough data from the network, then until first they are first processed in the DataHandler, and then additional processing is possible in the ViewModel, the UI stream will be busy all this time, and the application will appear to hang. If at this moment some animations were performed in the application, they freeze, then continue after processing. From the user's point of view, the application looks hung.

In case of explicit dispatching, it is necessary to have access to the dispatcher from any ViewModel. The most convenient way to do this is in some ViewModelLocator, that is, a place that unites all models, and all models can have access to this locator. Thus, the primary processing of data, the formation of models and the preparation of data for display can be done in the background stream. The user interface is responsive, the progress bar runs, the spinner is spinning, the user understands that the program is busy, but not frozen. Exactly at the moment when we have the data ready, that is, before exiting the ViewModel, it is necessary to transfer control to the UI-stream and inform the interface that the data has been updated and can be displayed.



The easiest way to get cross-platform access to the dispatcher is to do an Action property in the ViewModelLocator, for example, DoDispatched and then initialize this property when the application starts.

For Windows Phone 7 and 8, it is initialized as follows:

DoDispatched = action => Dispatcher.BeginInvoke(action);

In the case of Windows 8, we are doing approximately the same thing, only, in accordance with the call rate, we additionally indicate the priority:

DoDispatched = action => Dispatcher.RunAsync(priority, action.Invoke);

From the distinction of calls, it is clearly seen that the dispatcher is strongly platform dependent, however wrapping it in an action ViewModel-and may not care about their difference. The ViewModel simply calls DoDispatched, passes the delegate inside to the UI thread, and that's it:

DoDispatched(() =>
{
…
}

Summarizing, we note that there are actually not so many platform-dependent components. To start developing cross-platform applications, it is enough to create a network query factory that makes platform-dependent work classes with data retrieval, create a class that abstracts work with local storage and work with settings, and access to the dispatcher. This is actually a universal tip for modern mobile applications that work with the cloud.

I hope that after reading this article you can already answer the question:

Source: https://habr.com/ru/post/185218/


All Articles