Skip to content

Commit ab65af3

Browse files
committed
Added missing Reddit service section
1 parent e725a31 commit ab65af3

File tree

8 files changed

+212
-2
lines changed

8 files changed

+212
-2
lines changed

docs/mvvm/PuttingThingsTogether.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,84 @@ Ioc.Default.ConfigureServices(services =>
256256

257257
This will register a singleton instance of our `SettingsService` as a type implementing `ISettingsService`. This means that every time one of our viewmodels uses `Ioc.Default.GetService<ISettingsService>()` while the app in use is the UWP one, it will receive a `SettingsService` instance, which will use the UWP APIs behind the scene to manipulate settings. Perfect!
258258

259+
## Building the Reddit service
260+
261+
The last component of the backend that we're missing is a service that is able to use the Reddit REST APIs to fetch the posts from the subreddits we're interested in. To build it, we're going to use [refit](https://github.com/reactiveui/refit), which is a library to easily build type-safe services to interact with REST APIs. As before, we need to define the interface with all the APIs that our service will implement, like so:
262+
263+
```csharp
264+
public interface IRedditService
265+
{
266+
/// <summary>
267+
/// Get a list of posts from a given subreddit
268+
/// </summary>
269+
/// <param name="subreddit">The subreddit name.</param>
270+
[Get("/r/{subreddit}/.json")]
271+
Task<PostsQueryResponse> GetSubredditPostsAsync(string subreddit);
272+
}
273+
```
274+
275+
That `PostsQueryResponse` is a model we wrote that maps the JSON response for that API. The exact structure of that class is not important - suffice to say that it contains a collection of `Post` items, which are simple models representing our posts, that like like this:
276+
277+
```csharp
278+
public class Post
279+
{
280+
/// <summary>
281+
/// Gets or sets the title of the post.
282+
/// </summary>
283+
public string Title { get; set; }
284+
285+
/// <summary>
286+
/// Gets or sets the URL to the post thumbnail, if present.
287+
/// </summary>
288+
public string Thumbnail { get; set; }
289+
290+
/// <summary>
291+
/// Gets the text of the post.
292+
/// </summary>
293+
public string SelfText { get; }
294+
}
295+
```
296+
297+
Once we have our service and our models, can plug them into our viewmodels to complete our backend. While doing so, we can also replace those `object` placeholders with the `Post` type we've defined:
298+
299+
```csharp
300+
public sealed class SubredditWidgetViewModel : ObservableRecipient
301+
{
302+
/// <summary>
303+
/// Gets the <see cref="IRedditService"/> instance to use.
304+
/// </summary>
305+
private readonly IRedditService RedditService = Ioc.Default.GetRequiredService<IRedditService>();
306+
307+
/// <summary>
308+
/// Loads the posts from a specified subreddit.
309+
/// </summary>
310+
private async Task LoadPostsAsync()
311+
{
312+
var response = await RedditService.GetSubredditPostsAsync(SelectedSubreddit);
313+
314+
Posts.Clear();
315+
316+
foreach (var item in response.Data.Items)
317+
{
318+
Posts.Add(item.Data);
319+
}
320+
}
321+
```
322+
323+
We have added a new `IRedditService` field to store our service, just like we did for the settings service, and we implemented our `LoadPostsAsync` method, which was previously empty.
324+
325+
The last missing piece now is just to inject the actual service into our service provider. The big difference in this case is that by using `refit` we don't actually need to implement the service at all! The library will automatically create a type implementing the service for us, behind the scenes. So we only need to get an `IRedditService` instance and inject it directly, like so:
326+
327+
```csharp
328+
Ioc.Default.ConfigureServices(services =>
329+
{
330+
services.AddSingleton<ISettingsService, SettingsService>();
331+
services.AddSingleton(RestService.For<IRedditService>("https://www.reddit.com/"));
332+
});
333+
```
334+
335+
And that's all we need to do! We now have all our backend ready to use, including two custom services that we created specifically for this app! 🎉
336+
259337
## Building the UI
260338

261339
Now that all the backend is completed, we can write the UI for our widgets. Note how using the MVVM pattern let us focus exclusively on the business logic at first, without having to write any UI-related code until now. Here we'll remove all the UI code that's not interacting with our viewmodels, for simplicity, and we'll go through each different control one by one. The full source code can be found in the sample app.

samples/MvvmSampleUwp/MvvmSampleUwp/Assets/docs/PuttingThingsTogether.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,84 @@ Ioc.Default.ConfigureServices(services =>
256256

257257
This will register a singleton instance of our `SettingsService` as a type implementing `ISettingsService`. This means that every time one of our viewmodels uses `Ioc.Default.GetService<ISettingsService>()` while the app in use is the UWP one, it will receive a `SettingsService` instance, which will use the UWP APIs behind the scene to manipulate settings. Perfect!
258258

259+
## Building the Reddit service
260+
261+
The last component of the backend that we're missing is a service that is able to use the Reddit REST APIs to fetch the posts from the subreddits we're interested in. To build it, we're going to use [refit](https://github.com/reactiveui/refit), which is a library to easily build type-safe services to interact with REST APIs. As before, we need to define the interface with all the APIs that our service will implement, like so:
262+
263+
```csharp
264+
public interface IRedditService
265+
{
266+
/// <summary>
267+
/// Get a list of posts from a given subreddit
268+
/// </summary>
269+
/// <param name="subreddit">The subreddit name.</param>
270+
[Get("/r/{subreddit}/.json")]
271+
Task<PostsQueryResponse> GetSubredditPostsAsync(string subreddit);
272+
}
273+
```
274+
275+
That `PostsQueryResponse` is a model we wrote that maps the JSON response for that API. The exact structure of that class is not important - suffice to say that it contains a collection of `Post` items, which are simple models representing our posts, that like like this:
276+
277+
```csharp
278+
public class Post
279+
{
280+
/// <summary>
281+
/// Gets or sets the title of the post.
282+
/// </summary>
283+
public string Title { get; set; }
284+
285+
/// <summary>
286+
/// Gets or sets the URL to the post thumbnail, if present.
287+
/// </summary>
288+
public string Thumbnail { get; set; }
289+
290+
/// <summary>
291+
/// Gets the text of the post.
292+
/// </summary>
293+
public string SelfText { get; }
294+
}
295+
```
296+
297+
Once we have our service and our models, can plug them into our viewmodels to complete our backend. While doing so, we can also replace those `object` placeholders with the `Post` type we've defined:
298+
299+
```csharp
300+
public sealed class SubredditWidgetViewModel : ObservableRecipient
301+
{
302+
/// <summary>
303+
/// Gets the <see cref="IRedditService"/> instance to use.
304+
/// </summary>
305+
private readonly IRedditService RedditService = Ioc.Default.GetRequiredService<IRedditService>();
306+
307+
/// <summary>
308+
/// Loads the posts from a specified subreddit.
309+
/// </summary>
310+
private async Task LoadPostsAsync()
311+
{
312+
var response = await RedditService.GetSubredditPostsAsync(SelectedSubreddit);
313+
314+
Posts.Clear();
315+
316+
foreach (var item in response.Data.Items)
317+
{
318+
Posts.Add(item.Data);
319+
}
320+
}
321+
```
322+
323+
We have added a new `IRedditService` field to store our service, just like we did for the settings service, and we implemented our `LoadPostsAsync` method, which was previously empty.
324+
325+
The last missing piece now is just to inject the actual service into our service provider. The big difference in this case is that by using `refit` we don't actually need to implement the service at all! The library will automatically create a type implementing the service for us, behind the scenes. So we only need to get an `IRedditService` instance and inject it directly, like so:
326+
327+
```csharp
328+
Ioc.Default.ConfigureServices(services =>
329+
{
330+
services.AddSingleton<ISettingsService, SettingsService>();
331+
services.AddSingleton(RestService.For<IRedditService>("https://www.reddit.com/"));
332+
});
333+
```
334+
335+
And that's all we need to do! We now have all our backend ready to use, including two custom services that we created specifically for this app! 🎉
336+
259337
## Building the UI
260338

261339
Now that all the backend is completed, we can write the UI for our widgets. Note how using the MVVM pattern let us focus exclusively on the business logic at first, without having to write any UI-related code until now. Here we'll remove all the UI code that's not interacting with our viewmodels, for simplicity, and we'll go through each different control one by one. The full source code can be found in the sample app.

samples/MvvmSampleUwp/MvvmSampleUwp/MvvmSampleUwp.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@
147147
<Compile Include="Views\BuildingTheUIPage.xaml.cs">
148148
<DependentUpon>BuildingTheUIPage.xaml</DependentUpon>
149149
</Compile>
150+
<Compile Include="Views\RedditServicePage.xaml.cs">
151+
<DependentUpon>RedditServicePage.xaml</DependentUpon>
152+
</Compile>
150153
<Compile Include="Views\SettingsServicePage.xaml.cs">
151154
<DependentUpon>SettingsServicePage.xaml</DependentUpon>
152155
</Compile>
@@ -253,6 +256,10 @@
253256
<Generator>MSBuild:Compile</Generator>
254257
<SubType>Designer</SubType>
255258
</Page>
259+
<Page Include="Views\RedditServicePage.xaml">
260+
<Generator>MSBuild:Compile</Generator>
261+
<SubType>Designer</SubType>
262+
</Page>
256263
<Page Include="Views\SettingsServicePage.xaml">
257264
<Generator>MSBuild:Compile</Generator>
258265
<SubType>Designer</SubType>

samples/MvvmSampleUwp/MvvmSampleUwp/Shell.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
<muxc:NavigationViewItem Content="Creating a Service" Icon="Calendar" ToolTipService.ToolTip="Calendar">
118118
<muxc:NavigationViewItem.MenuItems>
119119
<muxc:NavigationViewItem x:Name="SettingsServiceItem" Content="Settings service" Icon="Mail" ToolTipService.ToolTip="Mail" />
120-
<muxc:NavigationViewItem Content="Navigation" Icon="Calendar" ToolTipService.ToolTip="Calendar"/>
120+
<muxc:NavigationViewItem x:Name="RedditServiceItem" Content="Reddit service" Icon="Mail" ToolTipService.ToolTip="Mail" />
121121
</muxc:NavigationViewItem.MenuItems>
122122
</muxc:NavigationViewItem>
123123
<muxc:NavigationViewItem

samples/MvvmSampleUwp/MvvmSampleUwp/Shell.xaml.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public Shell()
3939
new SampleEntry(RedditBrowserOverviewItem, typeof(PuttingThingsTogetherPage), "Putting things together"),
4040
new SampleEntry(ViewModelsSetupItem, typeof(SettingUpTheViewModelsPage), "Setting up the ViewModels"),
4141
new SampleEntry(SettingsServiceItem, typeof(SettingsServicePage), "Settings service"),
42+
new SampleEntry(RedditServiceItem, typeof(RedditServicePage), "Reddit service"),
4243
new SampleEntry(BuildingTheUIItem, typeof(BuildingTheUIPage), "Building the UI"),
4344
new SampleEntry(FinalResultItem, typeof(RedditBrowserPage), "Reddit browser")
4445
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Page
2+
x:Class="MvvmSampleUwp.Views.RedditServicePage"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
7+
xmlns:toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls"
8+
xmlns:viewModels="using:MvvmSampleUwp.ViewModels"
9+
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
10+
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
11+
mc:Ignorable="d"
12+
NavigationCacheMode="Enabled">
13+
<Page.DataContext>
14+
<viewModels:SamplePageViewModel x:Name="ViewModel"/>
15+
</Page.DataContext>
16+
<interactivity:Interaction.Behaviors>
17+
<core:EventTriggerBehavior EventName="Loaded">
18+
<core:InvokeCommandAction Command="{x:Bind ViewModel.LoadDocsCommand}" CommandParameter="PuttingThingsTogether"/>
19+
</core:EventTriggerBehavior>
20+
</interactivity:Interaction.Behaviors>
21+
22+
<ScrollViewer Padding="16" CanContentRenderOutsideBounds="True">
23+
<StackPanel Spacing="16">
24+
<toolkit:MarkdownTextBlock Text="{x:Bind ViewModel.GetParagraph('Building the Reddit service'), Mode=OneWay}"/>
25+
</StackPanel>
26+
</ScrollViewer>
27+
</Page>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using Windows.UI.Xaml.Controls;
6+
7+
namespace MvvmSampleUwp.Views
8+
{
9+
/// <summary>
10+
/// An empty page that can be used on its own or navigated to within a Frame.
11+
/// </summary>
12+
public sealed partial class RedditServicePage : Page
13+
{
14+
public RedditServicePage()
15+
{
16+
this.InitializeComponent();
17+
}
18+
}
19+
}

samples/MvvmSampleUwp/MvvmSampleUwp/Views/Widgets/SubredditWidget.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
ScrollViewer.CanContentRenderOutsideBounds="True">
6161
<ListView.ItemContainerStyle>
6262
<Style TargetType="ListViewItem">
63-
<Setter Property="Background" Value="#10FFFFFF"/>
63+
<Setter Property="Background" Value="#30808080"/>
6464
<Setter Property="Margin" Value="0,2,0,2"/>
6565
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
6666
</Style>

0 commit comments

Comments
 (0)