Creating CocoaSharp Applications for the Mac

From The Oxygene Language Wiki

Jump to:navigation, search

This is a Platform topic
Feel free to add your notes to this topic below.



Delphi Prism provides quite a few features targeted specifically at developers who want to leverage the cross-platform promise of Mono and .NET and write applications for Mac OS X using the standard Cocoa UI framework and the Cocoa# managed support provided by Mono.


Why Cocoa?

The most obvious choice for developing Mono applications for the Mac might be to go with WinForms. After all, WinForms is now pretty well supported by Mono and will work cross-platform, allowing you to share your code between Windows and the Mac. While that is true, WinForms applications on the Mac will rely on X11, and look awkward and out of place in OS X's Aqua UI - much like Java or Gtk applications somehow look wrong on Windows. Cocoa is the preferred UI library for Mac applications (replacing its predecessor Carbon), and your best choice for creating apps that look and feel like true Mac apps - because they are.

Of course, well designed applications should properly separate user interface and application logic, anyway, so you can still share the bulk of your application code between platforms, and yet provide a true "native" UI everywhere - Cocoa on the Mac and WinForms on Windows.


Cocoa, Cocoa# and Delphi Prism

Historically, Cocoa applications have been written in Objective-C, a language that builds on the original to add object oriented concepts, but is distinctly different from C++, C# or other C-based OO languages. Luckily, you don't need to worry about Objective-C much (although some fundamental understanding of the language is certainly recommended for serious Cocoa development - if only to understand the samples and code snippets in the documentation), for two reasons:

  1. Mono provides an excellent interface to the Cocoa API, fittingly named Cocoa#. This interface is provided in cocoa-sharp.dll (more on that later), which provides managed "wrapper" classes for all important Cocoa classes (and is easily extended, as well).
  2. On top of what Mono provides as part of Cocoa#, Delphi Prism comes with state-of-the-art IDE integration for Mono in general and Cocoa# in particular, making for a development experience that is second to none for Cocoa# developers.

With these two combined, Delphi Prism makes it very easy for developers familiar with .NET to write Mac applications or port their existing .NET applications to the Mac. All the IDE extensibility provided builds on Visual Studio, the IDE most .NET developers are familiar with - the idea being that development can happen either on Windows systems or (ideally) on a Mac using VMware Fusion or Parallels.

Let's see what's involved in getting up and running and building a first 'Hello World' Mac project in Delphi Prism.

Setting up Shop

To set up a Delphi Prism working environment for Cocoa# (Monoxide), you will need:

First, ensure that you have the Apple Developer Tools installed (also commonly referred to as Xcode, the name of the included IDE). If they are available, you can typically find them in the /Developer sub-folder of your boot disk (not inside Applications). Xcode itself is not needed (although it will be helpful to have, if you want to try out Objective-C based Cocoa samples), but later we will want to use the Interface Builder to design our UI. Next, download and install the latest Mono for OS X from here. Just run the setup, and you're set. Now boot up your Windows VM, download the latest Mono for Windows and install this as well.

Technically, we won't need Mono on the Windows machine, but having it installed makes it easier to link your projects directly against Mono - one of the benefits of that being that you can use Mono-specific classes and get full intellisense from within the IDE. Unfortunately, Mono for Windows currently doesn't ship with the cocoa-sharp.dll required to build Cocoa# apps. But that's easily fixed: just copy the dll off your Mac (from /Library/Frameworks/Mono.framework/Versions/Current/lib/mono/cocoa-sharp) to Windows (to C:\Program Files\Mono 1.x\...). Remember to repeat this step if you upgrade to newer versions of Mono, as the cocoa-sharp.dll is still evolving and you will want to make sure that you use the latest version.

Finally, install Visual Studio 2005 or 2008 and Delphi Prism (or, if you don't have a stand-alone VS, the combined "Delphi Prism with Visual Studio").

To make the switch between Mac and Windows more seamless during development and testing, we recommend setting up a shared folder on the Mac, that you can access from within your VM. This way, you can build your projects in Visual Studio, switch back to the Mac and run them without the need to copy/paste files around.

Ok, we're set now to write our first Mac application.


Your First Mac Application

Let's get our hands dirty. Delphi Prism ships special templates for Cocoa#, in the MonOxide folder of the New Project dialog ("MonOxide" is the code name for an ongoing project to bring a better Mono development experience to Delphi Prism). Make sure to browse for a target folder on the network drive shared with the Mac host (i.e. on \\.host\) and then create the new project:

MyCocoaApp1.png


Take a first look at what the template created for us by checking the project in the Solution Explorer. You will notice several items of interest:

MyCocoaApp2.png


First, you will find, that instead of the typical App.ico icon file, there's an App.icns. Because the target of our project is a Mac application, we need an appropriate Mac format icon, and that's what this is. You can replace this with any other Mac icon file, or change it by using one of the many tools available for editing icons. On Windows, Axialis Icon Workshop is an excellent tool that can handle both Windows and Mac icon files and is, in fact, what we used to create this one.

Next, you will notice a folder with a strange "metal plate" icon called Interface.nib. Cocoa uses .NIB files to design the user interface, vaguely comparable to XAML files in WPF of DFMs in Delphi. On the Mac side, .NIB (which is short for NeXT Interface Builder) files are "packages", meaning that they are actually folders, but are treated as single files by the Finder. It is this dual-nature that you see here, with Interface.nib actually showing up as a folder in your solution.

Inside the .NIB, you will find two .NIB files, but their XML based content is really irrelevant, as you will never touch them directly, and instead use the Interface Builder to edit your UI, as described further down. What is of note is that if you inspect the properties of designable.nib, you see a Custom Tool defined, and, indeed, a designable.pas file is shown nested below the .NIB. This .PAS file is auto-generated by the Custom tool based on the data defined in your .NIB. You can look at classes.pas now, but you will find it rather empty or at least uninteresting, so let's wait with that until we design some UI later.

Finally, you will find an ApplicationController.pas file in your project. This file provides a skeleton for a class we'll be talking about more, and will be the place where you will add a lot of the core UI functionality of your application - as a matter of fact, for our simple project, all of the actual application code will eventually go here.

Now that we've taken a brief look around, let's build the project (Ctrl+Shift+B) and run it to see what happens. Being a Mac app, of course we cannot run our project inside the Windows VM. So let's switch over to the Mac, open the Finder and browse to the project we just created. Drill down into the bin/Debug folder, and we'll find MyCocoaApp.app - ready to run with a simple double-click.

MyCocoaApp3.png


Three things happen when you run your application: First, the application icon pops up in the dock, as it does for any other native Mac program. Second, the (empty) main form opens and last, the Mac's menu bar changes to show your application's menu along with the fancy title MyCocoaApp. You will see that all the standard menu infrastructure is already being provided and just waiting to be filled. All in all, you are looking at what feels (and is) a full native OS X application.

Take a few more minutes to play around and let this sink in, then close your application (Cmd-Q). Now let's extend it to do something (more or less) useful...


Designing Your User Interface in the Interface Builder

While you're Mac-side, browse up the folders back to your main project, and double-click the Interface.nib (remember that this shows as a file on the Mac, but is in fact the Interface.nib folder you see in Visual Studio. If you are curious, you can right-click it and select "Show Package Contents" to get a new Finder window that shows you the same three files (two .NIBs and one .PAS) that you see in the IDE.

Double-clicking the .NIB opens it in Interface Builder, the visual UI editor provided by Apple Developer Tools. When first started, you will see two Windows that look something like this:

MyCocoaApp4.png


The top window, aptly titled "Window", is your application main form, which we'll redesign to define the look of our app. Behind it, the window titled "Interface.nib' is called the "Document Window", and it lists all the elements defined in your .NIB. Most relevant for now are the following three items:

First, let's design our main form. Get rid of the toolbar by clicking on it and pressing backspace. Next, bring up the component Library by choosing Tools|Library from the menu (or pressing Cmd+Shift+L).


MyCocoaApp5.png


Drop an NSButton (from the "Buttons" folder) and an NSTextField (from the "Inputs & Values" folder), and arrange them on the form as shown below. Note that the Interface Builder will automatically show you guides to help you align your controls. (It also provides quite sophisticated means for controlling anchoring and the like, which we will ignore for now).

Double-click the button to turn its caption into edit mode, and rename it to "Speak".

MyCocoaApp6.png


We're almost done in the Interface Builder, our last step is to go to the Application Controller, and build some bridges between the controls we dropped and our code.

By default, any controls you drop in the interface builder live on your form anonymously. Just like when you select GenerateMember=false in WinForms, the controls exist, but you have no way to access them from code. To change that, we need to define so called "Outlets", which you can think of as properties that reference your controls.

Select the ApplicationController class in the Document Window, and then bring up the Inspector, via Tools|Inspector (or Cmd+Shift+I). Select the second tab from the right (the one with the "i" in a circle), and define two outlets, one for the button and one for the textfield - as shown below. While you're at it, also define an Action called "onClick", so that we will be notified when the button gets clicked.


MyCocoaApp7.png


Now, hook up the two outlets by holding down the Control (not Command) key, by dragging from the ApplicationController to the controls, to make the connection between them. When you let go of the mouse button, a small black popup appears, allowing you to select the outlet you want to connect (since our simple app only has one button and one edit, there is not much choice). In a similar fashion, Ctrl-drag from the button back to the Application Controller and connect the onClick: action.


MyCocoaApp8.png


Our work within the Interface Builder is done. Save the .NIB via File|Save (or Cmd-S) and switch back to your Windows VM and Visual Studio. You can close the interface builder, or leave it open for future work.


The Code

Back in Visual Studio, right-click the designable.nib file in Solution Explorer and choose "Run Custom Tool". This will ensure that the code in designable.pas will be updated to reflect our changes in the .NIB. Just for interest, open the designable.pas file and have a look at the ApplicationController class. You will notice that both of our outlets are represented as fields, giving us direct access to the button and edit box. Also, a "partial empty" method has been declared to match our "onClick:" actions:

[Register('ApplicationController')]
  ApplicationController = public partial class(Cocoa.Object)
    public
      var [Connect] edit_TextToSpeak: Cocoa.TextField;
      var [Connect] button_Speak: Cocoa.Button;
      [&Export('onClick:')]
      method onClick(sender: Cocoa.Object); partial; empty;
  end;


Since designable.pas is an auto-generated code file, we do not want to touch it with our own code - it would be overwritten the next time you update the .NIB. Instead, open ApplicationController.pas, which includes a second partial of the same class. This is where we'll place our own code.

It is worth mentioning that there is nothing magic about ApplicationController as a class. As you expand your application and it gets more complicated, you can define more classes in Interface Builder, define outlets or actions for them, and partial classes for all of them will be generated for you in designable.pas. ApplicationController is merely provided as a starting point by the Delphi Prism template, so you don't have to define this first class yourself.

ApplicationController.pas is looking fairly empty, so let's add an implementation for our onClick action. In the "public" section of the class declaration, type "method" followed by a space. Code Completion will automatically provide a suggestion for "method onClick(sender: Object); partial;" in the list. Complete the method, and add the following code:

method ApplicationController.onClick(sender: Object);
begin
  var lSpeech: SpeechSynthesizer := new SpeechSynthesizer();
  lSpeech.Initialize;
  lSpeech.StartSpeaking(edit_TextToSpeak.Value);
end;

And with that, we're done! Build your application, switch back to your Mac and run the MyCocoaApp.app. When the form opens, enter text of your choice ("All work and no play makes Jack a dull boy" always works well), and click the Speak button to have the OS X Speech engine read the text back to you out loud.


Summary

Of course this brief article can only begin to scratch the surface of OS X development with Delphi Prism, but it should give you a general starting point. Please refer to Apple's documentation and the various texts on general Cocoa development that are available (both online and in printed form) to learn more about the platform's API.


See Also


Oxygene-48.png

Area: Oxygene Platforms

Platform GlossaryKeywordsTypesFAQHow To

Navigation
Areas
More
Toolbox