UISplitViewController and UICollectionView

Continuing from the previous post that used a UICollectionView to display a grid of images from Bing, the following example adds a UICollectionView to a UISplitViewController.

When the row is selected, a new search is issued and the resulting images are updated in the UICollectionView. To connect the selection in the UISplitViewController’s master controller, which is an MT.D DialogViewController in this case, to the UICollectionViewController contained in the UISplitViewController’s detail controller, an event is raised when a row is selected:

public AnimalsController () : base (null)
{
    Root = new RootElement ("Animals") {
        new Section () {
            from animal in animals
            select (Element) new StringElement(animal, () => {
            if(AnimalSelected != null)
                AnimalSelected(this, new AnimalSelectedEventArgs{Animal = animal});
            })
         }};
 }

Then the UISplitViewController’s implementation handles this event to communicate to the UICollectionViewController:

animalImageController = new BingImageGridViewController (layout);

animalsController.AnimalSelected += (sender, e) => {
    animalImageController.LoadImages (e.Animal);
};

In the LoadImages method, the UICollectionViewController calls Bing for the selected item and uploads the UICollectionView when the data is returned by calling ReloadData:


public void LoadImages (string search)
{
    UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;

    bing = new Bing ((results) => {
        InvokeOnMainThread (delegate {
            imageUrls = results;
            CollectionView.ReloadData ();
            UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
        });
    });

    bing.ImageSearch (search);
}

The sample is available at https://github.com/mikebluestein/BingImageGrid/tree/master/BingImageGridSplit

Note: you’ll need a Bing API key, which you can get at http://datamarket.azure.com

Using Azure Marketplace’s Bing Image Search on iOS with UICollectionView in C#

The Windows Azure Marketplace now includes the Bing web service. Using the image search feature, combined with a UICollectionView, can be used to display images in a grid on iOS like this:

Consuming the Bing service with Xamarin on iOS can be accomplished just like anywhere else C# is available.  The UI here consists of a UICollectionView, using a UICollectionViewFlowLayout to achieve the grid display. Programming against a UICollectionView is very similar to using a UITableView, the big difference being that UICollectionView works with a layout class to achieve any layout you desire. iOS provides a very flexible UICollectionViewFlowLayout class that can be used  when a line-based layout is needed. Also, you can subclass UICollectionViewLayout directly as well to implement layouts that aren’t line-based.

You can get the code for this example here: https://github.com/mikebluestein/BingImageGrid

To run the exmaple, you need to sign up for an API key from the Windows Azure Marketplace.

For more information on using Collection Views, see this article in Xamarin’s Developer Center:

http://docs.xamarin.com/ios/tutorials/Introduction_to_CollectionViews

Voices That Matter: iOS Conference discount code

Update: 10 people registered with my speaker code, for which I received $500 that I am donating to the Jimmy Fund. Thanks to everyone who attended and to Pearson for hosting the conference.

I’m honored to be speaking at the Voices That Matter: iOS Developers Conference in Boston November 12-13. The conference organizers sent me a speaker’s priority code to pass along, which is good for $150 discount off the price of the core conference. The discount even applies in addition to the early bird pricing. My code is BSTSPK6. Also, I’ll receive $50 for every person that registers with my code, the proceeds of which I will be donating to the Jimmy Fund. I hope to see you at the conference in November.

Thoughts on MonoTouch

As you likely have heard, Attachmate let go of the entire Mono team recently, including all the outstanding people that created and supported MonoTouch. This came as a shock to many, including me, for a variety if reasons:

• There are many existing customers of MonoTouch.
• The community around MonoTouch loves the product.
• There have been some nice success stories recently of apps built with MonoTouch.
• The team that developed MonoTouch and Mono for Android are top-shelf developers.
• Several new books were near release for MonoTouch and Mono for Android.

I got involved with MonoTouch at the beginning of the first beta. I had already been doing some iPhone work with Apple’s tool chain and was (and still am) quite happy with it, but since I have always liked the C# language and .NET, I thought this would be an interesting technology to try out. Initially I was mainly intrigued with the fact that they could even create such a product from outside of Apple. As I learned more about it, I was nothing but impressed with how nicely they blended the C# language with CocoaTouch. All the knowledge I had from the Objective-C side was totally applicable, but I suddenly could bring other technologies to the table, such as being able to take advantage of nice C# language features like Linq, the ability to reuse non-UI code and additional library support via Mono’s implementation of the .NET framework class libraries.

As the technology builds on Apple’s stack, I didn’t have to trade away platform capabilities in any way. At the same time, I found a vibrant community of like-minded developers both from the Mono team as well as outside. I was having such a good time with the technology and the community that I decided to take some time to write about it a little bit on this blog, which eventually led to a book project with Addison-Wesley. I couldn’t have been more excited.

Then, in April of last year, Apple introduced licensing changes that effectively prohibited the use of technologies such as MonoTouch. Although in practice nothing ever got rejected for being built with MonoTouch, the looming threat of this resulted in my book project being placed on hold. I kept doing MonoTouch on my own, because I truly enjoy using it and participating in the community. However, business interests that I was doing MonoTouch development for, quite reasonably, could not proceed with such ambiguity, resulting in my moving some work over to Objective-C.

As you may imagine, I was pretty disappointed by all this, but then late in the summer, Apple changed the licensing again and everything was set back in motion. Excited as ever, I turned my attention back to reworking the parts of the book I had already written for the changes that came out over the time I was in limbo, and proceeded working on the remainder of the book. This work took me up to the end of last year, with the technical review following earlier this year. The book has entered the production process with my publisher and I’m proud of the result and grateful for all the fine people that helped me along the way. My publisher was planning a summer release, which would put the book out around the time of the upcoming Monospace conference. Things were looking great.

Then all these great Mono developers got laid off. First and foremost, I feel awful for any of the great people on the Mono team. They do spectacular work and provide amazing passion with everything they do. Such a team should have a better fate. As it turns out they just may.

Never one to give up so easily, not in the face of licensing issues and not even now with the entire team being dismissed, Miguel and his team are spinning up a new company, Xamarin, to continue what they started. He announced, among other things, that they would be bringing a new iOS product to the market that will be compatible with MonoTouch. This leads me to the current plan for my book.

UPDATE: My publisher has decided to send the book to print. It will be in available in bookstores by late July.

After discussing all the options with my publisher we have decided to go forward with the book in electronic form for now. We will be holding back from doing a print copy until more details emerge regarding Xamarin’s new offering. If we were to go ahead and print the book now it would be, well, printed. By sticking with an eBook we can make the information available to the many existing people using MonoTouch and still potentially offer an updated version for the new product in the future. I’m personally looking forward with great excitement to see what Miguel and his team create next.

Bonjour with MonoTouch

Someone was asking about Bonjour examples in MonoTouch in irc yesterday so I threw this quick and dirty sample together. If you’re not familiar, Bonjour is Apple’s zeroconf networking implementation. It is open-source and even includes a version for Windows. Bonjour allows you to publish and discover services without needing pre-configured information. It’s all about the discovery of services. The actual implementation of the server and client networking code is up to you (I haven’t included that in this example however).

To use Bonjour with MonoTouch, you deal primarily with two classes, NSNetService and NSNetServiceBrowser. NSNetService is used to publish and resolve services. NSNetServiceBrowser, as the name suggests, allows you to browse for services. Once you have discovered a service via a browse, you resolve it in a separate operation, after which you can implement whatever networking code you like to communicate with the service.

In this simple example I publish a service when the application finishes launching and browse from a view controller that includes a table and a text view. The table displays each service as it is discovered. Selecting the row in the table associated with a particular service calls resolve on the service. Adding, removing and resolution of services is logged in the text view.

Here’s the relevant code that does the service publication:

        NetDelegate _netDel;
        NSNetService _ns;

        void InitNetService ()
        {
            _ns = new NSNetService ("", "_testservice._tcp", UIDevice.CurrentDevice.Name, 9999);
            _netDel = new NetDelegate ();
            _ns.Delegate = _netDel;
            _ns.Publish ();
        }

        class NetDelegate : NSNetServiceDelegate
        {
            public override void Published (NSNetService sender)
            {
                Console.WriteLine ("published {0}", sender.Name);
            }

            public override void PublishFailure (NSNetService sender, NSDictionary errors)
            {
                Console.WriteLine ("publish failure {0}," + sender.Name);
            }
        }

The browsing is done by calling the NSNetServiceBrowser’s SearchForServices method. I keep a list of NSNetServices as they are discovered in the FoundService callback and register for the AddressResolved event. Similarly, I remove them from the list via ServiceRemoved. Each service is registered to an AddressResolved handler as it is found. The resolve call, something you would do when you want perform actual networking calls with the service, is made here in the UITableViewSource’s RowSelected implementation. When the callback to ServiceAddressResolved happens, you’ll have an NSNetService complete with information that you can use to perform networking as you see fit.

Here’s the code for the controller with the service browsing and resolution:

    public partial class TestViewController : UIViewController
    {
        List _serviceList;
        NSNetServiceBrowser _netBrowser;
        ServicesTableSource _source;

        // constructors omitted for brevity …

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();
            
            InitNetBrowser ();
        }

        internal void InitNetBrowser ()
        {
            _serviceList = new List ();
            _netBrowser = new NSNetServiceBrowser ();
            
            _source = new ServicesTableSource (this);
            servicesTable.Source = _source;
            
            _netBrowser.SearchForServices ("_testservice._tcp", "");
            
            _netBrowser.FoundService += delegate(object sender, NSNetServiceEventArgs e) {
                
                servicesTextView.Text += "\r\n" + e.Service.Name + " added";
                
                _serviceList.Add (e.Service);                
                e.Service.AddressResolved += ServiceAddressResolved;
                
                //NOTE: could also insert and remove rows in a more fine grained fashion here as well
                servicesTable.ReloadData ();
            };
            
            _netBrowser.ServiceRemoved += delegate(object sender, NSNetServiceEventArgs e) {
                
                servicesTextView.Text += "\r\n" + e.Service.Name + " removed";
                
                var nsService = _serviceList.Single (s => s.Name.Equals (e.Service.Name));
                _serviceList.Remove (nsService);
                servicesTable.ReloadData ();
            };
        }

        void ServiceAddressResolved (object sender, EventArgs e)
        {
            NSNetService ns = sender as NSNetService;
            
            if(ns != null)
                servicesTextView.Text += "\r\n" + ns.Name + " resolved";
            
            // at this point you could use any networking code you like to communicate with the service
        }

        class ServicesTableSource : UITableViewSource
        {
            TestViewController _controller;
            const string SERVICE_CELL_ID = "servicecell";

            public ServicesTableSource (TestViewController controller)
            {
                _controller = controller;
            }

            public override int RowsInSection (UITableView tableview, int section)
            {
                return _controller._serviceList.Count;
            }

            public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
            {
                var serviceCell = tableView.DequeueReusableCell (SERVICE_CELL_ID) ?? new UITableViewCell (UITableViewCellStyle.Value1, SERVICE_CELL_ID);
                
                NSNetService ns = _controller._serviceList[indexPath.Row];
                
                serviceCell.TextLabel.Text = ns.Name;
                
                return serviceCell;
            }
            
            public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
            {
               NSNetService ns = _controller._serviceList[indexPath.Row];
               ns.Resolve(60);
            }
        }
    }

Here’s the app running on the device while another instance is running in the simulator:

You can download the sample project here.

Using GameKit with MonoTouch

GameKit is a framework Apple created that provides peer to peer Bluetooth networking capability. It is meant for transmitting small messages between devices in close proximity. You can use GameKit on relatively newer iDevices including iPhone 3g, iPod Touch 2nd generation and iPad (and newer versions of each as well). Although it’s called GameKit, as the small messages between nearby devices are well suited to peer to peer gaming scenarios, it can be used for any purpose. For larger data transfer, you need to chunk data into many small messages (similar to what you typically do for an http chunking upload, if you’re familiar with that). I’ll show you a simple example here of using GameKit to create a minimalist chat application, the full source code for which is available at http://github.com/mikebluestein/GameKitChat.

GameKit abstracts all the networking details for you. It even provides the UI for device connectivity via the GKPeerPickerController class. You can also use your own UI as well. Additionally, if you want to support networking other than via Bluetooth, you can do so. GameKit doesn’t add any additional networking capability other than Bluetooth but it does give you the ability to hook in your own networking code (they call it “online mode”) and still take advantage of the common interface.

In MonoTouch GameKit lives under the MonoTouch.GameKit namespace. The main class you work with to use GameKit is the GKSession. The GKPeerPickController and the GKSession together make it very easy to request connections and manage data transfer. Internally these wrap Apple’s Bonjour technology, which is their config free networking stack.

In the example below I created a simple app with a UITextView as the chat log and a UITextField to enter new messages. A UIToolBar houses a single button to launch the peer picker. The pop up dialogs shown are all from the stock UI that you get for free with UIPeerPickerController.

The basic steps are:

– Create a GKSession
– Subscribe to events on the GKSession
  – ConnectionRequest to handle accepting a connection request from a peer
  – ReceiveData to handle incoming data from a peer
– Create a GKPeerPickerController
– Set the type of connection you are supporting on the GKPeerPickerController via its ConnectionTypesMask (nearby for Bluetooth)
– Set the GKPeerPickerController’s delegate (this is where I dismisses the picker in the example)

Here’s a code snippet showing the creation of the example’s GKSession and GKPeerPickerController:


GameKitSession = new GKSession ("com.mikebluestein.gamekitchat", 
                                 UIDevice.CurrentDevice.Name, 
                                 GKSessionMode.Peer);

GameKitSession.ReceiveData += (s, e) => { 
  AddMessage (NSString.FromData (e.Data, 
                       NSStringEncoding.UTF8).ToString ()); };

GameKitSession.ConnectionRequest += (s, e) => { 
  e.Session.AcceptConnection (e.PeerID, IntPtr.Zero); };

GKPeerPickerController peerPickerController = 
  new GKPeerPickerController ();

peerPickerController.Delegate = new PeerPickerDelegate (this);

peerPickerController.ConnectionTypesMask = GKPeerPickerConnectionType.Nearby;

peerPickerController.Show ();

The callback to ReceivedData takes a GKDataReceivedEventArgs that includes an NSData containing the message that was sent by a peer (in this case a text message that they entered in a UITextField). The data is sent via a call to GKSession.SendDataToAllPeers. The PeerPickerDelegate is a subclass of GKPeerPickerControllerDelegate. See the solution for the full implementation. Also, GameKit requires you to use the device for Bluetooth networking as this isn’t supported in the simulator, so you’ll need at least 2 devices. Here’s a demo video of the app running between an iPhone 3g and an iPad:

One other thing worth mentioning is the current GameKit stack also has support for peer-peer voice (think in game voice scenarios) and future versions will add more capabilities as well. The Apple GameKit Programing Guide is a good reference to learn more.