Page 50 - MSDN Magazine, April 2018
P. 50
• Several radio buttons, which let you choose your language (C#, VB.NET and so on)
• Two checkboxes: Add Namespace and Pascal Case
To generate the C# class, I set the Class Name to Photo and paste the following URL for the JSON Text or URL textbox: jsonplaceholder.typicode.com/photos/1. Finally, I enable the Pascal Case checkbox and click the Submit button. The generated Photo class now appears at the bottom of the page. Photo class (see companion code: Photos.Common/Model/Photo.cs) does not require addi- tional comments—it’s just composed of the auto-implemented
properties, representing the JSON object shown previously.
To implement the REST client, I create the static PhotoClient class (Photos.Common project). This class has one field of type HttpClient. This field is instantiated within the static construc- tor to set the BaseAddress property such that it points to the
JSONPlaceholder URL, as shown here:
private static HttpClient httpClient;
static PhotosClient() {
ArgumentNullException will be thrown (see companion code: Photos.Common/Helpers/Check.cs).
The shared functionality is now ready, so I can proceed to imple- ment the watchOS app.
The watchOS App and Its Structure
To create the actual watchOS app I first right-click the solution name (in this case Photos) under the Solution Explorer and then choose Add | Add New Project from the context menu. A New Project dialog appears, in which I click the following tab: watchOS | App (make sure you don’t use an Extension or Library tab). A list of project templates appears, from which I choose WatchKit App C# project template. Afterward, the list of configurable options will be presented, as shown in Figure 3.
As mentioned previously, every watchOS app has an associated parent iOS app. In this instance it’s the Photos.iOS app. Then, in the App Name textbox I type WatchKit, set the Target to watchOS 4.2 (note that the particular items in this list will depend on the installed SDK version) and uncheck all checkboxes under the Scenes group.
After clicking the Next button, Visual Studio will create two additional projects: Photos.iOS.WatchKit and Photos.iOS.Watch- KitExtension.
The first project (WatchKit) contains the storyboard, which you use to define scenes and segues (transitions) between them. The second project (WatchKitExtension) contains associated logic, including view controllers that in the watchOS are called interface controllers. So, you typically modify the UI of the watch app using storyboard designer (shown in Figure 4), which is activated by double-clicking the Interface.storyboard file of the WatchKit app.
Figure 2 Public Methods of the PhotoClient Class
httpClient = new HttpClient() {
BaseAddress = new Uri("https://jsonplaceholder.typicode.com/") };
}
As Figure 2 shows, the PhotosClient has two public methods: • GetByAlbumId: Achieves the collection of photos from the given album using the GetAsync method of the HttpClient class. After checking the HTTP response status code (Check- StatusCode), the resulting response is deserialized to a col- lection of C# Photo objects using a generic helper method DeserializeResponse (helper methods are discussed later). • GetImageData: Retrieves the byte array, representing the photo from the provided URL. To get the image data I use
GetByteArrayAsync of the HttpClient class instance. PhotosClient also implements two private methods: Check- StatusCode and DeserializeResponse. The first method accepts an instance of the HttpResponseMessage class and checks the value of its IsSuccessStatusCode property, throwing an exception if the
status code was different than 200 (success code).
private static void CheckStatusCode(HttpResponseMessage response) {
if (!response.IsSuccessStatusCode) {
throw new Exception($"Unexpected status code: {response.StatusCode}"); }
}
The second method, DeserializeReponse, also accepts an instance of the HttpResponseMessage class, but reads the message body as a string using HttpResponseMessage.Content.ReadAsStringAsync method. The resulting value is then passed to the static Deseri- alizeObject method of the Newtonsoft.Json.JsonConvert class. The latter returns the C# object of the given type, as shown here:
private static async Task<T> DeserializeResponse<T>(HttpResponseMessage response) {
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(jsonString); }
In Figure 2 I also use the IsNull method of the custom Check class. IsNull method performs simple argument validation to check whether argument is null. If so, an exception of type
public static async Task<IEnumerable<Photo>> GetByAlbumId(int albumId) {
var response = await httpClient.GetAsync($"photos?albumId={albumId}"); var photoCollection = new List<Photo>();
try {
CheckStatusCode(response);
photoCollection = await DeserializeResponse<List<Photo>>(response); }
catch(Exception ex) {
Console.WriteLine(ex.Message); }
return photoCollection; }
public static async Task<byte[]> GetImageData(Photo photo) {
var imageData = new byte[0];
try {
Check.IsNull(photo);
imageData = await httpClient.GetByteArrayAsync(photo.ThumbnailUrl); }
catch(Exception ex) {
Console.WriteLine(ex.Message); }
return imageData; }
44 msdn magazine
Visual Studio for Mac