Skip to content

Commit

Permalink
Add an initial hack at an iOS ISuspensionHost
Browse files Browse the repository at this point in the history
  • Loading branch information
anaisbetts committed Mar 20, 2013
1 parent 93911af commit c50b4e0
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
132 changes: 132 additions & 0 deletions ReactiveUI.Mobile/AutoSuspendAppDelegate.cs
@@ -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>();
});
}
}
}

85 changes: 85 additions & 0 deletions ReactiveUI.Mobile/ReactiveUI.Mobile_Monotouch.csproj
@@ -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>
1 change: 1 addition & 0 deletions ReactiveUI/Properties/AssemblyInfo.cs
Expand Up @@ -41,3 +41,4 @@
[assembly: InternalsVisibleTo("ReactiveUI.Android_Monodroid")]
[assembly: InternalsVisibleTo("ReactiveUI.Mobile_WinRT")]
[assembly: InternalsVisibleTo("ReactiveUI.Mobile_WP8")]
[assembly: InternalsVisibleTo("ReactiveUI.Mobile_Monotouch")]
18 changes: 18 additions & 0 deletions ReactiveUI_Monotouch.sln
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit c50b4e0

Please sign in to comment.