HeightSquare – Partie 2 : Développement d’un composant Windows Runtime

HeightSquarePicker Cet article fait suite à une 1ère partie dans laquelle nous avons expliqué comment nous authentifier auprès des services Foursquare et plus généralement auprès de services qui implémentent le protocole OAuth.

Cette 2ème partie est un peu plus “Roots”, puisqu’elle est consacrée au développement de composants Windows Runtime dans laquelle nous répondons notamment à la question : comment Visual Studio génère un composant WinRT ? Nous terminons cet article par quelques notions d’asynchronisme avec notamment les types IAsyncInfo et IAsyncOperation<T>.

Comme je l’avais déjà précisé dans le 1er article, le code complet de cette application sera disponible à la fin de cette série, et donc au prochain article😉

Création et compilation d’un composant Windows Runtime

Lorsque l’on veut développer une API pour WinRT et la rendre accessible quel que soit le langage utilisé (C#, VB.NET, C++, Javascript), cette API doit être compilée en tant que composant Windows Runtime.

Avec Visual Studio rien de plus simple… La première étape consiste à créer un projet de type Windows Runtime Component :

HeightSquare1

Si vous avez travaillé avec les versions Developer ou Consumer Preview de VS2012 , ce type de projet n’existait pas. Il fallait créer un projet de type Class Library, puis dans les propriétés du projet, changer le type de sortie en WinMD.

Compilation d’un projet Windows Runtime

Le fait d’ajouter un projet de type Windows Runtime Component permet à Visual Studio de l’identifier en tant que tel, et de le compiler différemment des autres projets.

Si l’on ouvre le fichier csproj avec l’éditeur XML, on peut voir que le type de sortie est winmdobj :

image

 

Visual Studio va appeler le compilateur C# avec cet argument. On peut retrouver cet argument en ouvrant une invite de commande VS2012 et en consultant l’aide du compilateur via la commande csc.exe /? :

image

Ensuite Visual Studio va exporter le fichier winmdobj résultant de cette compilation en fichier winmd. Cette opération se fait via la tache MSBuild WinMdExp, que l’on retrouve dans la documentation de la nouvelle version de Windows.Build.Tasks .

Cette tache MSBuild génère en sortie un fichier d’extension WINMD. C’est ce type de fichier qui représente un composant Windows Runtime. Un composant avec une extension WINMD est réutilisable dans les applications de type Windows Store quel que soit le langage utilisé.

Un composant WinMD est pour les applications Windows Store ce qu’une assembly .Net est aux applications Desktop.

Un fichier WinMD contient le code compilé et les métadonnées. La MSDN nous précise que ce sont ces métadonnées qui ont un format différent entre .Net et WinRT. C’est donc la raison pour laquelle nous devons exporter la sortie du compilateur csc.exe vers un composant WinMD via WinMDExp.exe.

Prenons par exemple la classe SampleClass définie dans le fichier SampleClass.cs. Ouvrons une invite de commande Visual Studio 2012 et tapons la ligne de commande suivante : csc /target:winmdobj SampleClass.cs

image

Nous obtenons en sortie un fichier d’extension winmdobj :

image

 

Ensuite appelons l’outil winmdexp sur le fichier winmdobj. Il faut ajouter 3 références. 2 références .Net, mscorlib.dll et System.Runtime.dll, et une référence vers le Windows Runtime, Windows.winmd. Voici la ligne de commande à exécuter :

winmdexp.exe /reference: »%ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\mscorlib.dll » /reference: »%ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.dll »  /reference: »%ProgramFiles(x86)%\Windows Kits\8.0\References\CommonConfiguration\Neutral\Windows.winmd » SampleClass.winmdobj

image

Comme nous l’indique l’invite de commande, nous obtenons en sortie un fichier d’extension WINMD :

image

 

L’outil WinMDExp a exporté le code de notre classe dans un fichier de nom SampleWinMD.winmd. Il a simplement utilisé le nom de l’espace de noms racine dans lequel se trouve la classe.

En effet l’une des règles lorsque l’on écrit un composant WIndows Runtime est de mettre toutes les classes que nous exposons dans un même espace de noms racine. On peut bien évidemment les “ranger” dans des sous espaces de noms.

Maintenant que notre composant est compilé et exporté, nous pouvons, depuis un projet WinJS par exemple, l’ajouter en référence :

image

 

Et il ne reste plus qu’à appeler notre classe :

 

image

 

Quelques règles de développement d’un composant Windows Runtime

Nous allons ici détailler quelques règles de base. L’ensemble des règles à respecter lorsque vous développez une composant Windows Runtime est décrit dans cet article de la MSDN .

Exposition de classes “sealed”

Toutes les classes exposées (donc publiques) doivent être scellées (mot clé sealed). Vous pouvez bien évidemment définir ou implémenter des interfaces, mais il n’est pas possible d’utiliser les concepts de l’abstraction ou du polymorphisme par le biais de l’héritage de type pour les classes que vous exposez. Ceci est une restriction qui peut s’avérer assez contraignante, mais nous n’avons pas le choix…

Les types du Windows Runtime

Seuls les types du Windows Runtime, en plus des vôtres (qui respectent les règles) peuvent être exposés, que ce soit pour des valeurs de retour ou des paramètres d’entrée. Vous pouvez exposer certains types primitifs du Framework .Net, comme par exemple les Value types (en fait ils sont également définis dans le WinRT). Par exemple le type DateTime du Framework .Net ne peut pas être exposé depuis un composant WinRT. Il faudra dans ce cas utiliser le type DateTimeOffset. La classe ObservableCollection est également un type .Net et donc non exposable…

Dans cet article de la MSDN vous retrouvez la correspondance des types primitifs du Windows Runtime selon le langage utilisé.

Un autre exemple sur les types .Net que l’on ne peut pas exposer, c’est le type Task, que nous utilisons de plus en plus dans nos développements. Au lieu d’un objet de type Task, il faut exposer un objet de type IAsyncOperation. C’est pour cette raison qu’il existe aujourd’hui une méthode d’extension AsAsyncOperation() qui converti un objet Task en IAsyncOperation. L’inverse existe également, sur un objet IAsyncOperation on peut appeler la méthode AsTask. Thomas nous en parle dans ce post de blog (dans la langue de Shakespeare) et nous y reviendrons également dans la section suivante.

Limitations dans les surcharges des méthodes et des constructeurs

Le concept de surcharge d’une méthode en langage objet est le fait de définir une même méthode plusieurs fois avec des paramètres différents. La surcharge se définit sur le nombre et/ou sur le type des paramètres. Le type de retour reste identique d’une surcharge à l’autre.

Par exemple en C# on peut donc définir les surcharges suivantes :

  1. public void SampleMethod()
  2. {
  3. }
  4. public void SampleMethod( int i)
  5. {
  6. }
  7. public void SampleMethod( string i)
  8. {
  9. }
  10. public void SampleMethod( int i, int j)
  11. {
  12. }

 

Dans un composant Windows Runtime ce code ne compilera pas. En effet en javascript, on fait un peu ce que l’on veut en terme de paramètres… Par exemple, on peut appeler une fonction qui demande un “int” en entrée en lui passant une variable dans laquelle on a mis un “string”.

Si nous voulons persister à écrire ce genre de surcharge, nous devons indiquer quelle est la surcharge par défaut, c’est à dire celle qui sera appelée en cas de doute (en javascript donc…) :

  1. public void SampleMethod()
  2. {
  3. }
  4. [Windows.Foundation.Metadata. DefaultOverload ]
  5. public void SampleMethod( int i)
  6. {
  7. }
  8. public void SampleMethod( string i)
  9. {
  10. }
  11. public void SampleMethod( int i, int j)
  12. {
  13. }

 

Par contre cette règle ne s’applique pas aux constructeurs. Dans un composant WinRT il est tout simplement impossible de définir 2 constructeurs avec le même nombre d’arguments.

Pas de paramètre optionnel

Autre restriction, il n’est pas possible de définir de paramètre par défaut comme ci-dessous :

  1. public void SampleMethod( string s = « hello world » )
  2. {
  3. }

 

Dans ce cas il faudra utiliser des surcharges. Par exemple on peut utiliser les surcharges en “public” et le paramètre optionnel en “private” :

  1. public void SampleMethod()
  2. {
  3. PrivateSampleMethod();
  4. }
  5. public void SampleMethod( string s)
  6. {
  7. PrivateSampleMethod(s);
  8. }
  9. private void PrivateSampleMethod( string s = « Hello World » )
  10. {
  11. }

 

Un peu d’asynchronisme pour terminer

WindowsRuntimeSystemExtensions et les Mondes parallèles…

Nous le disions un peu plus haut, lorsque l’on développe un composant WIndows Runtime et que nous voulons exposer des méthodes asynchrones nous ne pouvons pas retourner un objet de type Task ou Task<TResult>. Nous devons utiliser les types IAsyncAction et IAsyncOperation<TResult> (et IAsyncActionWithProgress<TProgress> et IAsyncOperationWithProgress<TResult, TProgress> pour supporter la mise à jour de la progression).

Pour passer du monde .Net au monde WinRT il faut jeter un œil du côté de la classe WindowsRuntimeSystemExtensions. Cette classe définie des méthodes d’extension aux types Task, Task<T>, IAsyncInfo et IAsyncOperation<T>. On retrouve ces méthodes d’extension dans la classe WindowsRuntimeSystemExtensions :

image

Petite parenthèse au passage… on peut remarquer que des méthodes d’extension GetAwaiter existent pour les types IAsyncAction et IAsyncOperation<T>. C’est grâce à cette méthode qu’il nous est possible d’utiliser le mot clé await sur les composants WinRT. En effet souvenez-vous, les composants WinRT asynchrones ne retournent pas des objets de type Task ou Task<T> mais des objets de type IAsyncAction ou IAsyncOperation<T>. Or contrairement à l’objet Task qui possède bien une méthode GetAwaiter(), les 2 interfaces WinRT n’en définissent pas.

Pour information, l’IntelliSense ne vous montrera pas ces méthodes sur les types IAsyncAction et IAsyncOperation<T> car elles sont marquées par l’attribut EditorBrowsable(EditorBrowsableState.Never) :

  1. [ EditorBrowsable ( EditorBrowsableState .Never)]
  2. public static TaskAwaiter GetAwaiter( this IAsyncAction source)
  3. {
  4. return source.AsTask().GetAwaiter();
  5. }

 

Cet attribut a pour effet de cacher le membre aux différents éditeurs de Visual Studio, et notamment à l’éditeur de code. Cela ne nous empêche pas de l’appeler :

image

 

Donc finalement si nous avons une méthode qui nous retourne déjà un objet de type Task, il nous suffit de l’appeler, récupérer la tache résultante, et appeler la méthode AsAsyncOperation() (ou AsAsyncAction()).

L’exemple ci-dessous encapsule dans une méthode privée l’authentification OAuth aux services Foursquare et retourne un objet Task et une autre méthode publique est chargé de retourner l’objet IasyncOperation :

  1. private async static Task < string > AuthenticateTask( string clientId, string callbackUri)
  2. {
  3. string foursquareUriFormat = « https://foursquare.com/oauth2/authenticate?client_id={0}&response_type=token&redirect_uri={1} » ;
  4. Uri oauthFoursquareUri = new Uri ( string .Format(foursquareUriFormat, clientId, Uri .EscapeDataString(callbackUri)));
  5. Uri callbackFoursquareUri = new Uri (callbackUri);
  6. var authenticateFoursquareResult = await WebAuthenticationBroker .AuthenticateAsync( WebAuthenticationOptions .None, oauthFoursquareUri, callbackFoursquareUri);
  7. if (authenticateFoursquareResult != null && authenticateFoursquareResult.ResponseStatus == Windows.Security.Authentication.Web. WebAuthenticationStatus .Success)
  8. {
  9. Uri tokenFoursquareUri = new Uri (authenticateFoursquareResult.ResponseData.ToString());
  10. WwwFormUrlDecoder decoder = new WwwFormUrlDecoder (tokenFoursquareUri.Fragment);
  11. string accessToken = decoder.GetFirstValueByName( « #access_token » );
  12. return accessToken;
  13. }
  14. else
  15. throw new Exception ( « Authenticate fail… » );
  16. }
  17. public static IAsyncOperation < string > AuthenticateAsync( string clientId, string callbackUri)
  18. {
  19. return AsyncInfo .Run< string >((token) =>
  20. {
  21. return AuthenticateTask(clientId, callbackUri);
  22. });
  23. }

La classe AsyncInfo

La classe AsyncInfo nous offre une autre possibilité. Elle possède une méthode statique Run qui facilite la création d’objets IAsyncAction et IAsyncOperation. Elle supporte également l’annulation et la progression. Si on reprend l’exemple de l’authentification vu précédemment, on obtient le code suivant :

  1. public static IAsyncOperation < string > AuthenticateAsync( string clientId, string callbackUri)
  2. {
  3. return AsyncInfo .Run< string >((token) =>
  4. {
  5. return AuthenticateInternalAsync(clientId, callbackUri);
  6. });
  7. }

AsAsyncOperation() et AsyncInfo.Run : Même combat…

Que doit-on utiliser, y-a-t-il un choix plus judicieux que l’autre ?

Et bien pas vraiment, car les 2 font la même chose. Elles utilisent un objet de type TaskToAsyncOperationAdapter (ou TaskToAsyncActionAdapter) pour encapsuler la tâche. Ci-dessous la méthode AsAsyncOperation() :

  1. public static IAsyncOperation <TResult> AsAsyncOperation<TResult>( this Task <TResult> source)
  2. {
  3. if (source == null )
  4. {
  5. throw new ArgumentNullException ( « source » );
  6. }
  7. return new TaskToAsyncOperationAdapter<TResult>(source, null );
  8. }

 

Et ici la méthode AsyncInfo.Run :

  1. public static IAsyncOperation <TResult> Run<TResult>( Func < CancellationToken , Task <TResult>> taskProvider)
  2. {
  3. if (taskProvider == null )
  4. {
  5. throw new ArgumentNullException ( « taskProvider » );
  6. }
  7. return new TaskToAsyncOperationAdapter<TResult>(taskProvider);
  8. }

 

Finalement notre API Foursquare…

Nous savons maintenant implémenter un composant Windows Runtime asynchrone et réutilisable quel que soit le langage.

Et donc voici le code de notre API Foursquare :

  1. public sealed class FoursquareService
  2. {
  3. private const string versionDate = « 20120828 » ;
  4. public static IAsyncOperation < string > AuthenticateAsync( string clientId, string callbackUri)
  5. {
  6. return AsyncInfo .Run< string >((token) =>
  7. {
  8. return AuthenticateTask(clientId, callbackUri);
  9. });
  10. }
  11. public static IAsyncOperation < Contact > GetMyUserDetailsAsync( string token)
  12. {
  13. return GetUserDetailsTask(token).AsAsyncOperation();
  14. }
  15. public static IAsyncOperation < Contact > GetUserDetailsAsync( string token, string userId)
  16. {
  17. return GetUserDetailsTask(token, userId).AsAsyncOperation();
  18. }
  19. /// <summary>
  20. /// Returns a history of checkins for the authenticated user
  21. /// </summary>
  22. /// <param name= »token »> Access token </param>
  23. /// <returns></returns>
  24. public static IAsyncOperation < IEnumerable < Checkin >> GetMyCheckinsAsync( string token)
  25. {
  26. return GetCheckinsTask(token).AsAsyncOperation< IEnumerable < Checkin >>();
  27. }
  28. /// <summary>
  29. /// Returns a history of checkins for the authenticated user
  30. /// </summary>
  31. /// <param name= »token »> Access token </param>
  32. /// <param name= »limit »> Number of results to return, up to 250 </param>
  33. /// <returns></returns>
  34. public static IAsyncOperation < IEnumerable < Checkin >> GetMyCheckinsAsync( string token, int limit)
  35. {
  36. return GetCheckinsTask(token, « self » , limit).AsAsyncOperation< IEnumerable < Checkin >>();
  37. }
  38. /// <summary>
  39. ///
  40. /// </summary>
  41. /// <param name= »token »> Access token </param>
  42. /// <param name= »limit »> Number of results to return, up to 250 </param>
  43. /// <param name= »offset »> The number of results to skip. Used to page through results. </param>
  44. /// <returns></returns>
  45. public static IAsyncOperation < IEnumerable < Checkin >> GetMyCheckinsAsync( string token, int limit, int offset)
  46. {
  47. return GetCheckinsTask(token, « self » , limit, offset).AsAsyncOperation< IEnumerable < Checkin >>();
  48. }
  49. /// <summary>
  50. /// Returns badges for authenticated user
  51. /// </summary>
  52. /// <param name= »token »> Access token </param>
  53. /// <returns></returns>
  54. public static IAsyncOperation < IEnumerable < Badge >> GetMyBadgesAsync( string token)
  55. {
  56. return GetBadgesTask(token).AsAsyncOperation();
  57. }
  58. /// <summary>
  59. /// Returns badges for a given user
  60. /// </summary>
  61. /// <param name= »token »> Access token </param>
  62. /// <param name= »userId »> ID for user to view badges for </param>
  63. /// <returns></returns>
  64. public static IAsyncOperation < IEnumerable < Badge >> GetBadgesAsync( string token, string userId)
  65. {
  66. return GetBadgesTask(token, userId).AsAsyncOperation();
  67. }
  68. /// <summary>
  69. /// Returns friends of authenticated user
  70. /// </summary>
  71. /// <param name= »token »> Access token </param>
  72. /// <returns></returns>
  73. public static IAsyncOperation < IEnumerable < Contact >> GetMyFriendsAsync( string token)
  74. {
  75. return GetFriendsTask(token).AsAsyncOperation();
  76. }
  77. /// <summary>
  78. /// Returns friends of authenticated user
  79. /// </summary>
  80. /// <param name= »token »> Access token </param>
  81. /// <param name= »limit »> Number of results to return, up to 500. </param>
  82. /// <returns></returns>
  83. public static IAsyncOperation < IEnumerable < Contact >> GetMyFriendsAsync( string token, int limit)
  84. {
  85. return GetFriendsTask(token, « self » , limit).AsAsyncOperation();
  86. }
  87. /// <summary>
  88. /// Returns friends of authenticated user
  89. /// </summary>
  90. /// <param name= »token »> Access token </param>
  91. /// <param name= »limit »> Number of results to return, up to 500 </param>
  92. /// <param name= »offset »> Used to page through results </param>
  93. /// <returns></returns>
  94. public static IAsyncOperation < IEnumerable < Contact >> GetMyFriendsAsync( string token, int limit, int offset)
  95. {
  96. return GetFriendsTask(token, « self » , limit, offset).AsAsyncOperation();
  97. }
  98. /// <summary>
  99. /// Returns a list of a user’s friends
  100. /// </summary>
  101. /// <param name= »token »> Access token </param>
  102. /// <param name= »userId »> Identity of the user to get friends of </param>
  103. /// <returns></returns>
  104. public static IAsyncOperation < IEnumerable < Contact >> GetFriendsAsync( string token, string userId)
  105. {
  106. return GetFriendsTask(token, userId).AsAsyncOperation();
  107. }
  108. /// <summary>
  109. /// Returns a list of a user’s friends
  110. /// </summary>
  111. /// <param name= »token »> Access token </param>
  112. /// <param name= »userId »> Identity of the user to get friends of </param>
  113. /// <param name= »limit »> Number of results to return, up to 500 </param>
  114. /// <returns></returns>
  115. public static IAsyncOperation < IEnumerable < Contact >> GetFriendsAsync( string token, string userId, int limit)
  116. {
  117. return GetFriendsTask(token, userId, limit).AsAsyncOperation();
  118. }
  119. /// <summary>
  120. /// Returns a list of a user’s friends
  121. /// </summary>
  122. /// <param name= »token »> Access token </param>
  123. /// <param name= »userId »> Identity of the user to get friends of </param>
  124. /// <param name= »limit »> Number of results to return, up to 500 </param>
  125. /// <param name= »offset »> Used to page through results </param>
  126. /// <returns></returns>
  127. public static IAsyncOperation < IEnumerable < Contact >> GetFriendsAsync( string token, string userId, int limit, int offset)
  128. {
  129. return GetFriendsTask(token, userId, limit, offset).AsAsyncOperation();
  130. }
  131. /// <summary>
  132. /// Returns mayorships of authenticated user
  133. /// </summary>
  134. /// <param name= »token »> Access token </param>
  135. /// <returns></returns>
  136. public static IAsyncOperation < IEnumerable < Venue >> GetMyMayorshipsAsync( string token)
  137. {
  138. return GetMayorshipsTask(token).AsAsyncOperation();
  139. }
  140. /// <summary>
  141. /// Returns a user’s mayorships
  142. /// </summary>
  143. /// <param name= »token »> Access token </param>
  144. /// <param name= »userId »> Identity of the user to get mayorships for </param>
  145. /// <returns></returns>
  146. public static IAsyncOperation < IEnumerable < Venue >> GetMayorshipsAsync( string token, string userId)
  147. {
  148. return GetMayorshipsTask(token, userId).AsAsyncOperation();
  149. }
  150. /// <summary>
  151. /// Returns the leaderboard of authenticated user
  152. /// </summary>
  153. /// <param name= »token »> Access token </param>
  154. /// <returns></returns>
  155. public static IAsyncOperation < IEnumerable < LeaderboardItem >> GetLeaderboardAsync( string token)
  156. {
  157. return GetLeaderboardTask(token).AsAsyncOperation();
  158. }
  159. /// <summary>
  160. /// Returns the leaderboard of authenticated user
  161. /// </summary>
  162. /// <param name= »token »> Access token </param>
  163. /// <param name= »neighbors »> Number of friends’ scores to return that are adjacent to your score, in ranked order. The current user’s score is returned as well </param>
  164. /// <returns></returns>
  165. public static IAsyncOperation < IEnumerable < LeaderboardItem >> GetLeaderboardAsync( string token, int neighbors)
  166. {
  167. return GetLeaderboardTask(token, neighbors).AsAsyncOperation();
  168. }
  169. public static IAsyncOperation < IEnumerable < Checkin >> GetRecentCheckinsByFriends( string token)
  170. {
  171. return GetRecentCheckinsByFriendsTask(token).AsAsyncOperation();
  172. }
  173. public static IAsyncOperation < IEnumerable < Checkin >> GetRecentCheckinsByFriends( string token, int limit)
  174. {
  175. return GetRecentCheckinsByFriendsTask(token, limit).AsAsyncOperation();
  176. }
  177. public static IAsyncOperation < IEnumerable < Checkin >> GetRecentCheckinsByFriends( string token, int limit, double latitude, double longitude)
  178. {
  179. return GetRecentCheckinsByFriendsTask(token, limit, latitude, longitude).AsAsyncOperation();
  180. }
  181. public static IAsyncOperation < IEnumerable < Checkin >> GetRecentCheckinsByFriends( string token, double latitude, double longitude)
  182. {
  183. return GetRecentCheckinsByFriendsTask(token, null , latitude, longitude).AsAsyncOperation();
  184. }
  185. public static IAsyncOperation < IEnumerable < Contact >> FindUserAsync( string token, string name)
  186. {
  187. return FindUsersTask(token, name).AsAsyncOperation();
  188. }
  189. public static IAsyncOperation < IEnumerable < Contact >> FindUsersAsync( string token, string email)
  190. {
  191. return FindUsersTask(token, null , email).AsAsyncOperation();
  192. }
  193. public static IAsyncOperation < IEnumerable < Venue >> FindVenuesAsync( string token, double latitude, double longitude)
  194. {
  195. return FindVenuesTask(token, latitude, longitude, null ).AsAsyncOperation();
  196. }
  197. [ DefaultOverload ]
  198. public static IAsyncOperation < IEnumerable < Venue >> FindVenuesAsync( string token, double latitude, double longitude, int limit)
  199. {
  200. return FindVenuesTask(token, latitude, longitude, null , limit).AsAsyncOperation();
  201. }
  202. public static IAsyncOperation < IEnumerable < Venue >> FindVenuesAsync( string token, double latitude, double longitude, string queryVenue)
  203. {
  204. return FindVenuesTask(token, latitude, longitude, queryVenue).AsAsyncOperation();
  205. }
  206. public static IAsyncOperation < IEnumerable < Venue >> FindVenuesAsync( string token, double latitude, double longitude, string queryVenue, int limit)
  207. {
  208. return FindVenuesTask(token, latitude, longitude, queryVenue, limit).AsAsyncOperation();
  209. }
  210. public static IAsyncOperation < IEnumerable < Venue >> FindVenuesAsync( string token, double latitude, double longitude, string queryVenue, int limit, int radius)
  211. {
  212. return FindVenuesTask(token, latitude, longitude, queryVenue, limit, radius).AsAsyncOperation();
  213. }
  214. private async static Task < string > AuthenticateTask( string clientId, string callbackUri)
  215. {
  216. string foursquareUriFormat = « https://foursquare.com/oauth2/authenticate?client_id={0}&response_type=token&redirect_uri={1} » ;
  217. Uri oauthFoursquareUri = new Uri ( string .Format(foursquareUriFormat, clientId, Uri .EscapeDataString(callbackUri)));
  218. Uri callbackFoursquareUri = new Uri (callbackUri);
  219. var authenticateFoursquareResult = await WebAuthenticationBroker .AuthenticateAsync( WebAuthenticationOptions .None, oauthFoursquareUri, callbackFoursquareUri);
  220. if (authenticateFoursquareResult != null && authenticateFoursquareResult.ResponseStatus == Windows.Security.Authentication.Web. WebAuthenticationStatus .Success)
  221. {
  222. Uri tokenFoursquareUri = new Uri (authenticateFoursquareResult.ResponseData.ToString());
  223. WwwFormUrlDecoder decoder = new WwwFormUrlDecoder (tokenFoursquareUri.Fragment);
  224. string accessToken = decoder.GetFirstValueByName( « #access_token » );
  225. return accessToken;
  226. }
  227. else
  228. throw new Exception ( « Authenticate fail… » );
  229. }
  230. private async static Task < Contact > GetUserDetailsTask( string token, string userId = « self » )
  231. {
  232. string uriFormat = « https://api.foursquare.com/v2/users/{0}?v={1}&oauth_token={2} » ;
  233. Uri uri = new Uri ( string .Format(uriFormat, userId, versionDate, token));
  234. HttpClient client = new HttpClient ();
  235. var response = await client.GetAsync(uri);
  236. if (!response.IsSuccessStatusCode)
  237. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  238. string jsonResult = await response.Content.ReadAsStringAsync();
  239. JsonValue jsonValue;
  240. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  241. throw new Exception ( « Unable to parse checkins response » );
  242. var userJson = jsonValue.GetObject()[ « response » ].GetObject()[ « user » ].GetObject();
  243. Contact contact = DeserializeUser(userJson);
  244. return contact;
  245. }
  246. private async static Task < IEnumerable < Venue >> FindVenuesTask( string token, double latitude, double longitude, string queryVenue, int ? limit = null , int ? radius = null )
  247. {
  248. string queryParam = string .IsNullOrEmpty(queryVenue) ? «  » : « &query= » + queryVenue;
  249. string limitParam = limit.HasValue ? « &limit= » + limit : «  » ;
  250. string radiusParam = radius.HasValue ? « &radius= » + radius : «  » ;
  251. string ll = latitude + « , » + longitude;
  252. string uriFormat = « https://api.foursquare.com/v2/venues/search?v={0}&ll={1}{2}{3}{4}&oauth_token={5} » ;
  253. Uri uri = new Uri ( string .Format(uriFormat, versionDate, ll, queryParam, limitParam, radiusParam, token));
  254. HttpClient client = new HttpClient ();
  255. var response = await client.GetAsync(uri);
  256. if (!response.IsSuccessStatusCode)
  257. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  258. string jsonResult = await response.Content.ReadAsStringAsync();
  259. JsonValue jsonValue;
  260. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  261. throw new Exception ( « Unable to parse checkins response » );
  262. var venues = new List < Venue >();
  263. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « venues » ].GetArray().AsParallel())
  264. {
  265. var venueItem = item.GetObject();
  266. var venue = DeserializeVenue(venueItem);
  267. venues.Add(venue);
  268. }
  269. return venues;
  270. }
  271. private async static Task < IEnumerable < Contact >> FindUsersTask( string token, string name, string email = null )
  272. {
  273. string nameParam = string .IsNullOrEmpty(name) ? «  » : « &name= » + name;
  274. string emailParam = string .IsNullOrEmpty(email) ? «  » : « &email= » + email;
  275. string uriFormat = « https://api.foursquare.com/v2/users/search?v={0}{1}{2}&oauth_token={3} » ;
  276. Uri uri = new Uri ( string .Format(uriFormat, versionDate, nameParam, emailParam, token));
  277. HttpClient client = new HttpClient ();
  278. var response = await client.GetAsync(uri);
  279. if (!response.IsSuccessStatusCode)
  280. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  281. string jsonResult = await response.Content.ReadAsStringAsync();
  282. JsonValue jsonValue;
  283. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  284. throw new Exception ( « Unable to parse checkins response » );
  285. List < Contact > users = new List < Contact >();
  286. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « results » ].GetArray().AsParallel())
  287. {
  288. var contactItem = item.GetObject();
  289. var contact = new Contact ();
  290. contact.Id = contactItem[ « id » ].GetString();
  291. if (contactItem.ContainsKey( « firstName » ))
  292. contact.FirstName = contactItem[ « firstName » ].GetString();
  293. if (contactItem.ContainsKey( « lastName » ))
  294. contact.LastName = contactItem[ « lastName » ].GetString();
  295. if (contactItem.ContainsKey( « photo » ))
  296. {
  297. var photoItem = contactItem[ « photo » ].GetObject();
  298. var prefix = photoItem[ « prefix » ].GetString();
  299. var suffix = photoItem[ « suffix » ].GetString();
  300. contact.Photo = prefix + suffix;
  301. }
  302. if (contactItem.ContainsKey( « gender » ))
  303. contact.Gender = contactItem[ « gender » ].GetString();
  304. if (contactItem.ContainsKey( « homeCity » ))
  305. contact.HomeCity = contactItem[ « homeCity » ].GetString();
  306. if (contactItem.ContainsKey( « bio » ))
  307. contact.HomeCity = contactItem[ « bio » ].GetString();
  308. var contactInfoItem = contactItem[ « contact » ].GetObject();
  309. if (contactInfoItem.ContainsKey( « email » ))
  310. contact.Email = contactInfoItem[ « email » ].GetString();
  311. if (contactInfoItem.ContainsKey( « facebook » ))
  312. contact.Facebook = contactInfoItem[ « facebook » ].GetString();
  313. if (contactInfoItem.ContainsKey( « twitter » ))
  314. contact.Twitter = contactInfoItem[ « twitter » ].GetString();
  315. var tipsItem = contactItem[ « tips » ].GetObject();
  316. contact.CountTips = Convert .ToInt32(tipsItem[ « count » ].GetNumber());
  317. users.Add(contact);
  318. }
  319. return users;
  320. }
  321. private async static Task < IEnumerable < Checkin >> GetRecentCheckinsByFriendsTask( string token, int ? limit = null , double ? latitude = null , double ? longitude = null )
  322. {
  323. string limitParam = limit.HasValue ? « &limit= » + limit : «  » ;
  324. string ll = latitude.HasValue && longitude.HasValue ? « &ll= » + latitude + « , » + longitude : «  » ;
  325. string uriFormat = « https://api.foursquare.com/v2/checkins/recent?v={0}{1}{2}&oauth_token={3} » ;
  326. Uri uri = new Uri ( string .Format(uriFormat, versionDate, ll, limitParam, token));
  327. HttpClient client = new HttpClient ();
  328. var response = await client.GetAsync(uri);
  329. if (!response.IsSuccessStatusCode)
  330. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  331. string jsonResult = await response.Content.ReadAsStringAsync();
  332. JsonValue jsonValue;
  333. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  334. throw new Exception ( « Unable to parse checkins response » );
  335. List < Checkin > checkins = new List < Checkin >();
  336. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « recent » ].GetArray())
  337. {
  338. var checkinItem = item.GetObject();
  339. Checkin checkin = new Checkin ();
  340. //checkinItem[« distance »]
  341. // checkinItem[« isMayor »]
  342. // checkinItem[« createdAt »]
  343. // checkinItem[« shoot »]
  344. checkin.Id = checkinItem[ « id » ].GetString();
  345. if (checkinItem.ContainsKey( « distance » ))
  346. checkin.Distance = Convert .ToInt32(checkinItem[ « distance » ].GetNumber());
  347. if (checkinItem.ContainsKey( « isMayor » ))
  348. checkin.IsMayor = checkinItem[ « isMayor » ].GetBoolean();
  349. if (checkinItem.ContainsKey( « shoot » ))
  350. checkin.Shoot = checkinItem[ « shoot » ].GetString();
  351. if (checkinItem.ContainsKey( « createdAt » ))
  352. {
  353. var offset = checkinItem[ « timeZoneOffset » ].GetNumber();
  354. var da = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan .FromSeconds(offset));
  355. da = da.AddSeconds(checkinItem[ « createdAt » ].GetNumber());
  356. da = da.ToLocalTime();
  357. checkin.CreatedAt = da;
  358. }
  359. if (checkinItem.ContainsKey( « venue » ))
  360. {
  361. var venueItem = checkinItem[ « venue » ].GetObject();
  362. checkin.Venue = DeserializeVenue(venueItem);
  363. }
  364. if (checkinItem.ContainsKey( « user » ))
  365. {
  366. var userItem = checkinItem[ « user » ].GetObject();
  367. checkin.User = DeserializeUser(userItem);
  368. }
  369. checkins.Add(checkin);
  370. }
  371. return checkins;
  372. }
  373. private async static Task < IEnumerable < Checkin >> GetCheckinsTask( string token, string userId = « self » , int ? limit = null , int ? offset = null )
  374. {
  375. List < Checkin > checkins = new List < Checkin >();
  376. string limitParam = limit.HasValue ? « &limit= » + limit.Value : «  » ;
  377. string offsetParam = offset.HasValue ? « &offset= » + offset.Value : «  » ;
  378. string uriFormat = « https://api.foursquare.com/v2/users/{0}/checkins?v={1}{2}{3}&oauth_token={4} » ;
  379. Uri uri = new Uri ( string .Format(uriFormat, userId, versionDate, limitParam, offsetParam, token));
  380. HttpClient client = new HttpClient ();
  381. var response = await client.GetAsync(uri);
  382. if (!response.IsSuccessStatusCode)
  383. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  384. string jsonResult = await response.Content.ReadAsStringAsync();
  385. JsonValue jsonValue;
  386. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  387. throw new Exception ( « Unable to parse checkins response » );
  388. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « checkins » ].GetObject()[ « items » ].GetArray().AsParallel())
  389. {
  390. var checkinItem = item.GetObject();
  391. Checkin checkin = new Checkin ();
  392. checkin.Id = checkinItem[ « id » ].GetString();
  393. var venueItem = checkinItem[ « venue » ].GetObject();
  394. checkin.Venue = DeserializeVenue(venueItem);
  395. checkins.Add(checkin);
  396. }
  397. return checkins;
  398. }
  399. private async static Task < IEnumerable < Contact >> GetFriendsTask( string token, string userId = « self » , int ? limit = null , int ? offset = null )
  400. {
  401. string limitParam = limit.HasValue ? « &limit= » + limit.Value : «  » ;
  402. string offsetParam = offset.HasValue ? « &offset= » + offset.Value : «  » ;
  403. string uriFormat = « https://api.foursquare.com/v2/users/{0}/friends?v={1}{2}{3}&oauth_token={4} » ;
  404. Uri uri = new Uri ( string .Format(uriFormat, userId, versionDate, limitParam, offsetParam, token));
  405. HttpClient client = new HttpClient ();
  406. var response = await client.GetAsync(uri);
  407. if (!response.IsSuccessStatusCode)
  408. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  409. string jsonResult = await response.Content.ReadAsStringAsync();
  410. JsonValue jsonValue;
  411. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  412. throw new Exception ( « Unable to parse checkins response » );
  413. List < Contact > contacts = new List < Contact >();
  414. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « friends » ].GetObject()[ « items » ].GetArray().AsParallel())
  415. {
  416. var contactItem = item.GetObject();
  417. var contact = new Contact ();
  418. contact.Id = contactItem[ « id » ].GetString();
  419. if (contactItem.ContainsKey( « firstName » ))
  420. contact.FirstName = contactItem[ « firstName » ].GetString();
  421. if (contactItem.ContainsKey( « lastName » ))
  422. contact.LastName = contactItem[ « lastName » ].GetString();
  423. if (contactItem.ContainsKey( « photo » ))
  424. {
  425. var photoItem = contactItem[ « photo » ].GetObject();
  426. var prefix = photoItem[ « prefix » ].GetString();
  427. var suffix = photoItem[ « suffix » ].GetString();
  428. contact.Photo = prefix + « original » + suffix;
  429. }
  430. if (contactItem.ContainsKey( « gender » ))
  431. contact.Gender = contactItem[ « gender » ].GetString();
  432. if (contactItem.ContainsKey( « homeCity » ))
  433. contact.HomeCity = contactItem[ « homeCity » ].GetString();
  434. if (contactItem.ContainsKey( « bio » ))
  435. contact.HomeCity = contactItem[ « bio » ].GetString();
  436. var contactInfoItem = contactItem[ « contact » ].GetObject();
  437. if (contactInfoItem.ContainsKey( « email » ))
  438. contact.Email = contactInfoItem[ « email » ].GetString();
  439. if (contactInfoItem.ContainsKey( « facebook » ))
  440. contact.Facebook = contactInfoItem[ « facebook » ].GetString();
  441. if (contactInfoItem.ContainsKey( « twitter » ))
  442. contact.Twitter = contactInfoItem[ « twitter » ].GetString();
  443. var tipsItem = contactItem[ « tips » ].GetObject();
  444. contact.CountTips = Convert .ToInt32(tipsItem[ « count » ].GetNumber());
  445. contacts.Add(contact);
  446. }
  447. return contacts;
  448. }
  449. private async static Task < IEnumerable < Badge >> GetBadgesTask( string token, string userId = « self » )
  450. {
  451. string uriFormat = « https://api.foursquare.com/v2/users/{0}/badges?v={1}&oauth_token={2} » ;
  452. Uri uri = new Uri ( string .Format(uriFormat, userId, versionDate, token));
  453. HttpClient client = new HttpClient ();
  454. var response = await client.GetAsync(uri);
  455. if (!response.IsSuccessStatusCode)
  456. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  457. string jsonResult = await response.Content.ReadAsStringAsync();
  458. JsonValue jsonValue;
  459. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  460. throw new Exception ( « Unable to parse checkins response » );
  461. List < Badge > badges = new List < Badge >();
  462. var badgeItems = jsonValue.GetObject()[ « response » ].GetObject()[ « badges » ].GetObject();
  463. foreach ( var badgeKey in badgeItems.Keys)
  464. {
  465. var badgeItem = badgeItems[badgeKey].GetObject();
  466. var badge = new Badge ();
  467. badge.Id = badgeItem[ « id » ].GetString();
  468. badge.BadgeId = badgeItem[ « badgeId » ].GetString();
  469. badge.Name = badgeItem[ « name » ].GetString();
  470. if (badgeItem.ContainsKey( « unlockMessage » ))
  471. badge.UnlockMessage = badgeItem[ « unlockMessage » ].GetString();
  472. if (badgeItem.ContainsKey( « description » ))
  473. badge.Description = badgeItem[ « description » ].GetString();
  474. if (badgeItem.ContainsKey( « hint » ))
  475. badge.Description = badgeItem[ « hint » ].GetString();
  476. var imageItem = badgeItem[ « image » ].GetObject();
  477. string prefix = imageItem[ « prefix » ].GetString();
  478. string name = imageItem[ « name » ].GetString();
  479. badge.ImageUri = prefix + « 114 » + name;
  480. if (! string .IsNullOrEmpty(badge.Name))
  481. badges.Add(badge);
  482. }
  483. return badges;
  484. }
  485. private async static Task < IEnumerable < Venue >> GetMayorshipsTask( string token, string userId = null )
  486. {
  487. string userParam = userId ?? « self » ;
  488. string uriFormat = « https://api.foursquare.com/v2/users/{0}/mayorships?v={1}&oauth_token={2} » ;
  489. Uri uri = new Uri ( string .Format(uriFormat, userParam, versionDate, token));
  490. HttpClient client = new HttpClient ();
  491. var response = await client.GetAsync(uri);
  492. if (!response.IsSuccessStatusCode)
  493. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  494. string jsonResult = await response.Content.ReadAsStringAsync();
  495. JsonValue jsonValue;
  496. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  497. throw new Exception ( « Unable to parse checkins response » );
  498. List < Venue > venues = new List < Venue >();
  499. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « mayorships » ].GetObject()[ « items » ].GetArray().AsParallel())
  500. {
  501. var venueItem = item.GetObject()[ « venue » ].GetObject();
  502. var venue = DeserializeVenue(venueItem);
  503. venues.Add(venue);
  504. }
  505. return venues;
  506. }
  507. private async static Task < IEnumerable < LeaderboardItem >> GetLeaderboardTask( string token, int ? neighbors = null )
  508. {
  509. string uriFormat = « https://api.foursquare.com/v2/users/leaderboard?v={0}{1}&oauth_token={2} » ;
  510. string neighborsCount = neighbors.HasValue ? « &neighbors= » + neighbors.Value.ToString() : «  » ;
  511. Uri uri = new Uri ( string .Format(uriFormat, versionDate, neighborsCount, token));
  512. HttpClient client = new HttpClient ();
  513. var response = await client.GetAsync(uri);
  514. if (!response.IsSuccessStatusCode)
  515. throw new Exception ( « Error when requested Foursquare :  » + response.ReasonPhrase);
  516. string jsonResult = await response.Content.ReadAsStringAsync();
  517. JsonValue jsonValue;
  518. if (! JsonValue .TryParse(jsonResult, out jsonValue))
  519. throw new Exception ( « Unable to parse checkins response » );
  520. List < LeaderboardItem > leaderboard = new List < LeaderboardItem >();
  521. foreach ( var item in jsonValue.GetObject()[ « response » ].GetObject()[ « leaderboard » ].GetObject()[ « items » ].GetArray())
  522. {
  523. var lboardJson = item.GetObject();
  524. var leaderboardItem = new LeaderboardItem ();
  525. leaderboardItem.Rank = Convert .ToInt32(lboardJson[ « rank » ].GetNumber());
  526. var userJson = lboardJson[ « user » ].GetObject();
  527. leaderboardItem.User = new Contact ();
  528. leaderboardItem.User.Id = userJson[ « id » ].GetString();
  529. if (userJson.ContainsKey( « firstName » ))
  530. leaderboardItem.User.FirstName = userJson[ « firstName » ].GetString();
  531. if (userJson.ContainsKey( « lastName » ))
  532. leaderboardItem.User.LastName = userJson[ « lastName » ].GetString();
  533. leaderboardItem.User.Me = userJson[ « relationship » ].GetString() == « self » ;
  534. if (userJson.ContainsKey( « photo » ))
  535. {
  536. var photoItem = userJson[ « photo » ].GetObject();
  537. var prefix = photoItem[ « prefix » ].GetString();
  538. var suffix = photoItem[ « suffix » ].GetString();
  539. leaderboardItem.User.Photo = prefix + « original » + suffix;
  540. }
  541. var scoreJson = lboardJson[ « scores » ].GetObject();
  542. leaderboardItem.Score = new Score ();
  543. leaderboardItem.Score.Recent = Convert .ToInt32(scoreJson[ « recent » ].GetNumber());
  544. leaderboardItem.Score.Max = Convert .ToInt32(scoreJson[ « max » ].GetNumber());
  545. leaderboardItem.Score.CheckinsCount = Convert .ToInt32(scoreJson[ « checkinsCount » ].GetNumber());
  546. leaderboard.Add(leaderboardItem);
  547. }
  548. return leaderboard;
  549. }
  550. private static Contact DeserializeUser( JsonObject userJson)
  551. {
  552. Contact contact = new Contact ();
  553. contact.Id = userJson[ « id » ].GetString();
  554. if (userJson.ContainsKey( « firstName » ))
  555. contact.FirstName = userJson[ « firstName » ].GetString();
  556. if (userJson.ContainsKey( « lastName » ))
  557. contact.LastName = userJson[ « lastName » ].GetString();
  558. contact.Me = userJson[ « relationship » ].GetString() == « self » ;
  559. if (userJson.ContainsKey( « photo » ))
  560. {
  561. var photoItem = userJson[ « photo » ].GetObject();
  562. var prefix = photoItem[ « prefix » ].GetString();
  563. var suffix = photoItem[ « suffix » ].GetString();
  564. contact.Photo = prefix + « original » + suffix;
  565. }
  566. if (userJson.ContainsKey( « gender » ))
  567. contact.Gender = userJson[ « gender » ].GetString();
  568. if (userJson.ContainsKey( « homeCity » ))
  569. contact.HomeCity = userJson[ « homeCity » ].GetString();
  570. if (userJson.ContainsKey( « bio » ))
  571. contact.Bio = userJson[ « bio » ].GetString();
  572. if (userJson.ContainsKey( « contact » ))
  573. {
  574. var contactInfoItem = userJson[ « contact » ].GetObject();
  575. if (contactInfoItem.ContainsKey( « email » ))
  576. contact.Email = contactInfoItem[ « email » ].GetString();
  577. if (contactInfoItem.ContainsKey( « facebook » ))
  578. contact.Facebook = contactInfoItem[ « facebook » ].GetString();
  579. if (contactInfoItem.ContainsKey( « twitter » ))
  580. contact.Twitter = contactInfoItem[ « twitter » ].GetString();
  581. }
  582. if (userJson.ContainsKey( « tips » ))
  583. {
  584. var tipsItem = userJson[ « tips » ].GetObject();
  585. contact.CountTips = Convert .ToInt32(tipsItem[ « count » ].GetNumber());
  586. }
  587. return contact;
  588. }
  589. private static Venue DeserializeVenue( JsonObject venueItem)
  590. {
  591. Venue venue = new Venue ();
  592. venue.Id = venueItem[ « id » ].GetString();
  593. venue.Name = venueItem[ « name » ].GetString();
  594. var locationItem = venueItem[ « location » ].GetObject();
  595. venue.Location = DeserializeLocation(locationItem);
  596. var statsItem = venueItem[ « stats » ].GetObject();
  597. venue.VenueStatistics.CheckinsCount = Convert .ToInt32(statsItem[ « checkinsCount » ].GetNumber());
  598. venue.VenueStatistics.TipCount = Convert .ToInt32(statsItem[ « tipCount » ].GetNumber());
  599. venue.VenueStatistics.UsersCount = Convert .ToInt32(statsItem[ « usersCount » ].GetNumber());
  600. var categoryItems = venueItem[ « categories » ].GetArray();
  601. foreach ( var catItem in categoryItems)
  602. {
  603. var categoryItem = catItem.GetObject();
  604. var category = new VenueCategory ();
  605. category.Id = categoryItem[ « id » ].GetString();
  606. if (categoryItem.ContainsKey( « icon » ))
  607. {
  608. var iconJson = categoryItem[ « icon » ].GetObject();
  609. var prefix = iconJson[ « prefix » ].GetString();
  610. var suffix = iconJson[ « suffix » ].GetString();
  611. if (prefix.EndsWith( « _ » ))
  612. prefix = prefix.Substring(0, prefix.Length – 1);
  613. category.Icon = prefix + suffix;
  614. }
  615. category.Name = categoryItem[ « name » ].GetString();
  616. category.PluralName = categoryItem[ « pluralName » ].GetString();
  617. category.ShortName = categoryItem[ « shortName » ].GetString();
  618. venue.VenueCategory.Add(category);
  619. }
  620. return venue;
  621. }
  622. private static Location DeserializeLocation( JsonObject locationItem)
  623. {
  624. var location = new Location ();
  625. if (locationItem.ContainsKey( « city » ))
  626. location.City = locationItem[ « city » ].GetString();
  627. if (locationItem.ContainsKey( « address » ))
  628. location.Address = locationItem[ « address » ].GetString();
  629. if (locationItem.ContainsKey( « postalCode » ))
  630. location.PostalCode = locationItem[ « postalCode » ].GetString();
  631. location.Country = locationItem[ « country » ].GetString();
  632. location.CountryCode = locationItem[ « cc » ].GetString();
  633. location.Latitude = locationItem[ « lat » ].GetNumber();
  634. location.Longitude = locationItem[ « lng » ].GetNumber();
  635. if (locationItem.ContainsKey( « state » ))
  636. location.State = locationItem[ « state » ].GetString();
  637. return location;
  638. }
  639. }

 

Et ci-dessous un exemple d’utilisation de cette API.

  1. var token = await FoursquareService .AuthenticateAsync( App .clientId, App .callbackFoursquareUri);
  2. var friendsTask = FoursquareService .GetMyFriendsAsync(token, 9).AsTask();
  3. var badgesTask = FoursquareService .GetMyBadgesAsync(token).AsTask();
  4. var checkinsTask = FoursquareService .GetMyCheckinsAsync(token, 5).AsTask();
  5. var userDetailsTask = FoursquareService .GetMyUserDetailsAsync(token).AsTask();
  6. var leaderBoardTask = FoursquareService .GetLeaderboardAsync(token, 5).AsTask();
  7. var recentTask = FoursquareService .GetRecentCheckinsByFriends(token, 5).AsTask();
  8. var mayorshipsTask = FoursquareService .GetMyMayorshipsAsync(token).AsTask();
  9. await Task .WhenAll(friendsTask, badgesTask, checkinsTask, leaderBoardTask, recentTask, mayorshipsTask);
  10. TaskScheduler scheduler = TaskScheduler .FromCurrentSynchronizationContext();
  11. lvFriends.ItemsSource = friendsTask.Result;
  12. lvCheckins.ItemsSource = checkinsTask.Result.Select(c => c.Venue).ToList();
  13. lvBadges.ItemsSource = badgesTask.Result.Where(b => ! string .IsNullOrEmpty(b.UnlockMessage)).Take(9).ToList();
  14. lvLeaderboard.ItemsSource = leaderBoardTask.Result;
  15. lvFriendsActivity.ItemsSource = recentTask.Result;
  16. lvMayorships.ItemsSource = mayorshipsTask.Result.Take(6).ToList();

 

Dans ce 1er exemple, nous appelons différentes méthodes asynchrones de récupération de nos objets Foursquare et pour chacun on récupère la tâche associée.

Nous appelons ensuite la méthode Task.WhenAll avec le mot clé await qui permet d’attendre que toutes les tâches soient terminées. Puis nous faisons la liaison des résultats aux contrôles de la page.

Ci-dessous un autre exemple qui cette fois n’attend pas la fin de chacun des traitements pour effectuer les liaisons de données. Dès qu’une tache se termine elle continue avec la liaison de son résultat au contrôle de la page :

  1. var token = await FoursquareService .AuthenticateAsync( App .clientId, App .callbackFoursquareUri);
  2. var friendsTask = FoursquareService .GetMyFriendsAsync(token, 9).AsTask();
  3. var badgesTask = FoursquareService .GetMyBadgesAsync(token).AsTask();
  4. var checkinsTask = FoursquareService .GetMyCheckinsAsync(token, 5).AsTask();
  5. var userDetailsTask = FoursquareService .GetMyUserDetailsAsync(token).AsTask();
  6. var leaderBoardTask = FoursquareService .GetLeaderboardAsync(token, 5).AsTask();
  7. var recentTask = FoursquareService .GetRecentCheckinsByFriends(token, 5).AsTask();
  8. var mayorshipsTask = FoursquareService .GetMyMayorshipsAsync(token).AsTask();
  9. TaskScheduler scheduler = TaskScheduler .FromCurrentSynchronizationContext();
  10. friendsTask.ContinueWith(
  11. (task) =>
  12. {
  13. lvFriends.ItemsSource = task.Result;
  14. }, scheduler);
  15. badgesTask.ContinueWith(
  16. (task) =>
  17. {
  18. lvBadges.ItemsSource = task.Result.Where(b => ! string .IsNullOrEmpty(b.UnlockMessage)).Take(9).ToList();
  19. }, scheduler);
  20. checkinsTask.ContinueWith(
  21. (task) =>
  22. {
  23. lvCheckins.ItemsSource = task.Result.Select(c => c.Venue).ToList();
  24. }, scheduler);
  25. leaderBoardTask.ContinueWith(
  26. (task) =>
  27. {
  28. lvLeaderboard.ItemsSource = task.Result;
  29. }, scheduler);
  30. recentTask.ContinueWith(
  31. (task) =>
  32. {
  33. lvFriendsActivity.ItemsSource = task.Result;
  34. }, scheduler);
  35. mayorshipsTask.ContinueWith(
  36. (task) =>
  37. {
  38. lvMayorships.ItemsSource = task.Result;
  39. }, scheduler);

 

Aller plus loin

Du côté de l’asynchronisme je ne me suis pas étendu car le sujet est très vaste et je voulais aller à l’essentiel. Peut être que j’y reviendrai prochainement dans un article consacré à ce sujet. Mais si vous désirez en savoir un peu plus, je vous conseille vivement ces 2 articles, qui ont d’ailleurs été traduits :

Pour le développement de composants Windows Runtime, il faut garder à l’esprit qu’il existe quelques contraintes d’implémentation. Ces contraintes sont essentiellement imposées par le système de type du Windows Runtime et le fait de devoir supporter différents langages (et notamment le javascript…).

Dans le prochain article, nous nous finaliserons cette application et nous verrons comment implémenter le contrat ContactPicker.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s