├── .gitignore
├── Droid
├── Assets
│ └── AboutAssets.txt
├── MainActivity.cs
├── Properties
│ ├── AndroidManifest.xml
│ └── AssemblyInfo.cs
├── Resources
│ ├── AboutResources.txt
│ ├── Resource.designer.cs
│ ├── drawable-hdpi
│ │ └── icon.png
│ ├── drawable-xhdpi
│ │ └── icon.png
│ ├── drawable-xxhdpi
│ │ └── icon.png
│ ├── drawable
│ │ ├── cities.png
│ │ ├── icon.png
│ │ └── users.png
│ ├── layout
│ │ ├── Tabbar.axml
│ │ └── Toolbar.axml
│ └── values
│ │ └── styles.xml
├── XamarinReduxDemo.Droid.csproj
└── packages.config
├── LICENSE
├── README.md
├── XamarinReduxDemo.Core
├── App.cs
├── Modules
│ ├── Cities
│ │ └── List
│ │ │ ├── CityListPage.cs
│ │ │ └── CityListViewModel.cs
│ └── Users
│ │ ├── Form
│ │ ├── UserFormPage.cs
│ │ └── UserFormViewModel.cs
│ │ ├── List
│ │ ├── UserListPage.cs
│ │ └── UserListViewModel.cs
│ │ └── Profile
│ │ ├── UserProfilePage.cs
│ │ └── UserProfileViewModel.cs
├── Resources
│ ├── 1.jpg
│ ├── 2.jpg
│ └── 3.jpg
├── Shared
│ ├── SmartCollection.cs
│ └── ViewModelBase.cs
└── XamarinReduxDemo.Core.csproj
├── XamarinReduxDemo.Store
├── Actions.fs
├── Domain.fs
├── Reducers.fs
├── State.fs
├── Store.fs
└── XamarinReduxDemo.Store.fsproj
├── XamarinReduxDemo.sln
├── demo.gif
└── iOS
├── AppDelegate.cs
├── Assets.xcassets
├── AppIcon.appiconset
│ └── Contents.json
└── Contents.json
├── Entitlements.plist
├── Info.plist
├── LaunchScreen.storyboard
├── Main.cs
├── Resources
├── cities@2x.png
└── users@2x.png
├── XamarinReduxDemo.iOS.csproj
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | # Autosave files
2 | *~
3 |
4 | # build
5 | [Oo]bj/
6 | [Bb]in/
7 | packages/
8 | TestResults/
9 |
10 | # globs
11 | Makefile.in
12 | *.DS_Store
13 | *.sln.cache
14 | *.suo
15 | *.cache
16 | *.pidb
17 | *.userprefs
18 | *.usertasks
19 | config.log
20 | config.make
21 | config.status
22 | aclocal.m4
23 | install-sh
24 | autom4te.cache/
25 | *.user
26 | *.tar.gz
27 | tarballs/
28 | test-results/
29 | Thumbs.db
30 |
31 | # Mac bundle stuff
32 | *.dmg
33 | *.app
34 |
35 | # resharper
36 | *_Resharper.*
37 | *.Resharper
38 |
39 | # dotCover
40 | *.dotCover
41 |
42 | *.suo
43 | *.user
44 | *.userosscache
45 | *.sln.docstates
46 | */.idea/*
47 | */**/.idea
48 |
--------------------------------------------------------------------------------
/Droid/Assets/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories) and given a Build Action of "AndroidAsset".
3 |
4 | These files will be deployed with your package and will be accessible using Android's
5 | AssetManager, like this:
6 |
7 | public class ReadAsset : Activity
8 | {
9 | protected override void OnCreate (Bundle bundle)
10 | {
11 | base.OnCreate (bundle);
12 |
13 | InputStream input = Assets.Open ("my_asset.txt");
14 | }
15 | }
16 |
17 | Additionally, some Android functions will automatically load asset files:
18 |
19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
20 |
--------------------------------------------------------------------------------
/Droid/MainActivity.cs:
--------------------------------------------------------------------------------
1 |
2 | using Android.App;
3 | using Android.Content.PM;
4 | using Android.OS;
5 | using XamarinReduxDemo.Core;
6 |
7 | namespace XamarinReduxDemo.Droid
8 | {
9 | [Activity(Label = "XamarinReduxDemo.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
10 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
11 | {
12 | protected override void OnCreate(Bundle bundle)
13 | {
14 | TabLayoutResource = Resource.Layout.Tabbar;
15 | ToolbarResource = Resource.Layout.Toolbar;
16 |
17 | base.OnCreate(bundle);
18 |
19 | Xamarin.Forms.Forms.Init(this, bundle);
20 |
21 | LoadApplication(new App());
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Droid/Properties/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Droid/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using Android.App;
4 |
5 | // Information about this assembly is defined by the following attributes.
6 | // Change them to the values specific to your project.
7 |
8 | [assembly: AssemblyTitle("XamarinReduxDemo.Droid")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("")]
13 | [assembly: AssemblyCopyright("${AuthorCopyright}")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
18 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
19 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
20 |
21 | [assembly: AssemblyVersion("1.0.0")]
22 |
23 | // The following attributes are used to specify the signing key for the assembly,
24 | // if desired. See the Mono documentation for more information about signing.
25 |
26 | //[assembly: AssemblyDelaySign(false)]
27 | //[assembly: AssemblyKeyFile("")]
28 |
--------------------------------------------------------------------------------
/Droid/Resources/AboutResources.txt:
--------------------------------------------------------------------------------
1 | Images, layout descriptions, binary blobs and string dictionaries can be included
2 | in your application as resource files. Various Android APIs are designed to
3 | operate on the resource IDs instead of dealing with images, strings or binary blobs
4 | directly.
5 |
6 | For example, a sample Android app that contains a user interface layout (main.axml),
7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
8 | would keep its resources in the "Resources" directory of the application:
9 |
10 | Resources/
11 | drawable/
12 | icon.png
13 |
14 | layout/
15 | main.axml
16 |
17 | values/
18 | strings.xml
19 |
20 | In order to get the build system to recognize Android resources, set the build action to
21 | "AndroidResource". The native Android APIs do not operate directly with filenames, but
22 | instead operate on resource IDs. When you compile an Android application that uses resources,
23 | the build system will package the resources for distribution and generate a class called "R"
24 | (this is an Android convention) that contains the tokens for each one of the resources
25 | included. For example, for the above Resources layout, this is what the R class would expose:
26 |
27 | public class R {
28 | public class drawable {
29 | public const int icon = 0x123;
30 | }
31 |
32 | public class layout {
33 | public const int main = 0x456;
34 | }
35 |
36 | public class strings {
37 | public const int first_string = 0xabc;
38 | public const int second_string = 0xbcd;
39 | }
40 | }
41 |
42 | You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
43 | to reference the layout/main.axml file, or R.strings.first_string to reference the first
44 | string in the dictionary file values/strings.xml.
45 |
--------------------------------------------------------------------------------
/Droid/Resources/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/Droid/Resources/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/Droid/Resources/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable-xxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/Droid/Resources/drawable-xxhdpi/icon.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable/cities.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/Droid/Resources/drawable/cities.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/Droid/Resources/drawable/icon.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable/users.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/Droid/Resources/drawable/users.png
--------------------------------------------------------------------------------
/Droid/Resources/layout/Tabbar.axml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Droid/Resources/layout/Toolbar.axml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Droid/Resources/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
24 |
27 |
28 |
--------------------------------------------------------------------------------
/Droid/XamarinReduxDemo.Droid.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}
8 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
9 | Library
10 | XamarinReduxDemo.Droid
11 | XamarinReduxDemo.Droid
12 | v7.1
13 | True
14 | Resources\Resource.designer.cs
15 | Resource
16 | Properties\AndroidManifest.xml
17 | Resources
18 | Assets
19 | false
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug
26 | DEBUG;
27 | prompt
28 | 4
29 | None
30 |
31 |
32 | true
33 | pdbonly
34 | true
35 | bin\Release
36 | prompt
37 | 4
38 | true
39 | false
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | ..\packages\Xamarin.Android.Support.Annotations.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Annotations.dll
48 |
49 |
50 | ..\packages\Xamarin.Android.Support.Compat.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Compat.dll
51 |
52 |
53 | ..\packages\Xamarin.Android.Support.Core.UI.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Core.UI.dll
54 |
55 |
56 | ..\packages\Xamarin.Android.Support.Core.Utils.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Core.Utils.dll
57 |
58 |
59 | ..\packages\Xamarin.Android.Support.Media.Compat.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Media.Compat.dll
60 |
61 |
62 | ..\packages\Xamarin.Android.Support.Fragment.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Fragment.dll
63 |
64 |
65 | ..\packages\Xamarin.Android.Support.v4.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v4.dll
66 |
67 |
68 | ..\packages\Xamarin.Android.Support.Vector.Drawable.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.dll
69 |
70 |
71 | ..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.dll
72 |
73 |
74 | ..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.dll
75 |
76 |
77 | ..\packages\Xamarin.Android.Support.Transition.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Transition.dll
78 |
79 |
80 | ..\packages\Xamarin.Android.Support.v7.RecyclerView.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.dll
81 |
82 |
83 | ..\packages\Xamarin.Android.Support.Design.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Design.dll
84 |
85 |
86 | ..\packages\Xamarin.Android.Support.v7.CardView.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.CardView.dll
87 |
88 |
89 | ..\packages\Xamarin.Android.Support.v7.Palette.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.Palette.dll
90 |
91 |
92 | ..\packages\Xamarin.Android.Support.v7.MediaRouter.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.dll
93 |
94 |
95 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\MonoAndroid10\FormsViewGroup.dll
96 |
97 |
98 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\MonoAndroid10\Xamarin.Forms.Core.dll
99 |
100 |
101 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll
102 |
103 |
104 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\MonoAndroid10\Xamarin.Forms.Platform.dll
105 |
106 |
107 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll
108 |
109 |
110 | ..\packages\MvvmNano.Core.3.2.0\lib\netstandard2.0\MvvmNano.Core.dll
111 |
112 |
113 | ..\packages\MvvmNano.Forms.3.2.0\lib\netstandard2.0\MvvmNano.Forms.dll
114 |
115 |
116 | ..\packages\MvvmNano.TinyIoC.3.2.0\lib\netstandard2.0\MvvmNano.TinyIoC.dll
117 |
118 |
119 | ..\packages\FSharp.Core.4.2.3\lib\netstandard1.6\FSharp.Core.dll
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}
145 | XamarinReduxDemo.Store
146 |
147 |
148 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}
149 | XamarinReduxDemo.Core
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/Droid/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Thomas Bandt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building An F# Redux Store For C# Xamarin Apps
2 |
3 | 
4 |
5 | This tiny demo project demonstrates how to implement a basic Redux Store for a C# Xamarin App with the help of F#. For more information about the motivation behind see the [blog post](https://thomasbandt.com/fsharp-redux-store-for-xamarin-apps).
6 |
7 | The app is using Xamarin.Forms and supports iOS and Android as target platforms.
8 |
9 | The MVVM Framework in use is [MvvmNano](https://github.com/aspnetde/MvvmNano). Icons are coming from [Icons8](https://icons8.com/). And all the nice people shown as users are provided by [Unsplash](https://unsplash.com/). Sorry for the ugly app layout, though ;-).
10 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/App.cs:
--------------------------------------------------------------------------------
1 | using MvvmNano;
2 | using MvvmNano.Forms;
3 | using MvvmNano.TinyIoC;
4 | using Xamarin.Forms;
5 | using XamarinReduxDemo.Core.Modules.Cities;
6 | using XamarinReduxDemo.Core.Modules.Users;
7 | using XamarinReduxDemo.Store;
8 |
9 | namespace XamarinReduxDemo.Core
10 | {
11 | public class App : MvvmNanoApplication
12 | {
13 | protected override void OnStart()
14 | {
15 | base.OnStart();
16 |
17 | SetUpReduxStore();
18 | SetUpTabbedMainPage();
19 | }
20 |
21 | protected override IMvvmNanoIoCAdapter GetIoCAdapter()
22 | => new MvvmNanoTinyIoCAdapter();
23 |
24 | private void SetUpReduxStore()
25 | {
26 | IStore store = CreateStore();
27 |
28 | MvvmNanoIoC.RegisterAsSingleton(store);
29 | }
30 |
31 | private static IStore CreateStore()
32 | {
33 | IStore store = new Store.Store(
34 | // Loads up the instance of the Store with the last known State.
35 | // In this example a new state is always being created, but that's
36 | // the place where you should load your current State from disk
37 | // or whatever you prefer for persisting data.
38 | InitialState.Create(),
39 |
40 | // Register all Reducers that should be applied to dispatched
41 | // actions.
42 | RootReducer.Create()
43 | );
44 |
45 | store.Subscribe(SubscriptionType.NewStateSubscription(state =>
46 | {
47 | // The State was updated. That's the moment where you
48 | // should persist the current state.
49 | }));
50 |
51 | return store;
52 | }
53 |
54 | private void SetUpTabbedMainPage()
55 | {
56 | var tabbedPage = new TabbedPage();
57 |
58 | tabbedPage.Children.Add(new MvvmNanoNavigationPage(GetPageFor())
59 | {
60 | Title = "Users",
61 | Icon = "users"
62 | });
63 |
64 | tabbedPage.Children.Add(new MvvmNanoNavigationPage(GetPageFor())
65 | {
66 | Title = "Cities",
67 | Icon = "cities"
68 | });
69 |
70 | MainPage = tabbedPage;
71 | }
72 |
73 | private static Page GetPageFor() where TViewModel : MvvmNanoViewModel
74 | {
75 | TViewModel viewModel = MvvmNanoIoC.Resolve();
76 | viewModel.Initialize();
77 |
78 | return ResolvePage(viewModel);
79 | }
80 |
81 | private static Page ResolvePage(TViewModel viewModel) where TViewModel : IViewModel
82 | {
83 | IView viewFor = MvvmNanoIoC.Resolve().CreateViewFor();
84 | viewFor.SetViewModel(viewModel);
85 | return viewFor as Page;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Cities/List/CityListPage.cs:
--------------------------------------------------------------------------------
1 | using MvvmNano.Forms;
2 | using Xamarin.Forms;
3 | using XamarinReduxDemo.Store;
4 |
5 | namespace XamarinReduxDemo.Core.Modules.Cities
6 | {
7 | public class CityListPage : MvvmNanoContentPage
8 | {
9 | private readonly ListView _listView;
10 |
11 | public CityListPage()
12 | {
13 | Title = "Cities";
14 |
15 | Content = _listView = new ListView();
16 |
17 | _listView.ItemSelected += OnItemSelected;
18 |
19 | _listView.ItemTemplate = new DataTemplate(typeof(TextCell));
20 | _listView.ItemTemplate.SetBinding(TextCell.TextProperty, nameof(User.Name));
21 | }
22 |
23 | public override void OnViewModelSet()
24 | {
25 | base.OnViewModelSet();
26 |
27 | _listView.ItemsSource = ViewModel.Cities;
28 | }
29 |
30 | private async void OnItemSelected(object sender, SelectedItemChangedEventArgs arguments)
31 | {
32 | if (arguments.SelectedItem == null)
33 | {
34 | return;
35 | }
36 |
37 | const string cancel = "Cancel";
38 |
39 | var action = await DisplayActionSheet(
40 | "Do you want to remove that city from the list?",
41 | cancel: cancel,
42 | destruction: "Yes, remove it"
43 | );
44 |
45 | if (action != cancel)
46 | {
47 | ViewModel.RemoveCityCommand.Execute(arguments.SelectedItem);
48 | }
49 |
50 | _listView.SelectedItem = null;
51 | }
52 |
53 | public override void Dispose()
54 | {
55 | base.Dispose();
56 |
57 | _listView.ItemSelected -= OnItemSelected;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Cities/List/CityListViewModel.cs:
--------------------------------------------------------------------------------
1 | using MvvmNano;
2 | using XamarinReduxDemo.Core.Shared;
3 | using XamarinReduxDemo.Store;
4 |
5 | namespace XamarinReduxDemo.Core.Modules.Cities
6 | {
7 | public class CityListViewModel : ViewModelBase
8 | {
9 | public SmartCollection Cities { get; }
10 |
11 | private MvvmNanoCommand _removeCityCommand;
12 | public MvvmNanoCommand RemoveCityCommand
13 | => _removeCityCommand ??
14 | (_removeCityCommand = new MvvmNanoCommand(OnRemoveCity));
15 |
16 | public CityListViewModel(IStore store) : base(store)
17 | {
18 | Cities = new SmartCollection();
19 |
20 | ConnectToStore(state => state.Cities, cities =>
21 | {
22 | Cities.Reset(cities);
23 | });
24 | }
25 |
26 | private void OnRemoveCity(City city)
27 | {
28 | Store.Dispatch(StoreAction.NewCityRemoved(city));
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Users/Form/UserFormPage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MvvmNano.Forms;
3 | using Xamarin.Forms;
4 |
5 | namespace XamarinReduxDemo.Core.Modules.Users
6 | {
7 | public class UserFormPage : MvvmNanoContentPage
8 | {
9 | public UserFormPage()
10 | {
11 | Title = "Edit Profile";
12 | }
13 |
14 | public override void OnViewModelSet()
15 | {
16 | base.OnViewModelSet();
17 |
18 | ViewModel.Done += OnDone;
19 |
20 | ToolbarItems.Add(new ToolbarItem
21 | {
22 | Text = "Save",
23 | Command = ViewModel.SaveCommand
24 | });
25 |
26 | var scrollView = new ScrollView
27 | {
28 | HorizontalOptions = LayoutOptions.FillAndExpand,
29 | VerticalOptions = LayoutOptions.Start,
30 | BackgroundColor = Color.White
31 | };
32 |
33 | var layout = new StackLayout
34 | {
35 | HorizontalOptions = LayoutOptions.FillAndExpand,
36 | VerticalOptions = LayoutOptions.Start,
37 | Padding = 15,
38 | Spacing = 15
39 | };
40 |
41 | var profileImage = new Image
42 | {
43 | WidthRequest = 300,
44 | HeightRequest = 300,
45 | HorizontalOptions = LayoutOptions.Center,
46 | VerticalOptions = LayoutOptions.Start,
47 | Source = ImageSource.FromResource(ViewModel.ImageName)
48 | };
49 |
50 | var nameEntry = new Entry
51 | {
52 | FontSize = 20,
53 | WidthRequest = 300,
54 | HorizontalOptions = LayoutOptions.Center,
55 | VerticalOptions = LayoutOptions.Start,
56 | HorizontalTextAlignment = TextAlignment.Center,
57 | TextColor = Color.Black,
58 | Placeholder = "Name"
59 | };
60 | BindToViewModel(nameEntry, Entry.TextProperty, x => x.Name);
61 |
62 | layout.Children.Add(profileImage);
63 | layout.Children.Add(nameEntry);
64 | scrollView.Content = layout;
65 |
66 | Content = new StackLayout
67 | {
68 | HorizontalOptions = LayoutOptions.FillAndExpand,
69 | VerticalOptions = LayoutOptions.Start,
70 | Padding = 0,
71 | Spacing = 0,
72 | Children =
73 | {
74 | scrollView
75 | }
76 | };
77 | }
78 |
79 | private void OnDone(object sender, EventArgs eventArgs)
80 | {
81 | Navigation.PopAsync(true);
82 | }
83 |
84 | public override void Dispose()
85 | {
86 | base.Dispose();
87 |
88 | ViewModel.Done -= OnDone;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Users/Form/UserFormViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using MvvmNano;
4 | using XamarinReduxDemo.Core.Shared;
5 | using XamarinReduxDemo.Store;
6 |
7 | namespace XamarinReduxDemo.Core.Modules.Users
8 | {
9 | public class UserFormViewModel : ViewModelBase
10 | {
11 | private UserId _userId;
12 |
13 | private string _name;
14 | public string Name
15 | {
16 | get => _name;
17 | set { _name = value; NotifyPropertyChanged(); }
18 | }
19 |
20 | public string ImageName { get; private set; }
21 |
22 | private MvvmNanoCommand _saveCommand;
23 | public MvvmNanoCommand SaveCommand
24 | => _saveCommand ??
25 | (_saveCommand = new MvvmNanoCommand(OnSave));
26 |
27 | // Hack, because there's no Close mechanism in MvvmNano yet
28 | public event EventHandler Done;
29 |
30 | public UserFormViewModel(IStore store) : base(store)
31 | {
32 | }
33 |
34 | public override void Initialize(UserId userId)
35 | {
36 | _userId = userId;
37 |
38 | ConnectToStore(state => state.Users.SingleOrDefault(u => Equals(u.Id, _userId)), user =>
39 | {
40 | if (user == null)
41 | {
42 | return;
43 | }
44 |
45 | Name = user.Name;
46 | ImageName = user.Id.Item + ".jpg";
47 | });
48 | }
49 |
50 | private void OnSave()
51 | {
52 | var updatedUser = new User(
53 | _userId,
54 | Name
55 | );
56 |
57 | Store.Dispatch(StoreAction.NewUserUpdated(updatedUser));
58 |
59 | Done?.Invoke(this, EventArgs.Empty);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Users/List/UserListPage.cs:
--------------------------------------------------------------------------------
1 | using MvvmNano.Forms;
2 | using Xamarin.Forms;
3 | using XamarinReduxDemo.Store;
4 |
5 | namespace XamarinReduxDemo.Core.Modules.Users
6 | {
7 | public class UserListPage : MvvmNanoContentPage
8 | {
9 | private readonly ListView _listView;
10 |
11 | public UserListPage()
12 | {
13 | Title = "Users";
14 |
15 | Content = _listView = new ListView();
16 |
17 | _listView.ItemSelected += OnItemSelected;
18 |
19 | _listView.ItemTemplate = new DataTemplate(typeof(TextCell));
20 | _listView.ItemTemplate.SetBinding(TextCell.TextProperty, nameof(User.Name));
21 | }
22 |
23 | public override void OnViewModelSet()
24 | {
25 | base.OnViewModelSet();
26 |
27 | _listView.ItemsSource = ViewModel.Users;
28 | }
29 |
30 | private void OnItemSelected(object sender, SelectedItemChangedEventArgs arguments)
31 | {
32 | if (arguments.SelectedItem != null)
33 | {
34 | ViewModel.OpenUserProfileCommand.Execute(arguments.SelectedItem);
35 | }
36 |
37 | _listView.SelectedItem = null;
38 | }
39 |
40 | public override void Dispose()
41 | {
42 | base.Dispose();
43 |
44 | _listView.ItemSelected -= OnItemSelected;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Users/List/UserListViewModel.cs:
--------------------------------------------------------------------------------
1 | using MvvmNano;
2 | using XamarinReduxDemo.Core.Shared;
3 | using XamarinReduxDemo.Store;
4 |
5 | namespace XamarinReduxDemo.Core.Modules.Users
6 | {
7 | public class UserListViewModel : ViewModelBase
8 | {
9 | public SmartCollection Users { get; }
10 |
11 | private MvvmNanoCommand _openUserProfileCommand;
12 | public MvvmNanoCommand OpenUserProfileCommand
13 | => _openUserProfileCommand ??
14 | (_openUserProfileCommand = new MvvmNanoCommand(OnOpenUserProfile));
15 |
16 | public UserListViewModel(IStore store) : base(store)
17 | {
18 | Users = new SmartCollection();
19 |
20 | ConnectToStore(state => state.Users, users =>
21 | {
22 | Users.Reset(users);
23 | });
24 | }
25 |
26 | private void OnOpenUserProfile(User selectedUser)
27 | {
28 | NavigateTo(selectedUser.Id);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Users/Profile/UserProfilePage.cs:
--------------------------------------------------------------------------------
1 | using MvvmNano.Forms;
2 | using Xamarin.Forms;
3 |
4 | namespace XamarinReduxDemo.Core.Modules.Users
5 | {
6 | public class UserProfilePage : MvvmNanoContentPage
7 | {
8 | public UserProfilePage()
9 | {
10 | Title = "Profile";
11 | }
12 |
13 | public override void OnViewModelSet()
14 | {
15 | base.OnViewModelSet();
16 |
17 | var scrollView = new ScrollView
18 | {
19 | HorizontalOptions = LayoutOptions.FillAndExpand,
20 | VerticalOptions = LayoutOptions.Start,
21 | BackgroundColor = Color.White
22 | };
23 |
24 | var layout = new StackLayout
25 | {
26 | HorizontalOptions = LayoutOptions.FillAndExpand,
27 | VerticalOptions = LayoutOptions.Start,
28 | Padding = 15,
29 | Spacing = 15
30 | };
31 |
32 | var profileImage = new Image
33 | {
34 | WidthRequest = 300,
35 | HeightRequest = 300,
36 | HorizontalOptions = LayoutOptions.Center,
37 | VerticalOptions = LayoutOptions.Start,
38 | Source = ImageSource.FromResource(ViewModel.ImageName)
39 | };
40 |
41 | var nameLabel = new Label
42 | {
43 | FontSize = 20,
44 | HorizontalOptions = LayoutOptions.Center,
45 | VerticalOptions = LayoutOptions.Start,
46 | HorizontalTextAlignment = TextAlignment.Center,
47 | TextColor = Color.Black
48 | };
49 | BindToViewModel(nameLabel, Label.TextProperty, x => x.Name);
50 |
51 | layout.Children.Add(profileImage);
52 | layout.Children.Add(nameLabel);
53 | scrollView.Content = layout;
54 |
55 | Content = new StackLayout
56 | {
57 | HorizontalOptions = LayoutOptions.FillAndExpand,
58 | VerticalOptions = LayoutOptions.Start,
59 | Padding = 0,
60 | Spacing = 0,
61 | Children =
62 | {
63 | scrollView
64 | }
65 | };
66 | }
67 |
68 | protected override void OnAppearing()
69 | {
70 | base.OnAppearing();
71 |
72 | ToolbarItems.Clear();
73 | ToolbarItems.Add(new ToolbarItem
74 | {
75 | Text = "Edit",
76 | Command = ViewModel.OpenFormCommand
77 | });
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Modules/Users/Profile/UserProfileViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using MvvmNano;
3 | using XamarinReduxDemo.Core.Shared;
4 | using XamarinReduxDemo.Store;
5 |
6 | namespace XamarinReduxDemo.Core.Modules.Users
7 | {
8 | public class UserProfileViewModel : ViewModelBase
9 | {
10 | private UserId _userId;
11 |
12 | private string _name;
13 | public string Name
14 | {
15 | get => _name;
16 | set { _name = value; NotifyPropertyChanged(); }
17 | }
18 |
19 | public string ImageName { get; private set; }
20 |
21 | private MvvmNanoCommand _openFormCommand;
22 | public MvvmNanoCommand OpenFormCommand
23 | => _openFormCommand ??
24 | (_openFormCommand = new MvvmNanoCommand(OnOpenForm));
25 |
26 | public UserProfileViewModel(IStore store) : base(store)
27 | {
28 | }
29 |
30 | public override void Initialize(UserId userId)
31 | {
32 | _userId = userId;
33 |
34 | ConnectToStore(state => state.Users.SingleOrDefault(u => Equals(u.Id, _userId)), user =>
35 | {
36 | if (user == null)
37 | {
38 | return;
39 | }
40 |
41 | Name = user.Name;
42 | ImageName = user.Id.Item + ".jpg";
43 | });
44 | }
45 |
46 | private void OnOpenForm()
47 | {
48 | NavigateTo(_userId);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Resources/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/XamarinReduxDemo.Core/Resources/1.jpg
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Resources/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/XamarinReduxDemo.Core/Resources/2.jpg
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Resources/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/XamarinReduxDemo.Core/Resources/3.jpg
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Shared/SmartCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Collections.Specialized;
4 | using System.ComponentModel;
5 |
6 | namespace XamarinReduxDemo.Core.Shared
7 | {
8 | // Kudos to Jehof: https://stackoverflow.com/a/13303245
9 | public class SmartCollection : ObservableCollection
10 | {
11 | public SmartCollection()
12 | {
13 | }
14 |
15 | public SmartCollection(IEnumerable collection)
16 | : base(collection)
17 | {
18 | }
19 |
20 | public SmartCollection(List list)
21 | : base(list)
22 | {
23 | }
24 |
25 | public void AddRange(IEnumerable range)
26 | {
27 | foreach (var item in range)
28 | {
29 | Items.Add(item);
30 | }
31 |
32 | OnPropertyChanged(new PropertyChangedEventArgs("Count"));
33 | OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
34 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
35 | }
36 |
37 | public void Reset(IEnumerable range)
38 | {
39 | Items.Clear();
40 |
41 | AddRange(range);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/Shared/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using MvvmNano;
4 | using XamarinReduxDemo.Store;
5 |
6 | namespace XamarinReduxDemo.Core.Shared
7 | {
8 | public abstract class ViewModelBase : MvvmNanoViewModel
9 | {
10 | private readonly IList _disposables;
11 |
12 | protected readonly IStore Store;
13 |
14 | protected ViewModelBase(IStore store)
15 | {
16 | _disposables = new List();
17 |
18 | Store = store;
19 | }
20 |
21 | protected void ConnectToStore(
22 | Func selector,
23 | Action callback
24 | )
25 | {
26 | var subscription = Store.Subscribe(
27 | SubscriptionType.NewSelectionSubscription(
28 | selector,
29 | callback
30 | )
31 | );
32 | _disposables.Add(subscription);
33 |
34 | var selection = selector(Store.CurrentState);
35 | callback(selection);
36 | }
37 |
38 | public override void Dispose()
39 | {
40 | base.Dispose();
41 |
42 | foreach (var disposable in _disposables)
43 | {
44 | disposable?.Dispose();
45 | }
46 | }
47 | }
48 |
49 | public abstract class ViewModelBase : MvvmNanoViewModel
50 | {
51 | private readonly IList _disposables;
52 |
53 | protected readonly IStore Store;
54 |
55 | protected ViewModelBase(IStore store)
56 | {
57 | _disposables = new List();
58 |
59 | Store = store;
60 | }
61 |
62 | protected void ConnectToStore(
63 | Func selector,
64 | Action callback
65 | )
66 | {
67 | var subscription = Store.Subscribe(
68 | SubscriptionType.NewSelectionSubscription(
69 | selector,
70 | callback
71 | )
72 | );
73 | _disposables.Add(subscription);
74 |
75 | var selection = selector(Store.CurrentState);
76 | callback(selection);
77 | }
78 |
79 | public override void Dispose()
80 | {
81 | base.Dispose();
82 |
83 | foreach (var disposable in _disposables)
84 | {
85 | disposable?.Dispose();
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Core/XamarinReduxDemo.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 2.jpg
25 |
26 |
27 | 3.jpg
28 |
29 |
30 | 1.jpg
31 |
32 |
33 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Store/Actions.fs:
--------------------------------------------------------------------------------
1 | namespace XamarinReduxDemo.Store
2 |
3 | type StoreAction =
4 | | CityRemoved of City
5 | | UserUpdated of User
6 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Store/Domain.fs:
--------------------------------------------------------------------------------
1 | namespace XamarinReduxDemo.Store
2 |
3 | type CityId = CityId of int
4 | type City =
5 | { Id : CityId
6 | Name : string }
7 |
8 | type UserId = UserId of int
9 | type User =
10 | { Id : UserId
11 | Name : string }
12 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Store/Reducers.fs:
--------------------------------------------------------------------------------
1 | module XamarinReduxDemo.Store.RootReducer
2 |
3 | let reduceAppState action state =
4 | match action with
5 | | CityRemoved removedCity ->
6 | { state with
7 | Cities =
8 | state.Cities
9 | |> Array.filter (fun c -> c.Id <> removedCity.Id) }
10 | | UserUpdated updatedUser ->
11 | { state with
12 | Users =
13 | state.Users
14 | |> Array.map (fun u -> if u.Id = updatedUser.Id then updatedUser else u) }
15 |
16 | let Create() = reduceAppState
17 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Store/State.fs:
--------------------------------------------------------------------------------
1 | namespace XamarinReduxDemo.Store
2 |
3 | type AppState =
4 | { Cities : City array
5 | Users : User array }
6 |
7 | module InitialState =
8 | let Create() =
9 | // In Production you probably don't want to populate your
10 | // initial state with demo data – but for the purpose of
11 | // this demo that's a good place to start.
12 | { Cities =
13 | [| { Id = CityId(1); Name = "München" };
14 | { Id = CityId(2); Name = "New York" };
15 | { Id = CityId(3); Name = "Tokyo" } |]
16 | Users =
17 | [| { Id = UserId(1); Name = "Max" };
18 | { Id = UserId(2); Name = "Zoe" };
19 | { Id = UserId(3); Name = "Carl" } |] }
20 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Store/Store.fs:
--------------------------------------------------------------------------------
1 | namespace XamarinReduxDemo.Store
2 |
3 | open System
4 |
5 | type SubscriptionType<'TSelection> =
6 | | StateSubscription of Action
7 | | SelectionSubscription of Func * Action<'TSelection>
8 | | SelectionAndStateSubscription of Func * Action<'TSelection, AppState>
9 |
10 | type Subscription(unsubscribe:Subscription -> unit, callback:obj * AppState -> unit, selector:AppState -> obj) =
11 | member __.Callback = callback
12 | member __.Selector = selector
13 | interface IDisposable with
14 | member this.Dispose() = unsubscribe this
15 |
16 | type IStore =
17 | abstract CurrentState : AppState
18 | abstract Subscribe : SubscriptionType<'TSelection> -> Subscription
19 | abstract Unsubscribe : Subscription -> unit
20 | abstract Dispatch : StoreAction -> unit
21 |
22 | type Store(initialState:AppState, rootReducer:StoreAction -> AppState -> AppState) =
23 | let subscriptions = new ResizeArray()
24 | let mutable currentState = initialState
25 | let monitor = Object()
26 | let unsubscribe subscription = lock monitor (fun () ->
27 | subscriptions.Remove subscription |> ignore
28 | )
29 |
30 | interface IStore with
31 | member __.CurrentState = currentState
32 |
33 | member __.Subscribe<'T>(subscriptionType:SubscriptionType<'T>) =
34 | let (callback:obj * AppState -> unit, selector:AppState -> obj) =
35 | match subscriptionType with
36 | | StateSubscription(c) ->
37 | (fun (_, state) -> c.Invoke(state)),
38 | fun _ -> id :> obj
39 | | SelectionSubscription(s, c) ->
40 | (fun (selection, _) -> c.Invoke((selection :?> 'T))),
41 | fun state -> s.Invoke state :> obj
42 | | SelectionAndStateSubscription(s, c) ->
43 | (fun (selection, state) -> c.Invoke((selection :?> 'T), state)),
44 | fun state -> s.Invoke state :> obj
45 |
46 | let subscription = new Subscription(unsubscribe, callback, selector)
47 |
48 | lock monitor (fun () ->
49 | subscriptions.Add subscription
50 | )
51 |
52 | subscription
53 |
54 | member __.Unsubscribe subscription = unsubscribe subscription
55 |
56 | member __.Dispatch action =
57 | lock monitor (fun () ->
58 | let newState = currentState |> rootReducer action
59 | let oldState = currentState
60 |
61 | currentState <- newState
62 |
63 | subscriptions |> Seq.iter (fun subscription ->
64 | let newSelection = subscription.Selector newState
65 | let oldSelection = subscription.Selector oldState
66 |
67 | if newSelection <> oldSelection then
68 | subscription.Callback(newSelection, newState)
69 | )
70 | )
71 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.Store/XamarinReduxDemo.Store.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/XamarinReduxDemo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamarinReduxDemo.iOS", "iOS\XamarinReduxDemo.iOS.csproj", "{87F819BE-C59B-4BF6-8614-CD307F064366}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamarinReduxDemo.Droid", "Droid\XamarinReduxDemo.Droid.csproj", "{8414F9CC-CDCD-4E72-A214-3372CDD96A3A}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamarinReduxDemo.Core", "XamarinReduxDemo.Core\XamarinReduxDemo.Core.csproj", "{73705DA3-6A6E-4B33-8902-CB8B258C10CF}"
9 | EndProject
10 | Project("{f2a71f9b-5d33-465a-a702-920d77279786}") = "XamarinReduxDemo.Store", "XamarinReduxDemo.Store\XamarinReduxDemo.Store.fsproj", "{F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{21F01E4A-6887-4B5A-BBB6-E5620B3A4239}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | Debug|iPhoneSimulator = Debug|iPhoneSimulator
19 | Release|iPhone = Release|iPhone
20 | Release|iPhoneSimulator = Release|iPhoneSimulator
21 | Debug|iPhone = Debug|iPhone
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
25 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
26 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Release|Any CPU.ActiveCfg = Release|iPhone
27 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Release|Any CPU.Build.0 = Release|iPhone
28 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
29 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
30 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Release|iPhone.ActiveCfg = Release|iPhone
31 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Release|iPhone.Build.0 = Release|iPhone
32 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
33 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
34 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Debug|iPhone.ActiveCfg = Debug|iPhone
35 | {87F819BE-C59B-4BF6-8614-CD307F064366}.Debug|iPhone.Build.0 = Debug|iPhone
36 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
41 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
42 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Release|iPhone.ActiveCfg = Release|Any CPU
43 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Release|iPhone.Build.0 = Release|Any CPU
44 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
45 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
46 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
47 | {8414F9CC-CDCD-4E72-A214-3372CDD96A3A}.Debug|iPhone.Build.0 = Debug|Any CPU
48 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
53 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
54 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Release|iPhone.ActiveCfg = Release|Any CPU
55 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Release|iPhone.Build.0 = Release|Any CPU
56 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
57 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
58 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
59 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}.Debug|iPhone.Build.0 = Debug|Any CPU
60 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Debug|Any CPU.Build.0 = Debug|Any CPU
62 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Release|Any CPU.ActiveCfg = Release|Any CPU
63 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Release|Any CPU.Build.0 = Release|Any CPU
64 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
65 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
66 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Release|iPhone.ActiveCfg = Release|Any CPU
67 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Release|iPhone.Build.0 = Release|Any CPU
68 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
69 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
70 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
71 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}.Debug|iPhone.Build.0 = Debug|Any CPU
72 | EndGlobalSection
73 | GlobalSection(NestedProjects) = preSolution
74 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF} = {21F01E4A-6887-4B5A-BBB6-E5620B3A4239}
75 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A} = {21F01E4A-6887-4B5A-BBB6-E5620B3A4239}
76 | EndGlobalSection
77 | EndGlobal
78 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/demo.gif
--------------------------------------------------------------------------------
/iOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 | using UIKit;
3 | using XamarinReduxDemo.Core;
4 |
5 | namespace XamarinReduxDemo.iOS
6 | {
7 | [Register("AppDelegate")]
8 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
9 | {
10 | public override bool FinishedLaunching(UIApplication app, NSDictionary options)
11 | {
12 | global::Xamarin.Forms.Forms.Init();
13 |
14 | LoadApplication(new App());
15 |
16 | return base.FinishedLaunching(app, options);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "24x24",
95 | "idiom" : "watch",
96 | "scale" : "2x",
97 | "role" : "notificationCenter",
98 | "subtype" : "38mm"
99 | },
100 | {
101 | "size" : "27.5x27.5",
102 | "idiom" : "watch",
103 | "scale" : "2x",
104 | "role" : "notificationCenter",
105 | "subtype" : "42mm"
106 | },
107 | {
108 | "size" : "29x29",
109 | "idiom" : "watch",
110 | "role" : "companionSettings",
111 | "scale" : "2x"
112 | },
113 | {
114 | "size" : "29x29",
115 | "idiom" : "watch",
116 | "role" : "companionSettings",
117 | "scale" : "3x"
118 | },
119 | {
120 | "size" : "40x40",
121 | "idiom" : "watch",
122 | "scale" : "2x",
123 | "role" : "appLauncher",
124 | "subtype" : "38mm"
125 | },
126 | {
127 | "size" : "44x44",
128 | "idiom" : "watch",
129 | "scale" : "2x",
130 | "role" : "longLook",
131 | "subtype" : "42mm"
132 | },
133 | {
134 | "size" : "86x86",
135 | "idiom" : "watch",
136 | "scale" : "2x",
137 | "role" : "quickLook",
138 | "subtype" : "38mm"
139 | },
140 | {
141 | "size" : "98x98",
142 | "idiom" : "watch",
143 | "scale" : "2x",
144 | "role" : "quickLook",
145 | "subtype" : "42mm"
146 | },
147 | {
148 | "idiom" : "mac",
149 | "size" : "16x16",
150 | "scale" : "1x"
151 | },
152 | {
153 | "idiom" : "mac",
154 | "size" : "16x16",
155 | "scale" : "2x"
156 | },
157 | {
158 | "idiom" : "mac",
159 | "size" : "32x32",
160 | "scale" : "1x"
161 | },
162 | {
163 | "idiom" : "mac",
164 | "size" : "32x32",
165 | "scale" : "2x"
166 | },
167 | {
168 | "idiom" : "mac",
169 | "size" : "128x128",
170 | "scale" : "1x"
171 | },
172 | {
173 | "idiom" : "mac",
174 | "size" : "128x128",
175 | "scale" : "2x"
176 | },
177 | {
178 | "idiom" : "mac",
179 | "size" : "256x256",
180 | "scale" : "1x"
181 | },
182 | {
183 | "idiom" : "mac",
184 | "size" : "256x256",
185 | "scale" : "2x"
186 | },
187 | {
188 | "idiom" : "mac",
189 | "size" : "512x512",
190 | "scale" : "1x"
191 | },
192 | {
193 | "idiom" : "mac",
194 | "size" : "512x512",
195 | "scale" : "2x"
196 | }
197 | ],
198 | "info" : {
199 | "version" : 1,
200 | "author" : "xcode"
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iOS/Entitlements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | XamarinReduxDemo
7 | CFBundleName
8 | XamarinReduxDemo
9 | CFBundleIdentifier
10 | com.thomasbandt.XamarinReduxDemo
11 | CFBundleShortVersionString
12 | 1.0
13 | CFBundleVersion
14 | 1.0
15 | LSRequiresIPhoneOS
16 |
17 | MinimumOSVersion
18 | 8.0
19 | UIDeviceFamily
20 |
21 | 1
22 | 2
23 |
24 | UILaunchStoryboardName
25 | LaunchScreen
26 | UIRequiredDeviceCapabilities
27 |
28 | armv7
29 |
30 | UISupportedInterfaceOrientations
31 |
32 | UIInterfaceOrientationPortrait
33 | UIInterfaceOrientationLandscapeLeft
34 | UIInterfaceOrientationLandscapeRight
35 |
36 | UISupportedInterfaceOrientations~ipad
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationPortraitUpsideDown
40 | UIInterfaceOrientationLandscapeLeft
41 | UIInterfaceOrientationLandscapeRight
42 |
43 | XSAppIconAssets
44 | Assets.xcassets/AppIcon.appiconset
45 |
46 |
47 |
--------------------------------------------------------------------------------
/iOS/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/iOS/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | using Foundation;
6 | using UIKit;
7 |
8 | namespace XamarinReduxDemo.iOS
9 | {
10 | public class Application
11 | {
12 | // This is the main entry point of the application.
13 | static void Main(string[] args)
14 | {
15 | // if you want to use a different Application Delegate class from "AppDelegate"
16 | // you can specify it here.
17 | UIApplication.Main(args, null, "AppDelegate");
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/iOS/Resources/cities@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/iOS/Resources/cities@2x.png
--------------------------------------------------------------------------------
/iOS/Resources/users@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aspnetde/XamarinReduxDemo/3093aa699493ca4eafe52d8b49f23bfe7e528e84/iOS/Resources/users@2x.png
--------------------------------------------------------------------------------
/iOS/XamarinReduxDemo.iOS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | iPhoneSimulator
7 | {87F819BE-C59B-4BF6-8614-CD307F064366}
8 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
9 | Exe
10 | XamarinReduxDemo.iOS
11 | XamarinReduxDemo.iOS
12 | Resources
13 |
14 |
15 | true
16 | full
17 | false
18 | bin\iPhoneSimulator\Debug
19 | DEBUG;ENABLE_TEST_CLOUD;
20 | prompt
21 | 4
22 | iPhone Developer
23 | true
24 | true
25 | true
26 | 36393
27 | None
28 | x86_64
29 | HttpClientHandler
30 | x86
31 |
32 |
33 | pdbonly
34 | true
35 | bin\iPhone\Release
36 | prompt
37 | 4
38 | iPhone Developer
39 | true
40 | Entitlements.plist
41 | SdkOnly
42 | ARM64
43 | HttpClientHandler
44 | x86
45 |
46 |
47 | pdbonly
48 | true
49 | bin\iPhoneSimulator\Release
50 | prompt
51 | 4
52 | iPhone Developer
53 | true
54 | None
55 | x86_64
56 | HttpClientHandler
57 | x86
58 |
59 |
60 | true
61 | full
62 | false
63 | bin\iPhone\Debug
64 | DEBUG;ENABLE_TEST_CLOUD;
65 | prompt
66 | 4
67 | iPhone Developer
68 | true
69 | true
70 | true
71 | true
72 | true
73 | Entitlements.plist
74 | 16560
75 | SdkOnly
76 | ARM64
77 | HttpClientHandler
78 | x86
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll
87 |
88 |
89 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll
90 |
91 |
92 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll
93 |
94 |
95 | ..\packages\Xamarin.Forms.2.5.0.121934\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll
96 |
97 |
98 | ..\packages\MvvmNano.Core.3.2.0\lib\netstandard2.0\MvvmNano.Core.dll
99 |
100 |
101 | ..\packages\MvvmNano.TinyIoC.3.2.0\lib\netstandard2.0\MvvmNano.TinyIoC.dll
102 |
103 |
104 | ..\packages\MvvmNano.Forms.3.2.0\lib\netstandard2.0\MvvmNano.Forms.dll
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | {73705DA3-6A6E-4B33-8902-CB8B258C10CF}
129 | XamarinReduxDemo.Core
130 |
131 |
132 | {F69F0FAB-C5F5-4E1D-8101-C523BEB2651A}
133 | XamarinReduxDemo.Store
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/iOS/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------