Working with NFC–Take 2
Some time ago (I’ve blogged about working with proximity devices – NFC – in Windows 8 Consumer preview: http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2012/02/29/windows-8-consumer-preview-and-visual-studio-11-beta-working-with-proximity-device-part-2-11.aspx). Then I’ve talked about a simple way performing NFC communication – using pub/sub communication pattern which is good for exchanging simple messages using PublishMessage and PublishBinaryMessage functions on ProximityDevice class.
Few days ago my client asked me to prepare a demo which uses proximity communication to transfer custom “Items” between two instances of the app (and also Windows Phone 8 version of this app). The app uses complex “items” which includes many textual fields, undefined number of attached images, locations, etc. To achieve this I’ve decided to use 2nd approach to proximity communication briefly described in my previous blog post – the TriggeredConnection and Sockets.
(I had additional “constraints” while engineered the solution: to minimize amount of changes in the app by using existing in-the-app load/save mechanisms and to be compatible as much as possible with Windows Phone 8 app and provide maximum code reuse between the platforms to avoid developing same functionality twice. Please keep in mind, that this blog post is not focused directly on dealing those constraints; I will blog more about sharing the code and my traditional “What’s New” series about Windows Phone 8 in a short while – so stay tuned).
Let’s get started. First of all – the app must specify proximity capability in app manifest:

The app has to support two sides of proximity communication – sender and receiver. Both called peers. In both cases, I am using PeerFinder class (form Windows.Networking.Proximity namespace). In my case I’ve decided to use Triggered discovery type which supports Tap-to-Connect scenario I needed for my purposes.
1: public void StartConnect(string displayName)
2: {
3: if (!String.IsNullOrEmpty(displayName))
4: {
5: //Optionally name the peer so it could be easily identified
6: //and optionally presented to user
7: PeerFinder.DisplayName = displayName;
8: }
9:
10: if ((PeerFinder.SupportedDiscoveryTypes & PeerDiscoveryTypes.Triggered) != PeerDiscoveryTypes.Triggered)
11: {
12: //The device doesn't support tap-and-send communication
13: UpdateConnectionStatus(ConnectionStatus.NotSupported);
14: return;
15: }
16:
17: //Subscribe to connection triggers
18: PeerFinder.TriggeredConnectionStateChanged += TriggeredConnectionStateChanged;
19:
20: //Declare alternate identities (if needed and supported)
21: if (!PeerFinder.AlternateIdentities.ContainsKey(AlternateIdentityKey))
22: {
23: PeerFinder.AlternateIdentities.Add(AlternateIdentityKey, AlternateIdentity);
24: }
25:
26: PeerFinder.AllowInfrastructure = true;
27: PeerFinder.AllowBluetooth = true;
28: PeerFinder.AllowWiFiDirect = false;
29:
30: //Start listening to proximity events
31: PeerFinder.Start();
32:
33: UpdateConnectionStatus(ConnectionStatus.Ready);
34: }
The code snippet above starts the connectivity. It uses enables to optionally name the peer so it could be identified by other side, checks that device support required proximity discovery mode and subscribes to connection state changes.
The code above adds AlternateIdentities (see class declaration below). Alternate identities are used to enable communication between different applications and devices. As you remember, my demo requirement was to share the “items” between two instances of the app (which has same identity on all running devices) and Windows Phone 8 version of this app. Enabling the sharing between the app running Windows 8 and the app running on Windows Phone or different app running on Windows 8 requires adding alternate identities. The actual definition of the AlternateIdentityKey in my demo looks as follows:
1: #if NETFX_CORE
2: readonly string AlternateIdentityKey = "WindowsPhone";
3: readonly string AlternateIdentity = "{04f1e621-039f-4829-b59f-47e71c6af98d}";
4: #elif WINDOWS_PHONE
5: readonly string AlternateIdentityKey = "Windows";
6: readonly string AlternateIdentity = "a4ec8cdf-7759-4709-93af-5d2c2688ee46_cb1hhkscw5m06!App"; //Could be found using Windows.ApplicationModel.Package.Current.Id.FamilyName
7: #endif
This code is a part of shared code which defines AlternateIdentity for other platform and could also define the AlternateIdentity for other app running on the same platform. When compiled for Windows 8, it adds “WindowsPhone” identity key and the ID of application running on the phone; when compiled for Windows Phone 8 it adds the “Windows” identity key and the full app ID of the Windows 8 app.
Note: The app id could be obtained from app manifest (Packaging tab).
For Windows it will be the value of “Package family name” field + “!” + app ID (the namesapce and name of the App class). It also could be obtained using Windows.ApplicationModel.Package.Current.Id.FamilyName API from within the code.
For Windows Phone app it will be the value of “Product ID”.
This way PeerFinder is aware about other apps which are allowed to connect to this app. Additionally, when two devices are in proximity and the trying to establish the proximity connection, but one of the devices is not running the application (not started or in background) this alternate ID will be using by platform to identify the “target” app and pop up a toast notification asking user’s permission to start the app and establish proximity connection.
When the connection is triggered, every app responds in slightly different way (depending of the app logic). In my case the TriggeredConnectionStateChanged event handler looks as follows:
1: private void TriggeredConnectionStateChanged(object sender, TriggeredConnectionStateChangedEventArgs e)
2: {
3: switch (e.State)
4: {
5: case TriggeredConnectState.PeerFound:
6: UpdateConnectionStatus(ConnectionStatus.PeerFound);
7: break;
8: case TriggeredConnectState.Connecting:
9: UpdateConnectionStatus(ConnectionStatus.Connecting);
10: break;
11: case TriggeredConnectState.Listening:
12: UpdateConnectionStatus(ConnectionStatus.Listening);
13: break;
14: case TriggeredConnectState.Canceled:
15: UpdateConnectionStatus(ConnectionStatus.Canceled);
16: break;
17: case TriggeredConnectState.Failed:
18: UpdateConnectionStatus(ConnectionStatus.Failed);
19: break;
20: case TriggeredConnectState.Completed:
21: socket = e.Socket;
22: UpdateConnectionStatus(ConnectionStatus.Completed);
23: break;
24: }
25: }
For most cases my demo just informs the user with different connectivity state changes and when connection established (Completed state) saves a Socket for future use. The main app also listens to connectivity changes and when connection is established switches to either “receive” mode or “send” mode depending on the app logic. In both cases (will show then later in the post) a socket communication will be used to send/receive raw bytes and the actual information should be first converted to bytes representation (in case of send side) and parsed back (in case of receiving side).
To leverage the code we already had in the app I’ve decided to use objects serialized with JSON serializer. This also enabled me to write pretty “abstract” code which sending/receiving the items (you will see later). The next code block has two functions – GetItemStream which serializes my “item” to JSON format and ParseNewItem which deserializes received “item” from stream back to strongly typed object:
1: public byte[] GetItemStream(Item note)
2: {
3: List<Type> types = new List<Type>();
4: //Not required in this case, but should be used to
5: //serializing/deserializing multiple object types
6: types.Add(typeof(Item));
7:
8: DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Item)/*, types*/);
9: MemoryStream ms = new MemoryStream();
10:
11: ser.WriteObject(ms, note);
12: byte[] json = ms.ToArray();
13: var value = System.Text.Encoding.UTF8.GetString(json, 0, json.Length);
14: Debug.WriteLine("Serialized JSON object:\n" + value);
15: return json;
16: }
17:
18: private Item ParseNewItem(MemoryStream ms)
19: {
20: List<Type> types = new List<Type>();
21: //Not required in this case, but should be used to
22: //serializing/deserializing multiple object types
23: types.Add(typeof(Item));
24: DataContractJsonSerializer des = new DataContractJsonSerializer(typeof(Item)/*, types*/);
25: var item = (Item)des.ReadObject(ms);
26: item.UniqueId = DataManager.GetUniqueId();
27:
28: return item;
29: }
Once the object has been serialized and we got a stream it is ready to be sent.
1: public async Task SendItemAsync([ReadOnlyArray] byte[] itemBytes, int imagesCount, [ReadOnlyArray] List<byte[]> images)
2: {
3: if (itemBytes != null && socket != null)
4: {
5: //Start sending the item
6: try
7: {
8: DataWriter dw = new DataWriter(socket.OutputStream);
9:
10: //Sending item length
11: dw.WriteInt32(itemBytes.Length);
12: await dw.StoreAsync();
13:
14: //Sending item bytes
15: dw.WriteBytes(itemBytes);
16: await dw.StoreAsync();
17:
18: if (null != images && imagesCount > 0) //The only app-specific part here...
19: {
20: foreach (var imageBytes in images)
21: {
22: //Sending image length
23: dw.WriteInt32(imageBytes.Length);
24: await dw.StoreAsync();
25:
26: //Sending image bytes
27: dw.WriteBytes(imageBytes);
28: await dw.StoreAsync();
29: }
30: }
31:
32: //Flushing buffers to make sure we pushed all the data over "the wire"
33: await dw.FlushAsync();
34:
35: //Closing DataWriter
36: dw.DetachStream();
37: dw.Dispose();
38: }
39: catch (Exception ex)
40: {
41: Debug.WriteLine(ex.Message);
42: //Reset the connection if case of communication error
43: StopConnect();
44: #if NETFX_CORE
45: string PeerName = "Win8_NFC Sample";
46: #elif WINDOWS_PHONE
47: string PeerName = "WP8_NFC Sample";
48: #endif
49: //Restart connection
50: StartConnect(PeerName);
51: }
52: }
53: }
The function is relatively simple and straight forward – user DataWriter class (System.Networking.Sockets namespace) to send bytes over socketed connection. Same goes to images which were prepared in their raw/bytes format. The code snippet which prepares and calls the SendItemAsync looks as follows (and is not shared across platforms):
1: //Get bytes from current item
2: byte[] bytes = ProximityManager.Instance.GetItemStream(item);
3: List<byte[]> images = null;
4:
5: //If item has images attached - prepare images
6: if (item.Images.Count > 0)
7: {
8: images = new List<byte[]>();
9:
10: foreach (var imgSrc in item.Images)
11: {
12: //Read image from local storage
13: var filename = imgSrc.Replace("Images/", "");
14: var storage = Windows.Storage.ApplicationData.Current.LocalFolder;
15: var folder = await storage.GetFolderAsync("Images");
16: var file = await folder.GetFileAsync(filename);
17: var stream = await file.OpenReadAsync();
18: //Prepare image bytes
19: byte[] imageBytes = new byte[stream.Size];
20: var str = stream.AsStream();
21: await str.ReadAsync(imageBytes, 0, (int)stream.Size);
22:
23: images.Add(imageBytes);
24: }
25: }
26:
27: //Send the item
28: await ProximityManager.Instance.SendItemAsync(bytes, item.Images.Count, images);
The second part of the equation is to receive a message. Since we know exactly how we sending the message, the receive function looks as follows:
1: public async void ReceiveItemAsync()
2: {
3: if (socket != null)
4: {
5: try
6: {
7: DataReader dr = new DataReader(socket.InputStream);
8:
9: // Receive the size of the byte array first
10: if (await TryLoadAsync(dr, 4))
11: {
12: var count = dr.ReadInt32();
13: uint ucount = (uint)count;
14:
15: if (await TryLoadAsync(dr, ucount))
16: {
17: byte[] bytesIn = new byte[count];
18: dr.ReadBytes(bytesIn);
19:
20: //Save/parse new item
21: MemoryStream ms = new MemoryStream(bytesIn);
22: Item theItem = ParseNewItem(ms);
23:
24: var imagesCount = theItem.Images.Count;
25: //Receive "attached" images - app-specific functionality
26: if (imagesCount > 0)
27: {
28: for (int i = 0; i < imagesCount; i++)
29: {
30: if (await TryLoadAsync(dr, 4))
31: {
32: var imgSize = dr.ReadInt32();
33: uint uimgSize = (uint)imgSize;
34:
35: if (await TryLoadAsync(dr, uimgSize))
36: {
37: byte[] imgBytesIn = new byte[imgSize];
38: dr.ReadBytes(imgBytesIn);
39:
40: MemoryStream imgMs = new MemoryStream(imgBytesIn);
41:
42: var imgName = theItem.Images[i];
43: imgName = imgName.Replace("Images/", "");
44: var fileName = await DataManager.SaveImageAsync(imgName, imgMs.AsInputStream(), Windows.Storage.CreationCollisionOption.GenerateUniqueName);
45: }
46: }
47: }
48: }
49:
50: await DataManager.AddFileAsync(theItem);
51:
52: //Raise evens, so main app "knows" about new item
53: #if NETFX_CORE
54: await ((NFCSample.App)App.Current).Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
55: #elif WINDOWS_PHONE
56: ((WP8_NFC.App)System.Windows.Application.Current).Dispatcher.BeginInvoke((Action)(() =>
57: #endif
58: {
59: //Raise ItemReceived event
60: if (ItemReceived != null)
61: {
62: ItemReceivedEventArgs args = new ItemReceivedEventArgs();
63: args.NewItem = theItem;
64: ItemReceived(this, args);
65: }
66: }
67: #if WINDOWS_PHONE
68: )
69: #endif
70: );
71: }
72: }
73:
74: //Detach a stream and release it
75: dr.DetachStream();
76: dr.Dispose();
77: }
78: catch (Exception ex)
79: {
80: Debug.WriteLine(ex.Message);
81: //Reset the connection in case of communication error
82: StopConnect();
83: #if NETFX_CORE
84: string PeerName = "Win8_NFC Sample";
85: #elif WINDOWS_PHONE
86: string PeerName = "WP8_NFC Sample";
87: #endif
88: // Restart connection
89: StartConnect(PeerName);
90: }
91: }
92: }
In this code I am receiving the message parts in exactly the same order as sending them.
Note, that this code is share between Windows 8 and Windows Phone 8.
Last piece of puzzle here – the helper function which reads known amount of bytes from socket:
1: private async Task<bool> TryLoadAsync(DataReader dr, uint count)
2: {
3: //Get cancellation token
4: cts = new CancellationTokenSource();
5: CancellationToken token = cts.Token;
6:
7: bool result = false;
8:
9: try
10: {
11: var countReceived = await dr.LoadAsync(count).AsTask(token);
12:
13: if (countReceived != count)
14: {
15: //Connection error - close it
16: Debug.WriteLine("Bytes read {0} != {1} total bytes. Communucation error!", countReceived, count);
17: //Reset connection
18: StopConnect();
19: #if NETFX_CORE
20: string PeerName = "Win8_NFC Sample";
21: #elif WINDOWS_PHONE
22: string PeerName = "WP8_NFC Sample";
23: #endif
24: // Restart connection
25: StartConnect(PeerName);
26: result = false;
27: }
28: else
29: result = true;
30: }
31: catch (TaskCanceledException)
32: {
33: result = false;
34: }
35: finally
36: {
37: cts = null;
38: }
39:
40: return result;
41: }
This is it. Now my class is shared between two apps on multiple platforms and my samples can exchange complex custom items between instances on same and different platforms.
To see this demo working – come to BUILD 2.0 conference taking place at Redmond, WA (October 30 - November 2, 2012) or the video I will try to film and publish after the BUILD.
Also – if you are at the BUILD – feel free to find me and say “Hi”. I will be there!
Teaser:
I have a massive ‘What’s new in Windows Phone 8” series ready to be published after official release!
Stay tuned to more posts to come.
Alex