Before you continue - check out the demonstration video:
The demonstration uses RoboVM to build an IOS app (written in Java), and Versile to create a server and provide communication between the app and server.
Details of configuring Versile and RoboVM are not described here. Go to RoboVM website, and check my earlier Versile blog posts for more info.
The source for the IOS app is based on the RoboVM example:
The server source code is written in Groovy, and for the demonstration Groovy Shell is used (groovysh -cp versile.jar):
Now when the server is started. You can also launch the IOS app (make sure to compile to the build folder first). These are standard parameters for robovm - the only difference is the forcelinkclasses parameter referring to crypto classes needed by Versile.
The demonstration uses RoboVM to build an IOS app (written in Java), and Versile to create a server and provide communication between the app and server.
Details of configuring Versile and RoboVM are not described here. Go to RoboVM website, and check my earlier Versile blog posts for more info.
The source for the IOS app is based on the RoboVM example:
import java.util.Date;
import org.robovm.cocoatouch.coregraphics.*;
import org.robovm.cocoatouch.foundation.*;
import org.robovm.cocoatouch.uikit.*;
import org.robovm.objc.block.VoidBlock;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.NativeObject;
import org.robovm.rt.bro.annotation.Bridge;
import org.robovm.rt.bro.annotation.Library;
import org.versile.Versile;
import org.versile.orb.entity.VProxy;
import org.versile.reactor.url.VUrl;
import org.versile.vse.VSEResolver;
import org.versile.vse.util.VFunction;
public class VersileRoboVM extends UIApplicationDelegate.Adapter {
static {
// Versile support for VSE types
VSEResolver.addToGlobalModules();
}
private UIWindow window = null;
private int clickCount = 0;
// The Versile proxy to call remote functions (server)
VProxy proxy;
// IOS functions for updating UI from outside the main thread
@Library("dispatch")
public static class Dispatch {
static {
Bro.bind();
}
public static class Queue extends NativeObject {}
@Bridge(symbol = "dispatch_get_current_queue")
public static native Queue getMainQueue();
@Bridge(symbol = "dispatch_async")
public static native void async(Queue queue, VoidBlock block);
@Bridge(symbol = "dispatch_sync")
public static native void sync(Queue queue, VoidBlock block);
}
@Override
public boolean didFinishLaunching(UIApplication application,
NSDictionary launchOptions) {
// Versile license
Versile.setInternalUseAGPL();
final Dispatch.Queue queue = Dispatch.getMainQueue();
final UIButton button = UIButton.fromType(UIButtonType.RoundedRect);
button.setFrame(new CGRect(0f, 121.0f, 200f, 37.0f));
button.setTitle("Click me!", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() {
@Override
public void onTouchUpInside(UIControl control, UIEvent event) {
try {
// Call server and update button text with returned string
String ret = ""+proxy.call("calledFromIOS");
button.setTitle(ret, UIControlState.Normal);
} catch (Exception ex) {
System.out.println(ex.toString()+" "+ex.getMessage());
ex.printStackTrace();
}
}
});
final UILabel label = new UILabel(new CGRect(0, 160.0f, 200f, 37.0f));
label.setUserInteractionEnabled(true);
label.setText("TestLabel");
try {
// Set up proxy to connect to Versile server
proxy = (VProxy) VUrl.resolve("vop://localhost/");
// Post a function so that the server can call the IOS app
proxy.call("receiveFunction", new VFunction(1,1,null) {
@Override
public Object function(final Object... args) throws Exception {
Dispatch.sync(queue, new VoidBlock() {
public void invoke() {
label.setText("From server: "+args[0]);
label.setNeedsDisplay();
System.out.println("From server: "+args[0]);
}
});
return new Date().toString();
}
});
} catch (Exception ex) {
System.out.println(ex.toString()+" "+ex.getMessage());
ex.printStackTrace();
}
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGrayColor());
window.addSubview(button);
window.addSubview(label);
window.makeKeyAndVisible();
return true;
}
public static void main(String[] args) {
NSAutoreleasePool pool = new NSAutoreleasePool();
UIApplication.main(args, null, VersileRoboVM.class);
pool.drain();
}
}
import org.robovm.cocoatouch.coregraphics.*;
import org.robovm.cocoatouch.foundation.*;
import org.robovm.cocoatouch.uikit.*;
import org.robovm.objc.block.VoidBlock;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.NativeObject;
import org.robovm.rt.bro.annotation.Bridge;
import org.robovm.rt.bro.annotation.Library;
import org.versile.Versile;
import org.versile.orb.entity.VProxy;
import org.versile.reactor.url.VUrl;
import org.versile.vse.VSEResolver;
import org.versile.vse.util.VFunction;
public class VersileRoboVM extends UIApplicationDelegate.Adapter {
static {
// Versile support for VSE types
VSEResolver.addToGlobalModules();
}
private UIWindow window = null;
private int clickCount = 0;
// The Versile proxy to call remote functions (server)
VProxy proxy;
// IOS functions for updating UI from outside the main thread
@Library("dispatch")
public static class Dispatch {
static {
Bro.bind();
}
public static class Queue extends NativeObject {}
@Bridge(symbol = "dispatch_get_current_queue")
public static native Queue getMainQueue();
@Bridge(symbol = "dispatch_async")
public static native void async(Queue queue, VoidBlock block);
@Bridge(symbol = "dispatch_sync")
public static native void sync(Queue queue, VoidBlock block);
}
@Override
public boolean didFinishLaunching(UIApplication application,
NSDictionary launchOptions) {
// Versile license
Versile.setInternalUseAGPL();
final Dispatch.Queue queue = Dispatch.getMainQueue();
final UIButton button = UIButton.fromType(UIButtonType.RoundedRect);
button.setFrame(new CGRect(0f, 121.0f, 200f, 37.0f));
button.setTitle("Click me!", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() {
@Override
public void onTouchUpInside(UIControl control, UIEvent event) {
try {
// Call server and update button text with returned string
String ret = ""+proxy.call("calledFromIOS");
button.setTitle(ret, UIControlState.Normal);
} catch (Exception ex) {
System.out.println(ex.toString()+" "+ex.getMessage());
ex.printStackTrace();
}
}
});
final UILabel label = new UILabel(new CGRect(0, 160.0f, 200f, 37.0f));
label.setUserInteractionEnabled(true);
label.setText("TestLabel");
try {
// Set up proxy to connect to Versile server
proxy = (VProxy) VUrl.resolve("vop://localhost/");
// Post a function so that the server can call the IOS app
proxy.call("receiveFunction", new VFunction(1,1,null) {
@Override
public Object function(final Object... args) throws Exception {
Dispatch.sync(queue, new VoidBlock() {
public void invoke() {
label.setText("From server: "+args[0]);
label.setNeedsDisplay();
System.out.println("From server: "+args[0]);
}
});
return new Date().toString();
}
});
} catch (Exception ex) {
System.out.println(ex.toString()+" "+ex.getMessage());
ex.printStackTrace();
}
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGrayColor());
window.addSubview(button);
window.addSubview(label);
window.makeKeyAndVisible();
return true;
}
public static void main(String[] args) {
NSAutoreleasePool pool = new NSAutoreleasePool();
UIApplication.main(args, null, VersileRoboVM.class);
pool.drain();
}
}
The server source code is written in Groovy, and for the demonstration Groovy Shell is used (groovysh -cp versile.jar):
import org.versile.Versile
import org.versile.reactor.service.VOPService
import org.versile.orb.service.VGatewayFactory
import org.versile.orb.entity.VObject
import org.versile.orb.external.VExternal
import org.versile.orb.external.Publish
import org.versile.common.auth.VPrivateCredentials;
import org.versile.crypto.VDecentralIdentity;
import java.util.logging.LogManager
import java.util.logging.Logger
import java.util.logging.Level
import java.util.logging.ConsoleHandler
import java.util.Properties
import java.io.InputStream
import org.versile.vse.VSEResolver
import org.versile.orb.entity.VProxy
Versile.setInternalUseAGPL();
VSEResolver.addToGlobalModules();
// Functions to be called by the IOS app
class Svc extends VExternal {
static Object remoteFunction;
static String returnString = "Hello!";
@Publish(show=true, ctx=false) public String calledFromIOS() { System.out.println("IOS calling, returning: "+returnString); return returnString; }
@Publish(show=true, ctx=false) public void receiveFunction(Object function) { remoteFunction = function; }
}
// A gateway that can be connected to a VOPService listener
class GW implements VGatewayFactory {
public VObject build() { return new Svc(); }
}
// Create server identification credentials
key = VDecentralIdentity.dia(1024, "", "", "someServerPwd");
credentials = new VPrivateCredentials(key);
// Create the service using the gateway and credentials
service = new VOPService(new GW(),credentials,null);
// Start the service
service.start();
import org.versile.reactor.service.VOPService
import org.versile.orb.service.VGatewayFactory
import org.versile.orb.entity.VObject
import org.versile.orb.external.VExternal
import org.versile.orb.external.Publish
import org.versile.common.auth.VPrivateCredentials;
import org.versile.crypto.VDecentralIdentity;
import java.util.logging.LogManager
import java.util.logging.Logger
import java.util.logging.Level
import java.util.logging.ConsoleHandler
import java.util.Properties
import java.io.InputStream
import org.versile.vse.VSEResolver
import org.versile.orb.entity.VProxy
Versile.setInternalUseAGPL();
VSEResolver.addToGlobalModules();
// Functions to be called by the IOS app
class Svc extends VExternal {
static Object remoteFunction;
static String returnString = "Hello!";
@Publish(show=true, ctx=false) public String calledFromIOS() { System.out.println("IOS calling, returning: "+returnString); return returnString; }
@Publish(show=true, ctx=false) public void receiveFunction(Object function) { remoteFunction = function; }
}
// A gateway that can be connected to a VOPService listener
class GW implements VGatewayFactory {
public VObject build() { return new Svc(); }
}
// Create server identification credentials
key = VDecentralIdentity.dia(1024, "", "", "someServerPwd");
credentials = new VPrivateCredentials(key);
// Create the service using the gateway and credentials
service = new VOPService(new GW(),credentials,null);
// Start the service
service.start();
Now when the server is started. You can also launch the IOS app (make sure to compile to the build folder first). These are standard parameters for robovm - the only difference is the forcelinkclasses parameter referring to crypto classes needed by Versile.
robovm-0.0.5/bin/robovm -verbose -forcelinkclasses com.android.org.bouncycastle.jce.provider.symmetric.Blowfish:com.android.org.bouncycastle.crypto.engines.BlowfishEngine:org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK:org.apache.harmony.xnet.provider.jsse.JSSEProvider:org.apache.harmony.security.provider.crypto.CryptoProvider:org.apache.harmony.security.provider.cert.DRLCertFactory:com.android.org.bouncycastle.jce.provider.BouncyCastleProvider:org.apache.harmony.xnet.provider.jsse.OpenSSLProvider -arch x86 -os ios -cp build:versile.jar:robovm-0.0.5/lib/robovm-cocoatouch.jar:robovm-0.0.5/lib/robovm-rt.jar:robovm-0.0.5/lib/robovm-objc.jar -run VersileRoboVM
Comments