Writing a RemObjects SDK for .NET Server and Mac Client - Start to Finish

From RemObjects Wiki

Jump to:navigation, search
Xcode Platform Header-48.png

This is a RemObjects SDK for Xcode Articles topic
Feel free to add your notes to this topic below.


In this article, we will walk through the process of creating a fully working client and server application using RemObjects SDK, with the server written in .NET and the client written in Xcode.

The article assumes you have Visual Studio and the latest RemObjects SDK for .NET installed in a Windows VM or on a separate PC on your network, and the Xcode 3.0 with the latest RemObjects SDK for Xcode on your Mac.

Contents

The .NET Server

Start off by going into Visual Studio to create your server application. Choose File|New Project from the menu and locate the RemObjects SDK node underneath the language of your choice (C#, Delphi Prism or Visual Basic .NET) - for our example, we'll choose C#, as most people are familiar with that. Don't worry, you'll be writing literally two lines of code for this, which easily translate into Delphi Prism or VB if necessary).

For the purposes of this article, choose the Windows Forms Server option, pick a destination folder and name your project 'MyROServer'. Click OK to continue.

VisualStudioFileNewROWinFormsServer.png

Next, you will be presented with the New RemObjects SDK Server wizard. Since we will be creating our client application in Xcode on the Mac side, you can uncheck the Also create a matching client application checkbox. Beyond that, all the default settings should be good, although in a real-life project you may want to click into Advanced Project Options to configure names for service and library, or choose different communication channels. Once again, click OK to have the project created.

VisualStudioNewROServerWizard.png

On the right-hand side of Visual Studio, you now see your project in Solution Explorer. Next to the usual source files in your language of choice (here .cs), you will see a .RODL file which contains the service definitions for your server. Double-click the file (or press the red RemObjects SDK icon in the toolbar above) to edit the RODL.

VisualStudioROServerSoluntionExplorer1.png

This will launch Service Builder. New projects created from templates already containing an initial service, with two methods named Sum and GetServerTime. For the purpose of this article, you will simply keep those methods - in a real project you would of course add your own. Do however change the lengthy names for the library and service to MyROLibrary and MyROService, respectively.

ServiceBuilderNewServiceTemplate.png

When done, simply close Service Builder to get back to Visual Studio. You will notice that your project gets automatically updated, and three new files have been added in Solution Explorer: MyROLibrary_Intf.cs, MyROLibrary_Invk.cs and MyROLibrary_Impl.cs.

VisualStudioROServerSoluntionExplorer2.png

You can ignore the Interface ("Intf") and Invoker ("Invk") files for now, but you do want to take a look at the Implementation ("Impl") file, as this is where you will implement your service. By default, service classes have a design surface for non-visual components, as you can see by the "component"-style icon the Impl file sports. This is to make it easy to implement more complex server logic or data access. For our purposes, we just want to write some code, so right-click the MyROService_Impl.cs file and choose View Code to open the class in the source editor.

You will find a class called MyROService, with a bunch of attributes attached, that mark the class as being published as a RemObjects SDK service. At the bottom of the source, you will find two empty method bodies - Sum and GetServerTime, respectively. It is these two methods that need to be filled with an implementation for your server to do its job. Add the following code:

public virtual int Sum(int A, int B)
{
    return A + B;
}

public virtual System.DateTime GetServerTime()
{
    return DateTime.Now;
}

Press the Play button (or hit F5), and your server compiles, starts and is up and running.

Depending on your Windows Firewall settings, you might get a warning dialog such as the following, alerting you that the application is opening a server network port. Click Allow Access to permit this.

WindowsFirewall.png

You should now see your server running. You can confirm it is accessible by opening your web browser and entering http://localhost:8099 as address, which should show you the HMTL Info Page provided by the server.

RunningRONetServer.png

ROZeroConf

In theory, we're ready to switch to the Mac to implement our client now, but there is one more thing we can do to make this easier: If you are using RemObjects SDK for .NET 5.0.39 or later, you can add ROZeroConf support that will allow our server to be discovered on the network automatically. It's easy:

Close the running server and go back to Visual Studio. in Solution Explorer, locate the Main form, and double-click it to open the form designer. In the Toolbox window on the left (available via View|Toolbox if it is not showing yet) open up the RemObjects SDK section, and double-click the ZeroConfRegistration component at the very bottom, to add it to your form.

ZeroConfRegistrationComonentInToolPalette.png

Finally, select the the newly added zeroConfRegistration1 component on your form and in the Properties window on the right (F4 brings it up, if necessary), select the ServerChannel property from the drop-down.

ZeroConfRegistrationServerChannelProperty.png

Your server is now ready to register services with ROZeroConf, there's just one last thing missing: the actual service needs to be marked to be registered.

Go back into your implementation file and at the top of your class declaration, next to the existing attributes, add the following new attribute:

[...other attributes...]
[RemObjects.SDK.Server.ZeroConfServiceType("MyROService")]
public class MyROService : ...

Click Play (or press F5) again to rebuild and start your server, and (assuming either Apple's Bonjour or the RemObjects ZeroConf Service is running) it is now publishing itself, to be discoverable across your LAN.

You're now ready to start working on your Mac client, but before leaving your PC, there's one more thing: open the folder containing your project in Windows Explorer (for example, right-click the editor tab of one of your files in Visual Studio and choose Open Containing Folder, and copy the RODL file to your Mac (if you're using VMware, simply drag the file out of the VM onto your desktop; if you're using separate computers, send it to yourself via email or copy to to a network share).

The Xcode Client

Now go to the Mac and start up Xcode (normally installed in the /Developer/Applications folder of your root drive). We'll start off with a standard Mac OS X Cocoa project available via File|New Project... menu in the Applications section:

XcodeNewCocoaApplicationProject.png

Pick a folder of your choice to save the project, and name it "MyROClient', to match your server. A new Xcode project window comes up and will look something like this:

NewProjectShowingInXcode.png

Linking the Framework

First, you will get our new project ready to use RemObjects SDK by adding the appropriate framework. This part is explained in more detail and with more options here, but for this article, two simple steps will suffice:

First, open a Finder window, and locate the RemObjectsSDK.framework (usually under /Developer/RemObjects Software/Bin/Release). Grab the .framework folder and drag it onto the 'Frameworks node of your project. When prompted, make sure to select to not copy it into your project (i.e. just reference it from its original location).

XcodeAddFilesToProjectNoCopy.png

This takes care of linking to RemObjects SDK, but you also need to set it up for deployment with your app. Open the Targets node of your project, then right-click the single MyROClient target below it. Choose Add|New Build Phase|New Copy Files Build Phase from the menu. In the dialog that opens up, choose Frameworks as the Destination, then close it.

Finally, drag RemObjectsSDK.framework from the Frameworks node of your project down onto the new Copy Files build phase you created.

Importing the RODL

Next, you will want to generate interface code for your services from your service RODL. Locate the RODL file you copied from your PC earlier and double-click it to open the rodl2objc tool. On the RODL Summary click Save Intf Code and save the generated code to your hard disk.

Rodl2objcSmall.png

As you did with the framework, before, locate the newly generated .m and .h source files in Finder, and drag them into your project, preferable into the Classes node. This time, when asked, select to copy the files into the project.

Building a Quick UI

Since this is not an article on general Cocoa UI development, we will skip over the setup of our user interface rather quickly. If you are not familiar with Interface Builder and writing Cocoa applications, please check the available books and articles on the subject.

Double-click the MainMenu.xib file inside your project to open Interface Builder and design the minimal UI for the application, as shown below. Add three Text Fields for Sum and one for GetServerTime, and one button each. Also, add a Combo Box to allow the user to specify or select the server to talk to.

SampleROOSXClientUI.png

Add a new Object to the XIB file, and set its type to a new class called MainWindowController. Add the outlets and actions as shown, and connect them up with the appropriate controls. Also don't forget to connect the window property of the window controller to the base window.

SampleROOSXClientSetupInInterfaceBuilder.png

Finally, choose File|Write Class Files... to generate .m and .h files for the MainWindowController class and have them added to your project. Save the changes to your XIB and close Interface Builder, to return to Xcode.

In Xcode, open the new MainWindowController.h file and set the ancestor for the class to NSWindowController.

Writing the Code

Now, all that's left is to write the actual client code, in MainWindowController.m. Open the file in the editor, and start by adding two new import sections at the top, to include the RemObjects SDK framework, and your Intf file, respectively:

#import "RemObjectsSDK/RemObjectsSDK.h"
#import "MyROLibrary_Intf.h"

Next, create a new method to instantiate the proxy class you will use to make calls to the server. In a real-life application, you would probably create this globally and persist it between calls, but to keep things simple, we will just create the proxy as needed:

- (MyROService_Proxy *)getProxy
{
    ROClientChannel *clientChannel = [ROHTTPClientChannel channelWithTargetUrl:[serverCombo stringValue]];
    ROMessage *message = [ROBinMessage message];
    return [MyROService_Proxy proxyWithMessage:message channel:clientChannel];    
}

This method creates a new BinMessage, and a new HTTP client channel based on the URL entered in the serverCombo edit. It will then create a proxy for the MyROService using those two, and return it.

Implementing your event handlers is now dead simple:

- (IBAction) sum:(id)sender
{
    int a = [aField intValue];
    int b = [bField intValue];
    int result = [[self getProxy] Sum:a:b];
    [resultField setStringValue:[NSString stringWithFormat:@"%d", result]];
}

- (IBAction) getServerDate:(id)sender
{
    NSDate *d = [[self getProxy] GetServerTime];
    [dateField setStringValue:[d description]];
}

Click Build and Go, enter the address of your server (such as "http://your.kip.address:8099), and your client is fully functional:

SampleROOSXClientWorking1.png

The Icing on the Cake

Wait a moment. Did we say 'enter the address of your server'? That's not very user friendly. Back when you implemented your server, you added ROZeroConf support so your server could automatically be discovered. The client should leverage this to find available servers automatically. Cocoa supports ZeroConf discovery out of the box, so this is easily done with just a few methods:

First, provide an overridden -init method where the application starts the discovery process for services on launch:

- (id) init
{
    self = [super init];
    if (self) 
    {
        services = [[NSMutableDictionary alloc] init];
        NSNetServiceBrowser *netServiceBrowser = [[NSNetServiceBrowser alloc] init];
        [netServiceBrowser setDelegate:self];
        [netServiceBrowser searchForServicesOfType:@"_myroservice_rosdk._tcp." inDomain:@""];
        
    }
    return self;
}

This creates a local dictionary (you will need to add the appropriate

NSMutableDictionary *services

variable to the header file), as well as a NSNetServiceBrowser instance that will start looking for servers on the network. The browser will look for services of type _myroservice_rosdk._tcp. only - where myroservice is the name you specified earlier when registering your server for discovery.

Browsing for services is an asynchronous process, so the call to searchForServicesOfType:inDomain: will return right away, and the actual discovery will happen in the background. As services are found (or disappear again), two delegate methods will be called on your class - these you will need to implement next:

- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser 
           didFindService:(NSNetService *)netService 
               moreComing:(BOOL)moreServicesComing
{
    [services setObject:netService forKey:[netService name]];
    [serverCombo addItemWithObjectValue:[netService name]];        
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser 
         didRemoveService:(NSNetService *)netService 
               moreComing:(BOOL)moreServicesComing
{
    [services removeObjectForKey:[netService name]];
    [serverCombo removeItemWithObjectValue:[netService name]];
}

What these do is to simply add the service information to the dictionary when a new service is found, and to remove it again when a service goes offline. They also update the list of the combo box to add or remove the service in question.

It's important to realize that ZeroConf discovery is a continued process. Over the life-time of your application, new services might show up, or services discovered before might disappear. This code will keep the UI updated, so - for example - as soon as you shut down your server on the Windows machine, it will disappear from the drop-down; if you start it again, it will show up.

Finally, you need to update -getProxy. The previous implementation simply used the text from the combo box as URL. The new implementation will allow the user to either manually type in a URL as before, or - if found - use of of the discovered services:

- (MyROService_Proxy *)getProxy
{
    NSNetService *netService = [services objectForKey:[serverCombo stringValue]];
    if (netService)
    {
        RORemoteService *service = [RORemoteService remoteServiceWithNetService:netService]; 
        return [MyROService_Proxy proxyWithService:service];
    }
    else
    {
        //same as before:
        ROClientChannel *clientChannel = [ROHTTPClientChannel channelWithTargetUrl:[serverCombo stringValue]];
        ROMessage *message = [ROBinMessage message];
        return [MyROService_Proxy proxyWithMessage:message channel:clientChannel];    
    }
}

Run the application again, and as you can see, your server, running on your Windows machine elsewhere on the network, will automatically show up.

SampleROOSXClientWorkingWithZeroConf.png
Note: If you you are using VMware to run Windows on your Mac, make sure you are using "Bridged" or "Host only" network mode. When using "NAT", your VM is not technically on the same network as your Mac host.

See Also

Ro-48.png

Product: RemObjects SDK
Available Editions: RemObjects SDK for .NET, Delphi and Xcode

GlossaryArchitectureArticlesFeaturesLibrarySamples

Navigation
products
hubs
special
Toolbox