Permalink
Browse files

Add an initial hack at an iOS ISuspensionHost

  • Loading branch information...
1 parent 93911af commit c50b4e0a10bf068b1da80f03c4427b07d7394bcd @paulcbetts paulcbetts committed Mar 20, 2013
@@ -0,0 +1,132 @@
+using System;
+using System.Linq;
+using System.Reactive;
+using System.Reactive.Linq;
+using MonoTouch.Foundation;
+using MonoTouch.UIKit;
+using System.Reactive.Subjects;
+using System.Collections.Generic;
+using System.Reactive.Disposables;
+using ReactiveUI.Routing;
+
+namespace ReactiveUI.Mobile
+{
+ class CocoaSuspensionHost : ISuspensionHost
+ {
+ public IObservable<Unit> IsLaunchingNew { get { return ((AutoSuspendAppDelegate)UIApplication.SharedApplication.Delegate).SuspensionHost.IsLaunchingNew; } }
+ public IObservable<Unit> IsResuming { get { return ((AutoSuspendAppDelegate)UIApplication.SharedApplication.Delegate).SuspensionHost.IsResuming; } }
+ public IObservable<Unit> IsUnpausing { get { return ((AutoSuspendAppDelegate)UIApplication.SharedApplication.Delegate).SuspensionHost.IsUnpausing; } }
+ public IObservable<IDisposable> ShouldPersistState { get { return ((AutoSuspendAppDelegate)UIApplication.SharedApplication.Delegate).SuspensionHost.ShouldPersistState; } }
+ public IObservable<Unit> ShouldInvalidateState { get { return ((AutoSuspendAppDelegate)UIApplication.SharedApplication.Delegate).SuspensionHost.ShouldInvalidateState; } }
+
+ public void SetupDefaultSuspendResume(ISuspensionDriver driver = null)
+ {
+ var app = (AutoSuspendAppDelegate) UIApplication.SharedApplication.Delegate;
+ app.setupDefaultSuspendResume(driver);
+ }
+ }
+
+ public abstract class AutoSuspendAppDelegate : UIApplicationDelegate, IEnableLogger
+ {
+ readonly Subject<UIApplication> _finishedLaunching = new Subject<UIApplication>();
+ readonly Subject<UIApplication> _activated = new Subject<UIApplication>();
+ readonly Subject<UIApplication> _willTerminate = new Subject<UIApplication>();
+
+ internal SuspensionHost SuspensionHost;
+
+ readonly Subject<IApplicationRootState> _viewModelChanged = new Subject<IApplicationRootState>();
+ IApplicationRootState _ViewModel;
+
+ public IApplicationRootState ViewModel {
+ get { return _ViewModel; }
+ set {
+ if (_ViewModel == value) return;
+ _ViewModel = value;
+ _viewModelChanged.OnNext(value);
+ }
+ }
+
+ public IDictionary<string, string> LaunchOptions { get; protected set; }
+
+ public AutoSuspendAppDelegate()
+ {
+ var host = new SuspensionHost();
+ host.IsLaunchingNew = Observable.Never<Unit>();
+ host.IsResuming = _finishedLaunching.Select(_ => Unit.Default);
+ host.IsUnpausing = _activated.Select(_ => Unit.Default);
+
+ var untimelyDeath = new Subject<Unit>();
+ AppDomain.CurrentDomain.UnhandledException += (o,e) => untimelyDeath.OnNext(Unit.Default);
+
+ host.ShouldInvalidateState = untimelyDeath;
+ host.ShouldPersistState = _willTerminate.Select(app => {
+ var taskId = app.BeginBackgroundTask(new NSAction(() => {}));
+ return Disposable.Create(() => app.EndBackgroundTask(taskId));
+ });
+
+ SuspensionHost = host;
+ }
+
+ public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+ {
+ if (launchOptions != null) {
+ LaunchOptions = launchOptions.Keys.ToDictionary(k => k.ToString(), v => launchOptions[v].ToString());
+ } else {
+ LaunchOptions = new Dictionary<string, string>();
+ }
+
+ // NB: This is run in-context (i.e. not scheduled), so by the time this
+ // statement returns, UIWindow should be created already
+ _finishedLaunching.OnNext(application);
+
+ return true;
+ }
+
+ public override void OnActivated(UIApplication application)
+ {
+ _activated.OnNext(application);
+ }
+
+ public override void WillTerminate(UIApplication application)
+ {
+ _willTerminate.OnNext(application);
+ }
+
+ internal void setupDefaultSuspendResume(ISuspensionDriver driver)
+ {
+ driver = driver ?? RxApp.GetService<ISuspensionDriver>();
+
+ var window = new UIWindow(UIScreen.MainScreen.Bounds);
+ _viewModelChanged.Subscribe(vm => {
+ var frame = RxApp.GetService<IViewFor>("InitialPage");
+ frame.ViewModel = vm;
+
+ window.RootViewController = (UIViewController)frame;
+ window.MakeKeyAndVisible();
+ });
+
+ SuspensionHost.ShouldInvalidateState
+ .SelectMany(_ => driver.InvalidateState())
+ .LoggedCatch(this, Observable.Return(Unit.Default), "Tried to invalidate app state")
+ .Subscribe(_ => this.Log().Info("Invalidated app state"));
+
+ SuspensionHost.ShouldPersistState
+ .SelectMany(x => driver.SaveState(ViewModel).Finally(x.Dispose))
+ .LoggedCatch(this, Observable.Return(Unit.Default), "Tried to persist app state")
+ .Subscribe(_ => this.Log().Info("Persisted application state"));
+
+ SuspensionHost.IsResuming
+ .SelectMany(x => driver.LoadState<IApplicationRootState>())
+ .LoggedCatch(this,
+ Observable.Defer(() => Observable.Return(RxApp.GetService<IApplicationRootState>())),
+ "Failed to restore app state from storage, creating from scratch")
+ .ObserveOn(RxApp.DeferredScheduler)
+ .Subscribe(x => ViewModel = x);
+
+ SuspensionHost.IsLaunchingNew.Subscribe(_ => {
+ ViewModel = RxApp.GetService<IApplicationRootState>();
+ });
+ }
+ }
+}
+
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{9091337A-9E94-4DBD-801E-15E10DB72FFC}</ProjectGuid>
+ <ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <RootNamespace>ReactiveUI.Mobile</RootNamespace>
+ <IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
+ <AssemblyName>ReactiveUI.Mobile_Monotouch</AssemblyName>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>True</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>False</Optimize>
+ <OutputPath>bin\Debug\Monotouch</OutputPath>
+ <DefineConstants>DEBUG; MONO; UIKIT</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>False</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>none</DebugType>
+ <Optimize>True</Optimize>
+ <OutputPath>bin\Release\Monotouch</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>False</ConsolePause>
+ <DefineConstants>MONO; UIKIT</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Core" />
+ <Reference Include="monotouch" />
+ <Reference Include="System.Reactive.Core">
+ <HintPath>..\ext\ios\System.Reactive.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.Interfaces">
+ <HintPath>..\ext\ios\System.Reactive.Interfaces.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.Linq">
+ <HintPath>..\ext\ios\System.Reactive.Linq.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.PlatformServices">
+ <HintPath>..\ext\ios\System.Reactive.PlatformServices.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="Xamarin.Mobile">
+ <HintPath>..\ext\ios\Xamarin.Mobile.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Resources\" />
+ <Folder Include="Properties\" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ServiceLocationRegistration.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Geolocation.cs" />
+ <Compile Include="Interfaces.cs" />
+ <Compile Include="SuspensionHost.cs" />
+ <Compile Include="AutoSuspendAppDelegate.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\ReactiveUI\ReactiveUI_Monotouch.csproj">
+ <Project>{9091337A-9E94-4DBD-801E-05E1FDA78FFC}</Project>
+ <Name>ReactiveUI_Monotouch</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ReactiveUI.Xaml\ReactiveUI.Xaml_Monotouch.csproj">
+ <Project>{9891337A-9E94-4DBD-801E-05E1FDA78FFC}</Project>
+ <Name>ReactiveUI.Xaml_Monotouch</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ReactiveUI.Routing\ReactiveUI.Routing_Monotouch.csproj">
+ <Project>{9091337B-9E94-4DBD-801E-05E1FDA78FFC}</Project>
+ <Name>ReactiveUI.Routing_Monotouch</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
@@ -41,3 +41,4 @@
[assembly: InternalsVisibleTo("ReactiveUI.Android_Monodroid")]
[assembly: InternalsVisibleTo("ReactiveUI.Mobile_WinRT")]
[assembly: InternalsVisibleTo("ReactiveUI.Mobile_WP8")]
+[assembly: InternalsVisibleTo("ReactiveUI.Mobile_Monotouch")]
View
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.Cocoa_Monotouch"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOSPlayground", "iOSPlayground\iOSPlayground.csproj", "{420CF325-38B9-4B30-8978-D519EC40B6A8}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveUI.Mobile_Monotouch", "ReactiveUI.Mobile\ReactiveUI.Mobile_Monotouch.csproj", "{9091337A-9E94-4DBD-801E-15E10DB72FFC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -55,6 +57,22 @@ Global
{9091337A-9E94-4DBD-801E-05E1FDA78FFC}.Release|iPhone.Build.0 = Release|Any CPU
{9091337A-9E94-4DBD-801E-05E1FDA78FFC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{9091337A-9E94-4DBD-801E-05E1FDA78FFC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.AppStore|iPhone.Build.0 = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Release|iPhone.Build.0 = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {9091337A-9E94-4DBD-801E-15E10DB72FFC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{9091337A-9E94-4DBD-801E-15E1FDA78FFC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{9091337A-9E94-4DBD-801E-15E1FDA78FFC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{9091337A-9E94-4DBD-801E-15E1FDA78FFC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU

0 comments on commit c50b4e0

Please sign in to comment.