Mocast Postmortem Frank A. Krueger Seattle Mobile .NET July 1, 2014
Aug 27, 2014
Mocast PostmortemFrank A. Krueger
Seattle Mobile .NET July 1, 2014
What’s a Postmortem?After a project finishes in a big production for a movie, video game, or large project. Everyone gets together to discuss in a meeting what went wrong and what could be done better. The issues brought up are almost always completely ignored by management and they continue to make the same incompetent mistakes they always have.
- Urban Dictionary
What’s a Mocast?
• Podcast player
• Thumb-friendly UI
• Emphasizes the full catalog
• ADD-friendly UI
What Went Right? !
What Went Wrong?
Right: UI Design
Goal: Make it iOS 7-y
Right: UI DesignGoal: Make it iOS 7-y
Goal: Non-modal (no Now Playing screen, no Episode screen)
Right: UI Design
Right: UI Design
Right: UI Design
Right: UI Design
Right: Reliance on Bindings
INotifyCollectionChanged
INotifyPropertyChanged
Right: Reliance on Bindings
• Producers:
• ObservableCollection
• ObservableQuery!
• Consumers:
• ObservableTableView
Right: Reliance on Bindings• A query that changes over time and
depends on other collections and queries
• Emits minimal CollectionChanged events
• Automatic Update Throttling
• General Purpose - you put anything into the “query”
Right: iCloud Syncing
• Mocast uses iCloud Key-Value store
• Very simple API, very simple update model
Right: iCloud Syncing
Right: Async Everywhere• Async is the greatest thing to happen to UI
development since Visual Basic!announceTask = AnnounceNewEpisodeAsync (episode, streaming); !beginSeeking = true; beginSeekingPosition = GetPlaybackPosition (ue); await BeginSeekingAsync (); if (announceTask != null) { await announceTask; announceTask = null; } player.Rate = BaseRate; !UpdatePositionData (); UpdatePlaybackInfo ();
Wrong
Wrong: The “Episode Class”• Oh my, I don’t have an Episode class!
• Instead, 3 classes comprise “Episode” based upon the source of data:
• UserEpisode has all the data a user can control (play position, tags, etc.) and is sync’d with iCloud
• DataEpisode has all the info from the podcast feed
• DownloadEpisode has information regarding its download status
• Each implements IEpisodeReference so they can be correlated
Wrong: The “Episode Class”
Doesn’t Work :-(
Wrong: The “Episode Class”
Wrong: The “Episode Class”
I allowed the data source API to dictate my domain model :-(
Wrong: The “Episode Class”The Fix
Wrong: The “Episode Class”The Fix
Wrong: Threaded ObservableQueries
• To do modern UI programming, you need to do as much / all work asynchronously
• My ObservableQuery started, naïvely, as thread-dumb
• Eventually, had to make the class itself thread-safe but did not require thread-safe queries so couldn’t automatically run them on separate threads
• Result: UI freezes under very heavy load
Wrong: Threaded ObservableQueries
The FixInstead of:
!ObservableQuery (Func<T> source)!
!I should have:
!ObservableQuery (Func<Task<T>> asyncSource)
Wrong: iCloud Sync
• Wonderful because it’s simple
• BUT very small storage size:
• 1,000 keys
• 1 MB data
Wrong: iCloud Sync
• Use CloudKit!
The Fix
Wrong: Manual Entity Binding
Wrong: Manual Entity BindingThe Fix
?
Wrong: Manual Entity BindingThe Fix
There just aren’t many good declarative data binders out there:
!My Bind Library
That Reactive Library MvvmCross
BindableObject / Dependency Properties (haha)
• Require re-architecting around their pattern
• Make for great demos, but have real-world issues:
• async
• reference cleanup
• throttling
• deep hierarchies
• greedy events
• …
Wrong: Manual Entity Binding
Thank you!
• Mocast will be available on iPhone/iPod in a couple weeks