iOS Tutorial [Objective C] Writing libraries for B4i

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

  • Libraries.zip
    101.8 KB · Views: 756
  • B4Ih2xml.jar
    98.2 KB · Views: 453
Last edited:

Alberto Iglesias

Well-Known Member
Licensed User
I don´t think so, look:


The dependencies of library:

libdep.png



and the dependencies of example of app calling the library:

appdep.png



Have another place I can check?

This app example calling the library, works very well, but when compiling with B4i no? Where I do something wrong????
 

Alberto Iglesias

Well-Known Member
Licensed User
Erel,

I tried with another library and the same error, look:

cd /Users/iglesias/Development/B4i-MacServer/UploadedProjects/iglesias
export IPHONEOS_DEPLOYMENT_TARGET=7.0
export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/iglesias/bin/Sencha/Cmd/3.1.0.192:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/git/bin:/Users/iglesias/Development/sdk/platform-tools:/Users/iglesias/Development/sdk/tools"
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.4.sdk -L/Users/iglesias/Development/B4i-MacServer/UploadedProjects/iglesias/Payload -L../../Libs -F/Users/iglesias/Development/B4i-MacServer/UploadedProjects/iglesias/Payload -F../../Libs -filelist /Users/iglesias/Development/B4i-MacServer/UploadedProjects/iglesias/build/B4iProject.build/Release-iphoneos/B4iProject.build/Objects-normal/arm64/POP3SAMPLE.LinkFileList -ObjC -fobjc-arc -fobjc-link-runtime -miphoneos-version-min=7.0 -lCore -framework Foundation -framework CoreGraphics -framework UIKit -lvnpop -framework AVFoundation -lchilkatIos -lresolv -lpthread -liDebug -Xlinker -dependency_info -Xlinker /Users/iglesias/Development/B4i-MacServer/UploadedProjects/iglesias/build/B4iProject.build/Release-iphoneos/B4iProject.build/Objects-normal/arm64/POP3SAMPLE_dependency_info.dat -o /Users/iglesias/Development/B4i-MacServer/UploadedProjects/iglesias/Payload/POP3SAMPLE.app/POP3SAMPLE
Undefined symbols for architecture arm64:
"std::terminate()", referenced from:
___clang_call_terminate in libchilkatIos.a(CkoAtom.o)
___clang_call_terminate in libchilkatIos.a(CkoBounce.o)
___clang_call_terminate in libchilkatIos.a(CkoCertChain.o)
___clang_call_terminate in libchilkatIos.a(CkoCertStore.o)
___clang_call_terminate in libchilkatIos.a(CkoCgi.o)
___clang_call_terminate in libchilkatIos.a(CkoCrypt2.o)
___clang_call_terminate in libchilkatIos.a(CkoDateTime.o)
...
"vtable for __cxxabiv1::__class_type_info", referenced from:
typeinfo for CkObject in libchilkatIos.a(CkObject.o)
typeinfo for _ckBufferSet in libchilkatIos.a(DataBuffer.o)
typeinfo for DateParser in libchilkatIos.a(DateParser.o)
typeinfo for _clsBgTask in libchilkatIos.a(ClsBgTask.o)
typeinfo for _clsEncode in libchilkatIos.a(ClsEncode.o)
typeinfo for ChilkatRand in libchilkatIos.a(ChilkatRand.o)
typeinfo for UnicodeInfo in libchilkatIos.a(UnicodeInfo.o)
...


I using now with this UNIQUE library, don´t have any dependencies, just only one ".a"
http://www.chilkatsoft.com/downloads_ios.asp


In my xcode sample project works perfecly, but when try to compile with B4i no, below is my source code, including this library, my test project and my super simple library for B4i, can you check if forgot some to put in B4i?

http://www.visualnet.inf.br/UploadTemp/testeChilkat.zip

and my B4i sample to use my xcode library

http://www.visualnet.inf.br/UploadTemp/B4iExample.zip



HELP PLEASEE!!!!

I can´t believe in XCODE works and B4i no!


I don´t want develop in XCODE my next app, I´m waiting only for this solution to begin through B4i.




 
Last edited:

JanPRO

Well-Known Member
Licensed User
Maybe it's because of the linker flags, which can not be set in B4i, try to remove the linker flags in your Xcode project an run your app, then check if the error is the same like in B4i ;)
 
Last edited:

JanPRO

Well-Known Member
Licensed User

Alberto Iglesias

Well-Known Member
Licensed User
Erel,

Now it´s working, compiling with no errors.

The only command it´s not working now (and is the same anothers libraries) is for fire the events, look:


-(void)Initialize:(B4I*)bi :(NSString *)EventName {

[B4IObjectWrapper setBIAndEventName:self :bi :EventName];

[B4IObjectWrapper raiseEvent:self :mad:"_oninitialized:" :mad:[]];


// **** I TRIED TOO
[B4IObjectWrapper raiseEventFromDifferentThread:self : @"_oninitialized:" :mad:[]];
[B4IObjectWrapper raiseEventFromDifferentThread:self : @"_oninitialized:" :nil;

[B4IObjectWrapper raiseEvent:self :mad:"_oninitialized:" :nil;

}


and the B4i:

objPOP.Initialize("objPOP")

Sub objPOP_oninitialized <==== NOT FIRE!!!
LogColor("[objPOP_onInitialized]",Colors.Blue)
End Sub


Why????

 

Alberto Iglesias

Well-Known Member
Licensed User
in XCODE

B4X:
-(void)Initialize:(B4I*)bi :(NSString *)EventName {
    [B4IObjectWrapper setBIAndEventName:self :bi :EventName];
    [B4IObjectWrapper raiseEvent:self :@"_oninitialized:" :@[]];
  //[B4IObjectWrapper raiseEventFromDifferentThread:self :@"_oninitialized:" :nil];
}

in B4i
B4X:
Private Sub Application_Start (Nav As NavigationController)
    NavControl = Nav
    Page1.Initialize("Page1")
    Page1.Title = "Page 1"
    Page1.RootPanel.Color = Colors.White
    NavControl.ShowPage(Page1)
   
    objPOP.Initialize("objPOP")
End Sub
Sub objPOP_oninitialized
    LogColor("[objPOP_onInitialized]",Colors.Blue)
End Sub

and in Process_Global
Dim objPOP As POP3


And
 

Alberto Iglesias

Well-Known Member
Licensed User
Perfectly Erel, now it´s almost done....


For create a event to receive a "List" I think is NSArray * wrapper to send no?

Why when I send a NSArray not work? Look my sample:

//~event:DownloadCompleted(Messages as List)

in Xcode

B4X:
NSArray *retArray;
[B4IObjectWrapper raiseEvent:self :@"_downloadcompleted" :@[retArray]];

and in B4i

B4X:
Sub objPOP_DownloadCompleted(Messages As List)
    LogColor("[objPOP_DownloadCompleted]" & Messages,Colors.Blue)
End Sub

When I change to String or Int, works very well, but with NSArray no.

Whats the problem? Where I doing wrong?
 
Top