iOS Tutorial [Objective C] Writing libraries for B4i

Erel

Administrator
Staff member
Licensed User
B4i libraries are written in Objective C. You need to use a Mac with Xcode and you need to know Objective C.
From my experience it is more difficult to write B4i libraries compared to B4A / B4J libraries.

Libraries are made of three files: library.a, library.h and library.xml.

The xml file is created automatically from the h file using the tool attached to this thread.
This is a command line tool written with B4J. You need to pass two arguments: path to the h file and path to the output file.
For example:
B4X:
java -jar B4Ih2xml.jar test.h test.xml
Note that this tool parses the file line by line. It will not handle properly signatures that span multiple lines.

The a and h files should be eventually copied to the Libs folder on the Mac and the xml file should be copied to the IDE libraries folder.

Libraries should include two binaries: arm64 and armv7.

Supported types

The following types can be exposed to B4i: BOOL, unsigned char, unichar, short, int, long long, float, double and any objc object. Other types cannot be directly exposed.

Header file

The xml is derived from a single h file. This means that all the interfaces that should be exposed need to be defined in this h file.
You need to add all kinds of attributes to the h file (similar to B4A annotations).
This is done by writing a comment that starts with:
//~

For example:
B4X:
//~shortname:LocationManager
//~event:LocationChanged (Location1 As Location)
//~event:AuthorizationStatusChanged (Status As int)
//~event:LocationError
//~event:HeadingChanged (MagneticHeading As Double, TrueHeading As Double)
//~event:AllowCalibration As Boolean
//~version:1.00
//~dependsOn:CoreLocation.framework
@interface B4ILocationManager : NSObject<CLLocationManagerDelegate>
The attributes are case-insensitive and the values are trimmed from extra spaces.

Attributes
  • ShortName - The B4i type name.
  • Event - The type events. Can be used multiple times.
  • Version - library version
  • Author - library author
  • RaisesSynchronousEvents - Tells the compiler that calling this method may cause an event to be raised (before the code returns).
    //~RaisesSynchronousEvents:true
  • ObjectWrapper - If the type is a thin wrapper and it inherits from B4IObjectWrapper then this attribute should be set with the native type name.
    //~ObjectWrapper:CLLocation*
  • DependsOn - Tells the compiler that this library depends on other frameworks or a files.
    //~dependsOn:CoreLocation.framework
    If the framework is an external framework then you need to add .3:
    //~dependson:GoogleMaps.framework.3
    DependsOn with a file (the extension is omitted):
    //~dependson:GoogleAdMobAds
Methods

The exposed methods can have any number of parameters. However the parameter names should be empty:
B4X:
- (B4IMarker *)AddMarker: (double)Lat :(double)Lon :(NSString *)Title;
B4IObjectWrapper

If your object is a thin wrapper over a different object then you can inherit from B4IObjectWrapper. Such objects should not have any instance variables (including not properties that are implicitly back by a variable).
You need to create a class method named getClass that returns the native object class:
B4X:
+(Class)getClass {
  return [CLLocation class];
}
Don't use this type if you are not sure.


Raising events / B4I object

The B4I object is similar to B4A/B4J BA object. You use this object to raise events.
B4IObjectWrapper includes several class methods that help with raising events. You can use it even if your class doesn't subclass B4IObjectWrapper.

The first method is:
B4X:
[B4IObjectWrapper setBIAndEventName:self :bi :EventName];
This method takes the B4I object and the EventName parameter and stores them in the object associated map. The bi object is stored with a weak reference.
It is important to remember that the bi and EventName are tied to the object passed to this method. In this case it is 'self'.

Raising event:
B4X:
[B4IObjectWrapper raiseEvent:self :@"_authorizationstatuschanged:" :@[@((int)status)]];
Don't forget to include the colons in the method name!

Two similar methods: raiseUIEvent and raiseEventFromDifferentThread. These methods send a message to the message queue which will cause the event to be raised when the message is processed.

Tips

- Add a reference to iCore.h in your library project.

Two libraries projects are attached. The iLocation library and iAdMob library.

Latest version of iAdMob is available here: https://github.com/AnywhereSoftware/B4i_iAdMob
 

Attachments

Last edited:

imbault

Well-Known Member
Licensed User
Hi Erel, thank you for this tutorial, which seems tricky.

To be less crypticall, could you just give use an example like creating a sample Library which toggle On/off the Flash which code is pretty readable :

B4X:
- (void)torchOnOff: (BOOL) onOff
{
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    if ([device hasTorch]) {
        [device lockForConfiguration:nil];
        [device setTorchMode: onOff ? AVCaptureTorchModeOn : AVCaptureTorchModeOff];
        [device unlockForConfiguration];
    }
}
 

Erel

Administrator
Staff member
Licensed User
Create a new static library project and add this method. Make sure that it is defined in the h file.

Add //~shortname to the interface.
You will need to add a reference to AVFoundation in your project (the DependsOn attribute is not required in this case).
Compile it and then run the h2xml tool.

I will add this method to the phone library.
 

imbault

Well-Known Member
Licensed User
Create a new static library project and add this method. Make sure that it is defined in the h file.

Add //~shortname to the interface.
You will need to add a reference to AVFoundation in your project (the DependsOn attribute is not required in this case).
Compile it and then run the h2xml tool.

I will add this method to the phone library.
I tried Erel, but xCode is a big piece of cake, can you just provide us an example project with this very sample torchOnOff Objective C piece of code.

And it will be interesting to understand how B4i works technically and internally, do you transcode b4i to java then java to OC, then call some batch or command lines..., how does it works with the apple compiler to create an ipa.... could you send some tech infos in order for us to have a better understanding of the mechanism

Right now, B4i looks like a black box, the windows IDE sends files to the mac which makes the ipa and sends it to the ios iBridge, on the device, I think we'll need to know a little more, don't you think so?

It will help certainly a lot, and we'll begin to see new b4i libraries from our community.


Best regards

Patrick
 
Last edited:

Erel

Administrator
Staff member
Licensed User
There are two simple projects in the zip file attached to the first post.

The source code of the Leadbolt wrapper is also available: http://www.b4x.com/android/forum/threads/leadbolt-ads.47652/#content

How B4i works internally is not required for building libraries.

In Release mode it creates Objective C code that is then compiled with the standard Apple tools.
 

Ramezanpour

Active Member
Licensed User
This thread is a tutorial about creating libraries. For any other questions please start a new thread.

You can post a feature request if you want a library to be created. I do not create private libraries.
can you create library and public it ?
 

Alberto Iglesias

Well-Known Member
Licensed User
Hey... somebody help me please!

I have 2 problems when creating a B4i Library:

  • How return a string in a event: I try (NSString *)strStatus but not working
Boolean statusOK = true;
[B4IObjectWrapper raiseEvent:self : @"_onconnect:" : @[@((int)statusOK)]];


  • After call raiseEvent, the APP stopped and froze... I try put the raiseEventFromDifferentThread but the same behavior

[B4IObjectWrapper raiseEventFromDifferentThread:self : @"_onconnect:" : @[@((int)statusOK)]];

Thanks in advance
Alberto Iglesias
 

Alberto Iglesias

Well-Known Member
Licensed User
1. No... I need get a string on a B4i event, like this:

Sub objMqtt_onConnected(Status As String)
Log("[objMqtt_onConnect] Status = " & Status)
End Sub

2. The raiseEvent line is in a thread routine in xcode (coming in a Callback from a specific library). How to call in this type of routine.
How to put in a handle to call raiseEvent to send to B4i?
 

Erel

Administrator
Staff member
Licensed User

Alberto Iglesias

Well-Known Member
Licensed User
The way I found to resolve the frozen problem, follow:

Inside a function come from a iOS library, I need to call the sub in another thread, like this

[NSThreaddetachNewThreadSelector: @selector(func1: ) toTarget:selfwithObject:[NSNumbernumberWithInteger:0]];


and in a func1 I call:

- (void)func1: (int)pStatus
{
[B4IObjectWrapperraiseEventFromDifferentThread:self : @"_received:" : @[@((int)pStatus)]];

}


WORKS!!!
 

Alberto Iglesias

Well-Known Member
Licensed User
Hey Everybody,

I need pass to Objective-c the view of B4i to use in a function of my new library, how to do that?

I need to get self inside a library, like that:

[av showInViewController:self center:CGPointMake(10,20)];

how to pass this object? Or have already this object into iCore.h?

Thanks.
 
Top