Making Instant C# Viable – Part 1

At some point several weeks ago, I came across Bret Victor‘s presentation “Inventing on Principle”. In it, among many amazing prototypes, Bret demonstrates a little app in which he can write code and see the live running results next to the code (starts at 16:47).

Bret Victor – Inventing on Principle from CUSEC on Vimeo.

After seeing this little app, my first thought was simply that this was genius; the next was “I want this for C#”. Now, this idea demos really well and is fairly simple to prototype, but I find there’s a lot of problems you start running into when you try to make an actual tool out of it; so I started a research project currently called “Instant”. The plan is to blog about the evolution of the project as I work towards making something real out of it, including what problems I run into and how I solve them. This will serve as not only a running set of examples on using Roslyn, but hopefully an interesting exploration of the space. We’ll start by matching Bret Victor’s demo, and then take it from there (hopefully to a Visual Studio and MonoDevelop plugin). It’s worth noting that I have absolutely no experience with compilers, runtime internals, or any of that sort of thing that you might expect are necessary for this. As a result, everything is very much a work in progress.

Here’s a working prototype roughly matching Bret’s demo:


Get Adobe Flash player

In this demo you can see I implement binary search and I can see the results of the code execution on the right. When I change the search parameter I can instantly see that there’s a bug, so I fix the loop and see the new behavior immediately. So how did I do this?

The first thing we’ll need is a data structure of the code itself. I knew about Roslyn already, and while I realized the importance of the project, I never really personally had a need for it – until now. What I knew going in was that Roslyn was going to give me an object representation of the code. What I didn’t know is that they have a ScriptEngine class, and it rocks. My initial thought was that I would lift all the local variables from a single test method, run each statement one at a time through the script engine, and then read from the hosting class I’d created what the current variable states where. So I dug in and wrote a SyntaxRewriter that did the lifting for me, and then it occurred to me how much I was over-complicating things.

With the ScriptEngine class, you can execute code in the context of a Session. Session allows you the ability to supply a “host object”, which you initialize and then pass to the session. In the code of your “script”, you can call methods on this host object as if the code you’re running was just another method on the same class. This, combined with the ability to rewrite the class gives us a simple solution: Rewrite the code to include calls to methods that log the values. The solution that I felt covered the most scenarios on it’s own was to replace all assignments with a call to a logging method that returns the object.

Basically, turn:

int x = 5;

into:

int x = LogObject ("x", 5);

We’ll use two classes to get there, one to act as the rewriter, the other to act has the host object (or “logger”). The logger, as you might imagine, is quite simple:

public class Logger
{
	public string Log
	{
		get { return this.builder.ToString(); }
	}
 
	public T LogObject<T> (string name, T value)
	{
		this.builder.AppendLine (name + " = " + value);
		return value;
	}
 
	private StringBuilder builder = new StringBuilder();
}

Roslyn provides us with a SyntaxRewriter class that uses the visitor pattern to make it very simple to get up and running rewriting code. If you’ve ever used ExpressionVisitor implementing a LINQ provider, you should feel right at home. We’re given us two ways to construct new syntax: 1) Use static methods and pass in object representations of all of the elements making up the syntax or 2) Pass in a string representing just the piece of syntax we want and have Roslyn parse it for us. #1 is frankly a bit cumbersome currently, so we’ll go with #2.

So, for our previous example, we want to go from a variable name and value to a log call for them:

internal class LoggingRewriter
	: SyntaxRewriter
{
	private ExpressionSyntax GetLogExpression (string name, SyntaxNode value)
	{
		return GetLogExpression (name, value.ToString());
	}
 
	private ExpressionSyntax GetLogExpression (string name, string value)
	{
		return Syntax.ParseExpression ("LogObject (\"" + (name ?? "null") + "\", " + value + ")");
	}
}

Now we need to actually use it, so we’ll start with variable declarations (which are not the same as assignments):

protected override SyntaxNode VisitVariableDeclarator (VariableDeclaratorSyntax node)
{
	// We don't care about: int x;
	if (node.InitializerOpt == null)
		return base.VisitVariableDeclarator (node);
 
	EqualsValueClauseSyntax equals = node.InitializerOpt;
 
	// Get a log expression using the variable name and the value it's being assigned.
	ExpressionSyntax logExpression = GetLogExpression (node.Identifier.ValueText, equals.Value);
 
	// Update the immutable syntax object with value expression
	equals = equals.Update (equals.EqualsToken, logExpression);
 
	// Update the declaration with our new initialization expression
	return node.Update (node.Identifier, null, equals);
}

Now that we have a simple rewriter and our logger, let’s actually see it work. I’ll go ahead and assume you know how to set up a simple UI with two textboxes.

public class EditorViewModel
{
	public EditorViewModel()
	{
		// We need to set up our 'scripting' environment,
		// we'll specify assembly references and using
		// statements here for now
		this.scripting = new CommonScriptEngine (
			new [] { // References
				typeof(string).Assembly // mscorlib
				typeof(Logger).Assembly // our app
			},
			new[] { // Using statements
				"System"
			});
	}
 
	// ... UI stuff ... //
 
	private CommonScriptEngine scripting;
	private void ProcessInput()
	{
		var logger = new Logger();
 
		// Parse the code we've typed into a node that we can
		// manipulate
		SyntaxNode root = Syntax.ParseCompilationUnit (Input);
 
		var rewriter = new LoggingRewriter();
		// Here we hand our rewriter the node that we just
		// parsed and we'll get back the rewritten node.
		root = rewriter.Visit (root);
 
		try
		{
			// Create a new scripting session with the host
			// object that we created to log our state
			Session s = Session.Create (logger);
 
			// We hand the scripting engine our final code
			// to run and the scripting session to use.
			this.scripting.Execute (root.ToString(), s);
		}
		catch (CompilationErrorException cex)
		{
			// We'll ignore compilation errors, there'll be
			// plenty while you type. Outputting this, however,
			// is a great way to debug your syntax rewriter.
		}
		catch (Exception ex)
		{
			// We want to show any other errors, like those from
			// the code that you wrote.
			Output = ex.ToString();
			return;
		}
 
		// At this point we have something usable, show the output from
		// the logging host object.
		Output = logger.Log;
	}
}

And there we go, we can see any time we declare a new variable and what it’s value is. Now that we’ve got the infrastructure all set up, let’s go a little farther and handle all assignments. Assignments are binary expressions, they have a left (the variable) and a right (the value it’s being assigned to) side to them, so we’ll check each binary expression to see if it’s an assignment and rewrite it’s value appropriately:

protected override SyntaxNode VisitBinaryExpression (BinaryExpressionSyntax node)
{
	switch (node.Kind)
	{
		case SyntaxKind.AssignExpression:
 
			IdentifierNameSyntax identifierSyntax = (IdentifierNameSyntax) node.Left;
 
			// We just want the variable name
			string name = identifierSyntax.PlainName;
 
			// Get a logging expression for the value
			ExpressionSyntax logExpression = GetLogExpression (name, node.Right);
 
			// Update the node with our logging expression
			return node.Update (node.Left, node.OperatorToken, logExpression);
 
		default:
			return base.VisitBinaryExpression (node);
	}
}

Update (4/18): The above code has a bug! This works great for simple expressions like x = 5, but what about something more complex like

i < x = 5

In this case, the left side of the binary expression is:

i < x

Casting this as a name will obviously not work, so we need to do a search for the name. We’ll look at the right side of any binary expression we come across and return once we find an IdentifierName:

private IdentifierNameSyntax FindIdentifierName (ExpressionSyntax expression)
{
	// If we're at a name, we can just return it
	IdentifierNameSyntax name = expression as IdentifierNameSyntax;
	if (name != null)
		return name;
 
	// If we find a binary expression, look at the right side
	BinaryExpressionSyntax binaryExpression = expression as BinaryExpressionSyntax;
	if (binaryExpression != null)
		return FindIdentifierName (binaryExpression.Right);
 
	// If we don't know what to do, return null so we explode and we can find the bug ;)
	return null;
}

Now, we’ll replace a line in our previous code to use this:

IdentifierNameSyntax identifierSyntax = FindIdentifierName (node.Left);

And there we have logging simple assignments! In part two, we’ll expand upon this with more assignments and pre/postfix unary operators. Now, if you’d like to play with this now, install the Roslyn CTP and grab the prototype source from GitHub. Fair warning though, it’s changing rapidly and may not work at any given point.

Note: This post is based on the first Roslyn CTP, I intend to update it when a new version is released.

Introducing the Xamarin Mobile API Preview

Things have been a little quiet from me lately, both on blog series I started and projects I started. The reason is that I have been very busy starting an exciting new position with Xamarin. Today I’m happy to give a preview of my first project with Xamarin.

With MonoTouch and Mono for Android, you can already share a huge portion of your code across iOS, Android and Windows Phone. There is, however, this middle ground of APIs that are present in all three platforms, but you can’t share your code that utilizes them. You currently have to write separate sets of code to use geolocation, the accelerometer, contacts, etc on these various platforms and we want to fix that. The Xamarin Mobile API preview contains a new set of APIs designed and implemented to support these features evenly across iOS, Android and Windows Phone.

We want your feedback to help guide us in this endeavor, so today we’re releasing a preview. The preview contains a unified geolocation API with implementations on iOS and Android. We’ll be releasing a Windows Phone implementation at some point in the future. Try it out and give us your feedback, we want to know what you think! Let us know of any pain points in the API, if something doesn’t work, etc. We also want to know what other areas you care most about us unifying next, whether it be contacts, notifications, etc.

As this is a preview, please keep in mind that APIs may change, things may be broken and not all of the docs are filled out. For Android, currently a minimum API level of 7 is required. Additionally on Android, heading is disabled and Geolocator’s constructor takes an argument of Context.

You can download the preview here and let us know what you think or want to see here. If you run into a bug, please file it here.

Update 12/5: The package has been updated to work with Mono for Android 4.0 (and now requires it).

Avoiding callbacks to the UI thread with async and WinRT

You probably already know that async operates by using the SynchronizationContext of the call site to invoke its callback. This behavior is great and makes sense for the vast majority of scenarios. There are a few, however, that it is not ideal for and it centers mostly around library developers like myself. If you didn’t know this, you may want to go watch this fantastic talk that contains a deep dive of async: The zen of async: Best practices for best performance. In fact, if you are interested in async with .NET, I recommend you watch it regardless.

I write a lot of socket based software, so I started writing my own library to provide a consistent (and higher level) API across all platforms and naturally I want it to work on WinRT. One of the first things I looked into when I got my hands on the WinRT bits was the replacement Socket API, which contains several async methods. In Tempest, once you receive some data it constructs message objects from the data, performing hash checking and sometimes even decryption. This is not code I want to be running on the UI thread for every single message.

So, I started looking around for solutions. Most I came up with involved not using the async keyword, calling out to the ThreadPool from the continuation, or using an outside thread to initiate the call. The simplest and most efficient answer I could come up with is to just not use async, but I didn’t want to accept that. Both Task.ContinueWith and IAsyncInfo.Start() do not pay attention to the SynchronizationContext, that is a feature of await:

And, just as a reminder and proof:

The same demonstration could be done for using .ContinueWith() vs. await on a Task. What I thought was that I’d be relegated to using these for advanced purposes, until I watched the talk I linked above. There’s a new method on Task, called ConfigureAwait(bool) which enables you to ignore the SynchronizationContext. What about IAsyncInfo though? There is a StartAsTask extension method on it after all, so let’s see if that works:

As it turns out, it does. Unfortunately there’s one small difference for which I can only speculate as to the reason. When you use the Task  and disable the captured context, you end up on a ThreadPool thread. However, when you use the IAsyncOperation returned directly, you still end up on a different thread, but not a ThreadPool one. One possible logical explanation is that it’s actually using WinRT’s ThreadPool to execute:

We can see here that WinRT’s ThreadPool threads do not get identified by Thread.IsThreadPoolThread.

Getting back to the code, although still an improvement on using ContinueWith, it’s still quite long. Here’s a little extension method that combines the two by just adding an overload for StartAsTask to take the same argument as ConfigureAwait.

public static ConfiguredTaskAwaitable<T> StartAsTask<T> (
	this IAsyncOperation<T> self, bool continueOnContext)
{
	if (self == null)
		throw new ArgumentNullException("self");
 
	return self.StartAsTask()
		.ConfigureAwait (continueOnContext);
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
	await DeviceInformation.FindAllAsync().StartAsTask (false);
}

So we can see that it is possible to use async without dumping back to the UI thread for calls originating from the UI thread. In some scenarios it still may not be desirable, depending on what you’re doing and your desired performance. The async keyword introduces a non-trivial amount of extra code, so if you’re concerned with the performance, I once again recommend you watch this talk.

TL;DR: Use await IAsyncInfo.StartAsTask().ConfigureAwait(false);

Converting Tempest to run on WinRT: Part 1

First, a short introduction to Tempest. I had avoided posting about this project in the past because I wanted the first post to be a quick tutorial and announcement of a beta. However, this is a perfect opportunity to really dive into some of the lower level APIs of WinRT and share the experience.

Tempest is designed to be a simple application protocol messaging library that works everywhere. This is to say that it gives you a simple way to define messages and lets you select how these messages are transported. Furthermore, the library is built to work on .NET 3.5, .NET 4, Mono 2.10, Silverlight 4 and Windows Phone 7.? “Mango”. Though, at this point in time, I’d consider Tempest to be an early alpha for .NET and Mono. Silverlight and Windows Phone 7 support compiles, but assuredly does not work yet.

Getting it to compile

For Part 1 we’ll talk about even getting Tempest to compile. Keep in mind that the vast majority of the code must still work on standard .NET, so converting over to WinRT APIs completely isn’t an option. To support the various platforms, there’s a number of compiler defines already present in Tempest:

  • SAFE – Disables any unsafe optimizations (unsafe, System.Reflection.Emit)
  • NET_4 – Enables .NET 4 specific code. This is mostly optimizations from System.Collections.Concurrent and using SpinWait, but does include one very useful API based on Task.
  • SILVERLIGHT
  • WINDOWS_PHONE

WinRT does not contain System.Reflection.Emit, so first I turned SAFE on. Type.IsValueType, which is used primarily in MutableLookup`2 from Cadenza, has been moved to TypeInfo. For a quick fix, I replaced the check for it with:

if (default(TKey) == null)

Simple enough change, but the error list at this point is still quite daunting. Here’s a short list of what was affecting me:

Networking

As you might imagine, for a networking library, the first is a huge blow. As WinRT has its own implementation of sockets, I’ll have to write a new transport provider. Fortunately Tempest is designed to be able to do this, so for the moment I’ll just remove the existing network implementation altogether.

Unfortunately that wasn’t the only usages I had, as System.Net.EndPoint was used throughout my contracts for connection targets. So either I can write WinRT only implementations of EndPoint, or I can write my own type and take control. I opted to write my own types (prototype design): ConnectionTarget, DnsConnectionTarget, IPConnectionTarget.

Reflection

When I was working on this last night, I was under the impression that Type had simply been gutted. Today I know that many of the members I had been using were moved to TypeInfo. There is an extension method in System.Reflection on Type called GetTypeInfo() that will get you the TypeInfo instance. In order to facilitate code compatibility with normal .NET, I added my own little extension:

#if !WINRT
public static Type GetTypeInfo (this Type type)
{
	return type;
}
#endif

Using this, I can just call .GetTypeInfo() on my type instances in either framework and all should be well. It’s not terribly efficient as I can’t store instances to the proper type easily/cleanly most of the time, but it gets the ball rolling.

Despite TypeInfo containing most of what was originally on Type a few things are still missing, such as .GetMembers(). I was instead able to use TypeInfo.DeclaredProperties and its friends with LINQ to get to the members I wanted, including filtering I was originally doing in LINQ after GetMembers().

Serialization

To be perfectly honest, where most of the reflection used and all of .NET’s built in serialization is, is in a custom automatic serialization system. This is probably the buggiest and worst designed aspect of Tempest. Last night I made the decision that for WinRT (thinking at the time reflection was gutted), I’d only support Tempest’s ISerializable and ISerializer<T> interfaces. For now I’m going to leave that restriction in place until I can get it sorted out.

While System.Runtime.Serialization is present with DataContractAttribute and friends, SerializableAttribute and System.Runtime.Serialization.Formatters.* are not. These portions of code were optional and already disabled for Silverlight, so I just added the WINRT flag to the check.

Miscellaneous

The types using System.Buffer weren’t used anywhere except for the existing networking implementation, so I simply removed them from the WinRT build for now. Having gotten things to compile at this point, I decided to see what would happen if I turned on NET_4, and to my surprise everything compiled as is. It appears that WinRT’s support of the baser types (like collections) is quite good. Where things start talking to the OS (Sockets) is where things start to get replaced, and old crufty systems (SerializableAttribute) seem to have been trimmed out.

Now that I have the basic system compiling, I need to implement a transport using Windows.Networking.Sockets, which will be Part 2.

Using WinRT from .NET

As it turns out, it is possible to use the WinRT APIs from standard .NET. You’ll need the full Visual Studio 2011 preview installed on the Windows 8 Developer Preview.

First, create a new WPF project. Then, go to add references and press “browse”. Navigate to C:\Program Files (x86)\Windows Kits\8.0\Windows Metadata

C:\Program Files (x86)\Windows Kits\8.0\Windows Metadata

Make sure you select “All Files”. Note that VS11 gives you an error if you try to add more than one at a time, so you’ll have to add each individually. We’re going to show a quick app that lists devices, so we’ll need a couple of these namespaces: windows.foundation.winmd and windows.devices.enumeration.

First we’ll make a little view model:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Foundation;
 
namespace WpfApplication1
{
    public class MainWindowViewModel
        : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
 
        public MainWindowViewModel()
        {
            var operation = DeviceInformation.FindAllAsync();
            operation.Completed = CompletedFindDevices;
            operation.Start();
        }
 
        public IEnumerable<DeviceInformation> Devices
        {
            get { return this.devices; }
            private set
            {
                if (this.devices == value)
                    return;
 
                this.devices = value;
                OnPropertyChanged ("Devices");
            }
        }
 
        private IEnumerable<DeviceInformation> devices;
 
        private void OnPropertyChanged (string propertyName)
        {
            var changed = PropertyChanged;
            if (changed != null)
                changed (this, new PropertyChangedEventArgs (propertyName));
        }
 
        private void CompletedFindDevices (IAsyncOperation<DeviceInformationCollection> asyncInfo)
        {
            Devices = asyncInfo.GetResults();
        }
    }
}

And then the Xaml (don’t forget to hook up the datacontext to the view model in your code behind):

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{Binding Devices}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

And voila:

This doesn’t cover being able to implement the various platform contracts, but it’s a start.

Update: Apparently the opposite is true as well.. kind of. It’s possible to reference your existing .NET assemblies and use them in a WinRT application. It remains to be seen or heard whether that will be allowed for the App Store, but it does work in practice. The gotcha is that you can’t reference the missing pieces of the BCL, so if you need a type there (System.Net.EndPoint for example), you’re still out of luck.

Update 2: The app certification utility fails WinRT apps calling unsupported .NET APIs. So, they’ll run, but you won’t get them on the store.

Code Monkey vs. Silverlight Audio Capture

I’ve been working on a new project (Symphony, https://github.com/ermau/Symphony) that is heavily media centric. The general idea is to provide better support to .NET for a whole range of multimedia scenarios, whether they’re recording and playing back audio, decoding a song, or what have you. In order to reach the broadest audience possible, I decided early on to include as much Silverlight support as I possibly could. Part of that supports is bringing Silverlight’s built in microphone support under Symphony’s unified API (which is, to say, wrapping it). My head and desk are no longer on speaking terms, but more to the point I’d love to know (at least in the case of Silverlight) if Microsoft bothers to get people who have experience in the area their assigned to write libraries for.

A lesson I learned the hard way in developing Gablarski was that available audio devices change, a lot. Some enable/disable themselves automatically when detecting a 3.5mm being connected/disconnected, others are USB devices that get plugged in and removed at will, so on and so forth. As any logical person would deduce from this truth, it becomes important to be able to track what device is currently the default, when that default changes, and when a device is removed. So let’s look at Silverlight’s APIs and see what’s available; first we have the CaptureDeviceConfiguration:

public static class CaptureDeviceConfiguration
{
	public static bool AllowedDeviceAccess { get; }

	public static bool RequestDeviceAccess();
	public static ReadOnlyCollection<VideoCaptureDevice> GetAvailableVideoCaptureDevices();
	public static ReadOnlyCollection<AudioCaptureDevice> GetAvailableAudioCaptureDevices();
	public static VideoCaptureDevice GetDefaultVideoCaptureDevice();
	public static AudioCaptureDevice GetDefaultAudioCaptureDevice();
}

Ok, so this gives us a way to get the current default and a list of all available devices. As you’ll notice, however, it does not provide an event notifying you that the default device has changed. I’ve found that the best user experience for voice based applications is to default to the default audio device and to track that device. If I manually set another device, the application will gladly remember what I’ve selected, but I should be given the choice of “default” which follows the current default instead of just the default at selection time. Without a notification to tell the application when this has changed, the developer is left with no good way to provide an excellent user experience. The best I could come up with to work around this is to poll the current default device in order to provide the notification that Symphony’s API demands. Having the notification at all is a positive outcome, but having to balance between how much delay between polls and how many extra cycles we’re wasting checking is undesirable. Here’s what I came up with:

public class SilverlightAudioCaptureDeviceProvider
{
	public SilverlightAudioCaptureDeviceProvider()
	{
		if (!CaptureDeviceConfiguration.AllowedDeviceAccess)
			CaptureDeviceConfiguration.RequestDeviceAccess();

		UpdateDevices();
		this.pollTimer.Interval = TimeSpan.FromMilliseconds (250);
		this.pollTimer.Tick += PollTimerOnTick;
		this.pollTimer.Start();
	}

	public event EventHandler DefaultDeviceChanged;

	public AudioCaptureDevice DefaultDevice
	{
		get;
		private set;
	}

	private readonly DispatcherTimer pollTimer = new DispatcherTimer();
	private AudioCaptureDevice lastDefaultDevice;

	private void PollTimerOnTick (object sender, EventArgs eventArgs)
	{
		UpdateDevices();
	}

	private void UpdateDevices()
	{
		AudioCaptureDevice defaultDevice = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
		if (DefaultDevice != defaultDevice) // This is actually a bug which I’ll explain later
		{
			DefaultDevice = defaultDevice;
			OnDefaultDeviceChanged (EventArgs.Empty);
		}
	}

	private void OnDefaultDeviceChanged (EventArgs e)
	{
		var changed = DefaultDeviceChanged;
		if (changed != null)
			changed (this, e);
	}
}

(This is not the Symphony API, I’ve reduced it to a decorator off sorts to provide these functions for any Silverlight use for the reader.)

Now that we have default device notifications, we need to check into notifications for devices becoming unavailable (which could be a user manually disabling the device in Windows, unplugging a USB mic, etc.) It’s obviously not on CaptureDeviceConfiguration class, so let’s check the AudioCaptureDevice class:

public class CaptureDevice
	: DependencyObject
{
	public static readonly DependencyProperty FriendlyNameProperty;
	public static readonly DependencyProperty IsDefaultDeviceProperty;

	public string FriendlyName { get; }
	public bool IsDefaultDevice { get; }
}

public sealed class AudioCaptureDevice
	: CaptureDevice
{
	public static readonly DependencyProperty AudioFrameSizeProperty;

	public int AudioFrameSize { get; set; }
	public ReadOnlyCollection<AudioFormat> SupportedFormats { get; }
	public AudioFormat DesiredFormat { get; set; }
}

So there’s no status changed events and there’s no properties to check if the device is even still alive. Let’s step back and take a look at a basic usage of this API. First you new up a CaptureSource:

public sealed class CaptureSource
	: DependencyObject
{
	public static readonly DependencyProperty VideoCaptureDeviceProperty;
	public static readonly DependencyProperty AudioCaptureDeviceProperty;

	public CaptureSource();

	public event EventHandler<ExceptionRoutedEventArgs> CaptureFailed;
	public event EventHandler<CaptureImageCompletedEventArgs> CaptureImageCompleted;

	public VideoCaptureDevice VideoCaptureDevice { get; set; }
	public AudioCaptureDevice AudioCaptureDevice { get; set; }
	public CaptureState State { get; }

	public void Start();
	public void Stop();
	public void CaptureImageAsync();
}

So, we take our audio device and assign CaptureSource.AudioCaptureDevice to it and call start. CaptureFailed looks promising, I would expect it to be raised if the device had disappeared before or after starting the capture. I can’t help but notice that there’s explicit methods for capturing a single image, but there’s no way from right here to get audio samples (or multiple images). In order to actually get the audio samples you then have to create (as there are no built-in implementations) and initialize an AudioSink:

public abstract class AudioSink
{
	public CaptureSource CaptureSource { get; set; }

	protected abstract void OnCaptureStarted();
	protected abstract void OnCaptureStopped();
	protected abstract void OnFormatChange (AudioFormat audioFormat);
	protected abstract void OnSamples ( long sampleTimeInHundredNanoseconds, long sampleDurationInHundredNanoseconds,
					byte[] sampleData);
	~AudioSink();
}

Here are some more alternatives, OnCaptureStopped() might be called if the device goes away during capture. So, I set up a test with all of these components, put breakpoints everywhere, started the capture and disabled a device while it was running. Can you guess which event/callback breakpoint was hit? I asked a few different people that question when I was attempting to do all of this and the answer is so ridiculous that no one guessed it their first try. The answer is NONE of them. If the device you’re capturing with goes away while you’re capturing, you received no notification whatsoever. No event raised, no callback fired, no 0 value arguments to OnSamples, no CaptureSource.State == CaptureState.Failed, no exception, nothing.

As before, this is a pretty critical factor of user experience. The user may not even know that the device just went away. I can just imagine the “Hey, can anyone hear me?” and subsequent “I’ve been talking for 30 minutes and no one could hear me..”. If you’ve used a VoIP application while playing games, you’ve probably done this before with a mute switch on your headset. So, there are two alternatives: We can poll the complete list of devices to see if the device is listed anymore, or we can have a timeout for OnSamples calls (as it does stop being called when the device goes away). Since we already have a poll for the default device, I decided to go with polling.

First, we’ll need a decorator to hold the status and event:

public class AudioCaptureDeviceEx
{
	public AudioCaptureDeviceEx (AudioCaptureDevice device)
	{
		if (device == null)
			throw new ArgumentNullException ("device");

		this.device = device;
	}

	public event EventHandler IsAvailableChanged;

	public bool IsAvailable
	{
		get { return this.isAvailable; }
		set
		{
			if (this.isAvailable == value)
				return;

			this.isAvailable = value;
			var changed = IsAvailableChanged;
			if (changed != null)
				changed (this, EventArgs.Empty);
		}
	}

	// 1:1 AudioCaptureDevice wrappings left out for brevity

	private readonly AudioCaptureDevice device;
	private bool isAvailable = true;
}

We’ll want a list of devices with our additions in the device provider:

private readonly Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx> devices =
	new Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx>();

public IEnumerable<AudioCaptureDeviceEx> AudioDevices
{
	get
	{
		lock (this.devices)
			return this.devices.Values.Where (d => d.IsAvailable).ToList();
	}
}

Now we need to update our polling routine:

private void UpdateDevices()
{
	AudioCaptureDeviceEx currentDefault = null;

	lock (this.devices)
	{
		var newDevices = new HashSet<AudioCaptureDevice> (CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices());
		foreach (var kvp in this.devices)
		{
			if (kvp.Value.IsDefaultDevice)
				currentDefault = kvp.Value;

			if (newDevices.Remove (kvp.Key))
			{
				kvp.Value.IsAvailable = true;
				continue;
			}

			this.devices[kvp.Key].IsAvailable = false;
		}

		foreach (AudioCaptureDevice device in newDevices)
		{
			var d = new AudioCaptureDeviceEx (device);
			if (d.IsDefaultDevice)
				currentDefault = d;

			this.devices.Add (device, d);
		}
	}

	if (DefaultDevice != currentDefault)
	{
		DefaultDevice = currentDefault;
		OnDefaultDeviceChanged (EventArgs.Empty);
	}
}

Seems like it should work, right? Yeah, I thought so too. In my tests (on my machine anyway), I discovered AudioCaptureDevice.IsDefaultDevice always returns false. You’d think there’d be an Assert.IsTrue (CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice().IsDefaultDevice) somewhere in their tests. So, I need to instead go back to querying CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice() and then reconcile the AudioCaptureDeviceEx instances. This is where I was in for another surprise; remember that bug I said I’d tell you about later? AudioCaptureDevice does not implement equality functions and new instances are returned on each of CaptureDeviceConfiguration’s methods, so there’s no other way to compare them but their “FriendlyName” (which is actually cut off, which makes it impossible to display an exact match to what’s in Windows). Comparing by their name will probably work most of the time, it’s definitely not ideal. So now we have:

private class AudioCaptureDeviceEqualityComparer
	: IEqualityComparer<AudioCaptureDevice>
{
	public static readonly AudioCaptureDeviceEqualityComparer Instance
		= new AudioCaptureDeviceEqualityComparer();

	public bool Equals (AudioCaptureDevice x, AudioCaptureDevice y)
	{
		if (x == y)
			return true;
		if (x == null || y == null)
			return false;

		return (x.FriendlyName == y.FriendlyName);
	}

	public int GetHashCode (AudioCaptureDevice obj)
	{
		if (obj == null)
			throw new ArgumentNullException ("obj");

		return obj.FriendlyName.GetHashCode();
	}
}

We’ll need to update the dictionary holding the devices:

private readonly Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx> devices =
	new Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx> (AudioCaptureDeviceEqualityComparer.Instance);

And finally the updated UpdateDevices:

private void UpdateDevices()
{
	AudioCaptureDevice currentDefault = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
	AudioCaptureDeviceEx currentDefaultEx = null;

	lock (this.devices)
	{
		var newDevices = new HashSet<AudioCaptureDevice> (CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices(),
							AudioCaptureDeviceEqualityComparer.Instance);
		foreach (var kvp in this.devices)
		{
			if (currentDefault != null && kvp.Value.FriendlyName == currentDefault.FriendlyName)
				currentDefaultEx = kvp.Value;

			if (newDevices.Remove (kvp.Key))
			{
				kvp.Value.IsAvailable = true;
				continue;
			}

			this.devices[kvp.Key].IsAvailable = false;
		}

		foreach (AudioCaptureDevice device in newDevices)
		{
			var d = new AudioCaptureDeviceEx (device);
			if (currentDefault != null && d.FriendlyName == currentDefault.FriendlyName)
				currentDefaultEx = d;

			this.devices.Add (device, d);
		}
	}

	if (DefaultDevice != currentDefaultEx)
	{
		DefaultDevice = currentDefaultEx;
		OnDefaultDeviceChanged (EventArgs.Empty);
	}
}

When I was wrapping AudioCaptureDevice for Symphony’s capture device contract, I discovered another fun bit. Symphony has it’s own definition for audio formats, so I have to keep a map of formats so I can pass the correct one to Silverlight. Ok, no problem, I’ll just call ToDictionary with a little conversion function I wrote, and.. ArgumentException. AudioCaptureDevice.SupportedFormats actually contains duplicates. I realize this is probably just directly reading from whatever API it’s sitting on, but when you’re trying to provide a simple API you should really clean up crap like this.

Let’s review

  • No notifications for default device changed
  • No notifications for devices added / removed
  • AudioCaptureDevice.FriendlyName is fixed width
  • AudioCaptureDevice.IsDefaultDevice just plain broken
  • AudioCaptureDevice isn’t equatable
  • AudioCaptureDevice.SupportedFormats contains duplicates

If someone handed me code like this for such a wide reaching project like Silverlight and told me it was done, I’d fire them. That it actually made it to production baffles me, somewhere a manager completely failed at their job. I’m really not sure how you can go this wrong, but the more I explore Silverlight, the more I come to the conclusion that it’s a hack job and that if it wasn’t for WP7 I really wouldn’t bother wasting my time trying to support it for anything. The Mono crew have unified platform support figured out, how is it that Microsoft with all its resources (or perhaps that’s exactly the reason?) has managed to create such different platforms. I very quickly ran out of votes on Silverlight’s UserVoice page: http://dotnet.uservoice.com/forums/4325-silverlight-feature-suggestions

The complete final code for the device provider is available here: https://gist.github.com/1047203

P.S. If I got this wrong, I’d love to be corrected.

Code Monkey vs. Elderly Preconditioning

For anyone that’s given tech support to their parents, grandparents, or any older customers, one thing stands out: They don’t read what’s in front of them. Although the same could be said for most average computer users, it is my belief that is far more prevalent amongst the older generations. When these older people were growing up, computers weren’t expected household items. More importantly, they were easy to break software-wise.

As a result of this the older users, who’ve never taken the time to learn about their computer, have been preconditioned to never “try it and see”. When TIAS is likely to break possibly everything, it becomes easy to avoid doing anything you’re unsure of. This is especially true for those that don’t have tech support to go to (for those that do, that’d be you). As a result, it’s far less likely that these people will ever try to figure out how to do some things on their own, due to a fear of breaking things.

While it may sound ridiculous to think that uninstalling some simple program may break the whole computer, they have no clue how a computer works. Even as operating systems become more and more reliable, most will remain stuck in this preconditioned fear. Even on platforms like iOS where it’s extremely difficult to break by normal use, this behavior continues.

What can we do to stop this? Much to the dismay to this audience (or maybe not, depending on how much you get asked for support), I believe that platforms like iOS that are heavily locked down are the future of mainstream computing and gaming. We’re already seeing it with the rise of console gaming, and the old (and young) adopting the iPad. That’s not to say full fledged PCs are going anywhere anytime soon, but more and more they’ll be regulated to the office and the power user.

These locked down systems are decidedly easier to use. No, they’re not anywhere near as capable as a normal personal computer today, but that won’t stay the case for long. On top of that fact, the ones having issues with computers are generally those that only need very little from it. Advanced content creation will remain with more capable systems, but mass content consumption and simple content creation is the bulk of the average user’s use for computers and can easily be regulated to less capable systems. These generally less capable systems are even, in some cases, better suited to these tasks to begin with.

In the short term, for those annoyed by support requests, you can refuse to help. Demand they read before they ask, even make them recite what’s on the screen. Do not accept “well I don’t know” or “If I knew, I wouldn’t have to ask you” or even “You can figure it out a lot faster than me” as excuses. It may sound mean, but there’s few other ways to teach them that don’t require a significant portion of your time.

You can help them by teaching them how to find things. When we look for functionality in a program, we know to check context menus and program menus. These things are key to finding things, but right clicking things may not be all that apparent to those who don’t use the computer anywhere near as much as us.

For once, I don’t blame the application developers. Although some fail miserably at creating good user experiences, many are adequate. Despite developers’ best efforts, people don’t read and sadly not every application can be created without needing description or instruction.

Code Monkey vs. iOS4

So having ranted about the hardware, it’s only fitting that I rant about the software on it. Like my article on the hardware, I’m only going to focus on what I think is wrong with it; there’s plenty of articles on the good stuff already.

Multitasking

UI

Apple’s implementation of multitasking, UI-wise, is nearly the worst I could think of. Not only did they fail completely at avoiding a task manager, they ended up implementing one that confuses users even more than a proper one would. On top of this, they broke the cardinal rule: they broke a users understanding of simply closing an app.

Previous to iOS4, pressing the home button was synonymous with closing an app. There was no fear of it continuing to drain the battery, continuing to monitor your location or what have you. This changed slightly with the introduction of push notifications, but the effect was the same, the app was dead and gone. iOS4 has broken this completely, worse than simply changing an expected behavior, they’ve made the old behavior annoyingly difficult to get to. Let’s compare, shall we?

Really closing an app in iOS3-:

  1. Press the home button.

Really closing an app in iOS4:

  1. Press the home button.
  2. Double press the home button.
  3. Tap and hold on an icon.
  4. Press the red minus above the app.

I mean, really? What’s worse is this isn’t even remotely obvious; I’d be willing to bet that most users of iOS4 don’t even know about this. You may be wondering why this is even a problem since most apps don’t multitask anyway and state saving is a useful feature.

When introducing what is effectively just a MRU list, Steve Jobs said “These are all the apps that are running.” Problem is, that’s not even remotely true. It’s just a list if recently used apps, with absolutely no indication of wether they are actually still running or just in a suspended state (if that, since non-iOS4 apps are listed too). Beyond the fact that not actually closing the apps pollutes this terrible ‘multitasking UI’, I’m not even given a choice up front about whether an app should keep running in the background or not.

On the flip side of this, you could say that things like Skype and Pandora are now consistent to their core-feature counterparts. Except the fact that both of these apps use significant bandwidth and in the case of Pandora, more power. You could also say that 9.9 times out of 10, people will not want to actually close Skype while on a phone call, which I’d agree with. In the case of Pandora though, you have to weigh user expectations with consistency. Specifically for Pandora, consistency probably wins out because it’s more convenient than the alternative. However, I feel that core-feature alternatives are the exception and that we can’t even begin to apply this argument to all types of apps.

UI Ideas

The last time I was blogging, I posted an article proposing my solution for the UI of multitasking to avoid a task manager among other things. In it, I proposed that true multitasking be treated like GPS location, that you be asked by the OS whether or not to allow this app to keep draining battery. Having seen Apple’s solution now, I still think it’s a good idea and have some new ideas.

Part of the problem now is that the default behavior is completely up to the app developer who may or may not give you a choice. Sometimes when I exit Colloquy, I actually want to exit and sign out of Colloquy. Sure, Colloquy could likely add a dialog asking me in their task completion thread on whether to keep running or not (which I’d like, actually), but that doesn’t solve it for say Pandora or anything else.

Instead of having explicit closing, what about explicit multitasking? If I tap the home button, it would exit the app as it always has. If I double tap the home button, up would pop the multitasking UI. From there, I could either go to the home screen, or to another running app. Either way, exited through this multitasking UI, the running app would always be allowed to multitask. Options could be setup to allow this from the ‘normal’ app exit, but it would be opt-in.

As for saved state, I’m on the fence. I’d still like to keep a way to explicitly clear it, as is sometimes necessary when apps get confused (I’m looking at you, Trillian.) If it wasn’t for this, I’d want them not in the multitasking list. Sadly the software world is imperfect and I suspect there will always be an app I have to beat with a stick. Non-iOS4 apps should never have shown in the list to begin with, but it won’t really matter for much longer.

Actual possibilities

Many have criticized Apple for not allowing ‘true multitasking’ and instead watering it down to what they saw as important. However, I’m not going to do that, because I actually like the approach Apple has taken. Not only that, but the work to integrate things like Pandora and Skype to look and act like the similar core features of the phone impressed me.

Where Apple fell short though is their belief that Push Notifications was sufficient for all messaging clients. Problem is, not all of us tech informed people have a box running 24/7 they can use as a IRC proxy. Sure, I could probably set one up on my Linode VPS, but I shouldn’t have to. Persistent connections are an important but annoyingly missing piece. Everyone likes to use IRC as the example of this, but let’s use an example more relevant to the every day crowd.

Let’s take some multiplayer game you’re playing with your friend when you get a phone call. Now, a newly compiled single player game would happily have it’s state saved and then restored upon hanging up. Since it’s a multiplayer game however, one of two things just happened: 1. Because the developer took a ton of extra time, the game saved its state on its own and is now trying to reconnect. Or the more likely: 2. You’ve completely lost the connection and have to start the game over. Allowing the game to keep the connection would let it simply pause and then when the call is over it could simply un-pause and continue on it’s merry way with significantly less effort from the developer.

Contacts

I guess at this point it shouldn’t come as a shock to me that Apple seems to have no problem regressing features. Originally I’d planned to complain about the fact that I thought they’d removed custom labels from phone numbers and email addresses. When presented with the new interface that didn’t force me to select one and a preexisting label to the left, I assumed there was no way to change it. As it turns out however, it’s been moved to a menu that appears when you tap that label in edit mode.

Now, if I’d never used iOS 4 before, I’d probably have tried tapping on the label to change it. It seems like a pretty obvious spot to get to that menu on its own. However having been using iOS since 2.0, I was pretty used to it the way it was. When I saw the way I’d been used to was gone, I assumed the feature was gone and I bet I’m not alone.

On its own, the new design is simpler and has significantly less friction. However, as with multitasking, they’ve gone ahead and totally screwed with expected behavior.  I mean, was anyone really upset that adding a phone number or email address took a few seconds longer than it does now?

Email

Threading

Apple, if you’re going to steal/copy/whatever a feature, at least do it right. My biggest hope for email was that I’d get threaded messages from Gmail, where I love them to death. Upon setting up my email account on the new phone, I eagerly went into a 52 email thread and was promptly disappointed. Where are my own messages? Forum threads show my responses, Gmail shows my responses, but Mail does not. “Thread different”, perhaps?

Without seeing my own responses inline, the threading feature is all but useless. The point is to be able to see the entire conversation between parties, and that includes being able to see what you said. Being able to see them together should of helped avoid going into an email and backing out to try to find the previous one, but that hasn’t been remedied at all. Surely that must be an option I can enable, at least, right? Nope.

Worse still is the interface, the back and forth navigation is still just as annoying as it was before. Yes, sure, it’s an improvement because the messages are all together but the beauty of Gmail’s interface is that everything is right there. Even given the small size of the screen, I think that Gmail’s interface is totally doable. Simply pull the email down to reveal the subjects of the other messages, tap to expand. “But wait, that’s as just as many actions as before!” you say. Yes, but it skips the navigation animations that become time consuming quickly when compounded over several emails.

Gmail Archive

I guess at this point I really shouldn’t be surprised, but let us continue on this face-meets-palm quest pointing out where Apple broke user expectations. Previously setting up a Gmail account, delete would (insert surprise joke here) actually trash the email! In iOS4, Apple has added the ‘archive’ feature for Gmail accounts, which is great. What is not great is that it’s now turned on by default. So let me get this straight, Apple introduces a feature (threading) that just affects presentation and has it off by default. When Apple introduces a feature that radically affects where your email goes, however, it’s now on by default even for existing accounts?

Now, granted, at least it’s not the reverse (going from archive to a delete) so at least it’s not more destructive. However, yet once again, Apple has taken existing behavior and decided that you’d rather have this other new behavior. I’m not sure who archives their Gmail messages instead of moving them to trash, but I certainly don’t. Maybe we could have both? Swipe right to delete, left to archive. We’ve already associated swipe right with delete pretty much everywhere, let’s take new completely different behavior and put it somewhere else instead of replacing things.

Annoying Tidbits

  • Why is there a delay in opening a folder?
  • Still no bulk delete in MMS.
  • Safari, maybe I don’t want to go to something in my bookmarks if I don’t have an open page.

Conclusion

Generally speaking, I’m quite happy with iOS4 and do not wish to go back to iOS3. As I said before though, this has just been about the bad.

Apple seems to have no regard for previous user expectations, which I find very disturbing. I don’t have the total sales numbers for iPhones previous to iPhone 4, but this helps put it in perspective. The thing is, when you have something that works and works well, there’s likely not a good enough justification for changing it. It’s only when expectations of users is to be frustrated, or for the feature to not even work, that it’s OK to go ahead and gut the feature.

Code Monkey vs. Antennagate Press Conference

I am honestly appalled at Apple, not that I wasn’t already. So much of their “hard data” is easily defeated by a simple explanation that it makes me wonder if people will be gullible enough to become apologists regurgitating their “hard data” as an excuse. It’s not even an explanation, it’s nothing but downplay with some numbers that sound impressive on their face. I was dissapointed (but not surprised) to hear nothing about the glass being more easily scratchable. I’m more worried about the glass, since there’s no workaround once it scratches, than the reception.

So first off, playing that YouTube video is not cute, it’s arrogant and offensive. While logically correct from a customer’s stand point, “don’t buy one or take it back” is hardly a “we love our customers” approach and certainly not something I want to hear from the product’s company. Customers have voiced a legitimate concern, and Apple has simply shrugged.

“Hard Data”

In the press conference, Jobs liked to keep coming back to sarcastically stating that because of the sheer amount of coverage this issue has received, it must be on a grande scale. After each reiteration, he mentions some very low number as the counter. Here’s the thing about Web 2.0, Steve: All the coverage is people complaining. Post after post, comment after comment, video after video, people are complaining in large numbers. Simply because they do not go through methods you can track, doesn’t mean the outcry isn’t there.

The percentage of iPhone 4 users who have called into AppleCare about the antenna or reception is reported to be 0.55%. For once, I’m glad Job’s took the time to share the current sales numbers with us: 3M iPhone 4s sold. 0.55% of 3M is 16,500 and that’s just the people who’ve bothered to contact support. I’m not sure what land Apple lives in, but 16,500 is a lot of people, especially given that out of the users experiencing a problem, only a fraction of those experiencing it will ever bother to complain about it. Many others, like myself, knew of the issue and were simply awaiting Apple’s public response.

Low return rates is easily explained by the fact that many have been waiting to see what Apple would do. Seeing if they’d eventually give away cases, have some form of recall, or if some software fix would correct the problem. On top of this, you have the 3G users like me who’ve been waiting for a newer faster device and are willing to put up with this to get the rest. “Put up with” is not something you really want your customers associating with your product. Where’s the “hard data” on customer annoyance?

Call drop rates are again easily explained and are just as troubling. Many reviews point to the iPhone 4 not dropping calls in areas their 3G or 3GS previously did. So, if it’s actually not dropping the same calls as before, and the delta is only between 0-1, wouldn’t that imply that dropping calls from this new issue is more than a <1 delta? On top of this, people know about the issue and have begun (well, at least I have) holding their phone in less comfortable manners to ensure that we don’t drop the call. Where’s the “hard data” on hand comfort throughout calls? Admittedly, the lack of case theory may affect the numbers, but all this does is imply to me that a case is required.. which I have never wanted and shouldn’t be forced into.

“Perspective”

“.. but this does put it in perspective.” In your reality distortion field, maybe. The numbers cited sound great, until you actually stop and think about them. Job’s attitude throughout the presentation was dismissive and arrogant, which does put things into perspective.

All this being said, as far as the antenna issue itself goes, the free case solution is a good one. $29.99 was absurd for what the Bumper really was. Kudos to Apple for even going further and including other cases to ensure quantity, although I should probably wait until we see what the other selections are. The refund date, however, should have been extended for those waiting to see what Apple was going to do.

Yes, Apple made it extremely visible both in the bars issue and in where the spot to kill the signal is. However, what I would like to see if there’s any one spot on these other phones that you can kill the signal with a single finger. Somehow, I doubt it, but I certainly can on the iPhone 4 and that is why it’s still a bigger issue than with the other phones.

Code Monkey vs. iPhone 4

I have one and I hate it just as much as I love it. I find that I can actually do what I want in a reasonable amount of time and minimal frustration due to the sheer speed and general responsiveness of the device. Yet I find myself generally less happy with it than I was with my iPhone 3G when I got it. To have come this far, it’s sad to see how far Apple has fallen with this device.

Now, there’s been plenty of coverage on the various issues, many of which I feel completely miss the mark (that’s what we get for enabling everyone to have global soap boxes I guess). Some get close, but over complicate the issues with speculation that bring about more debate than consensus. I only plan to focus on what’s wrong, hardware wise; I’ll write about iOS4 separately.

Comfort

Awkward ways to hold the phone to get a signal aside, this is by far the most uncomfortable iPhone yet. Sure, it looks beautiful, but as a smart phone comfort should be on the top of the list. The iPhone has reached a point where thinner is not better, it doesn’t help consumers at this point and it only makes manufacturing and design that much harder. No one was really crying for a thinner iPhone, because the larger curved back was already comfortable. This new thinner design only makes it harder and more painful to hold on to, having to bring your fingers farther in, bending them even more. Honestly it reminds me greatly of the original NES controllers, turning your hands into painful fixed claws.

To add another to the ‘curved was better’ category, the new sides are just downright painful. Especially when it comes to certain games, the sides actually become painful because the steel band is not the same thickness as the phone. This leaves a nice little crevice on either side of the band that your finger gets pushed into and it doesn’t take very long before this starts to hurt. I suppose I should just ‘avoid holding it that way’, but if we keep adding ways to hold it to the ‘avoid’ list, I’m not going to be able to hold this thing at all.

There’s no doubt that the iPhone 4 looks and feels very solid and precise. The iPhone 3G / 3GS look like toys with their plastic parts in comparison. However, even with their slightly weaker feeling due to the plastic, there’s a comfort and solidity from the full curved back that fits more naturally into my hand. Being larger and filling your hand gives you a greater sense of having a good solid grip on the phone. If I could take the 4′s guts and stick them in a 3G case, I’d do it without hesitation.

Glass

I’ve had my iPhone 3G since the day after launch (2 years), it’s never worn a case and I just toss it in my pocket with my wallet. The plastic back is scratched up like crazy, but I’ve never really cared about that. The glass, however, only has a few minor scratches on the top and bottom, none through the screen. My iPhone 4 has more scratches all over its front glass in the two weeks than I’ve had it (treated exactly the same) than my 3G does. Think about that for a second, more scratches in two weeks than in two years. For what gain, exactly?

My guess is that it has something to do with the fact that they’re laminating the screen and glass together. There’s no doubt even just the lack of a gap between the glass and screen looks significantly better, but at what cost? Is it going to matter if there’s a gap or not when the screen is so covered in scratches that I can’t hardly use the device? Only time will tell, but my spidey sense tells me the answer is ‘no’.

Now, to be fair, the scratches are basically invisible while the screen is on but I wonder how long that will last as they accumulate. I think there’s a definite case for false advertising here; Apple’s own web page says “… the glass is ultradurable and more scratch resistant than ever”. More scratch resistant?! I’d have been perfectly happy if it was as scratch resistant as my 3G, but as it’s been demonstrated all over the web (and in my personal experience), it’s actually significantly worse.

Antenna

I have a hard time expressing just how ridiculous and unfathomable the whole ordeal has been. Speculation abounds, but I lack such guesses because I am completely dumbfounded. Suffice to say, Apple has lost my respect over this issue alone, let alone everything else that’s wrong with this device. I, personally, experience the death grip and it’s no exaggeration and it’s not simply that the bars are wrong. Web page loading can be paused and continued at will repeatedly just by touching and releasing a specific spot on the phone.

There’s been a lot of commenting about this being an issue for left handed people. Here’s the thing, this issue affects data just as much as phone calls. This means that when holding it in my left hand to touch and interact with using my right hand, that wonderful spot of death is firmly cupped in my hand. It’s not specific to just holding it in a way that you’d make a phone call. Please stop saying Apple hates lefties, it’s obvious Apple hates us all equally.

The absurdity of all this is mind boggling, despite the fact that some signal attenuation may happen with any cell phone, including previous iPhones. The simplest solution, even with the current overall design, would be to move the ‘death point’ to the right side. You’re moving it from what’s always going to hit the palm, greatly increasing the chances that it’ll be bridged, to the side where only your fingers are present which would be far less likely to be bridged. During a right handed phone call (at least in my hands), I’m not cupping that gap, my thumb rises over and latches the right side of the phone.

Although Apple claims that there are no real issues beyond what affects a normal cell phone and says that a lot of the confusion stems from a bug in the software, I’m calling bullshit. For me, it doesn’t just degrade the signal, it removes it completely. Data transfers stop dead in their tracks, even simple webpages won’t continue loading. I wish I had a 3GS or something to record it, but I’ve paused web page loads multiple times in the same load just by pressing the ‘death point’. To be fair, I have seen some cases where I had to use more than finger to interrupt it, giving credence to the idea that it is actually just extreme attenuation.

However, other cell phones don’t put the antenna in the best possible place to be blocked/shorted/whatever is going on. They don’t take the most comfortable way to hold the phone and tell you not to just “avoid holding it that way” or to buy a case (for which they sell an insanely overpriced piece of plastic). Besides the cases meant to clip to a belt, who actually bought a case for their cell phone pre-smart phone? Nearly no one, so why is this different?

The response has varied wildly, anywhere from “calm down, buy a case and enjoy your iPhone” all the way to lawsuits. The expectation of us to buy a case just to be able to hold our phone in a comfortable manner and still have it work is just absurd, given what we’re already paying for it. What’s worse has been Apple’s-and Steve Jobs’, if email postings are to be believed-response to the whole ordeal. If this was such a nonissue that was present in old phones, there wouldn’t be a such a sudden and huge outcry over it, so downplaying and dismissing the issue is just an insult to Apple’s customers.

Apple should at least acknowledge that the design of the iPhone 4 does, in fact, make it easier to significantly drop a signal just by holding it. They really should be handing out free bumpers to appease customers, as they’re certainly not worth $29, and even more so would be worth the hit to help curb the large amount of negative press they’re getting.

The Bad

In a time where Android is starting to come under fire, this should of been the perfect time to surge ahead in positive mindshare. Instead, they’ve made several wrong turns with a phone that iPhone 3G customers have been waiting for every since they saw how fast the 3GS was. Now these customers are stuck, sure they could return their phones, but after seeing the beautiful display and just how fast it is, it’s unlikely they’ll do anything of the sort. On top of this, they’ve dismissed and insulted customers without so much as an olive branch.

I’ve always had a hate-love relationship with Apple, but this whole ordeal has left me bitter and hateful of Apple. The iPhone 4 now has, at least among the tech-informed (you know, the guys who recommend devices to the non-tech informed), a negative mindshare. It’s full of problems, and the Android users are sitting back and laughing with their relatively minor issues in comparison.

I will likely end up with a case to protect the antenna and glass, and hopefully round out the back some for more comfort. The fact that I now actually feel that I have to buy a case is, in my opinion, a complete failure on Apple’s part. It’s fragile and uncomfortable, a terrible combination if you want to keep something intact and not piss off your customers.