iOS Tutorial [Objective C] Writing libraries for B4i

Discussion in 'iOS Tutorials' started by Erel, Nov 17, 2014.

Similar threads

B4i Library iAdMob
B4i Tutorial Swift libraries
B4i Tutorial AdMob Ads Mediation (with InMobi)
B4i Tutorial iAdMob and User Consent
Other B4i v3.00 has been released
  1. Erel

    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:
    Code:
    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:
    Code:
    //~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:
    Code:
    - (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:
    Code:
    +(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:
    Code:
    [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:
    Code:
    [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
     

    Attached Files:

    Last edited: Oct 19, 2017
    MarcoRome, icefairy333 and stevel05 like this.
  2. imbault

    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 :

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

    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.
     
    Derek Johnson and ilan like this.
  4. imbault

    imbault Well-Known Member Licensed User

    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: Dec 1, 2014
  5. Erel

    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.
     
  6. Erel

    Erel Administrator Staff 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.
     
    Ramezanpour likes this.
  7. Ramezanpour

    Ramezanpour Active Member Licensed User

    can you create library and public it ?
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    Post a feature request. I will consider it.
     
    Ramezanpour likes this.
  9. Alberto Iglesias

    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
     
  10. Erel

    Erel Administrator Staff Member Licensed User

    1. Do you want to get the value returned from the B4i sub?
    It should be:
    NSString* res = [B4IObjectWrapper raiseEvent...]

    2. Hard to say without debugging your library.
     
  11. 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?
     
  12. Erel

    Erel Administrator Staff Member Licensed User

  13. Alberto Iglesias

    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!!!
     
  14. Alberto Iglesias

    Alberto Iglesias Well-Known Member Licensed User

    But the problem to send a STRING to B4i, I can´t....

    How to send to B4i a string parameter? like this NOT WORKS!

    [B4IObjectWrapperraiseEventFromDifferentThread:self : @"_myevent:" : @[@((NSString *)pString)]];
     
  15. Erel

    Erel Administrator Staff Member Licensed User

    When you say it doesn't work, you need to explain what happens. Do you see any error?

    Your objective c code is wrong. It should be: @[pString]
     
  16. Alberto Iglesias

    Alberto Iglesias Well-Known Member Licensed User

  17. Alberto Iglesias

    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.
     
  18. Alberto Iglesias

    Alberto Iglesias Well-Known Member Licensed User

    How to pass a array to objective

    for receive like that:

    :(NSArray *)pArr



    Tks!
     
  19. Erel

    Erel Administrator Staff Member Licensed User

    1. Page is a wrapper for UIViewControlller. Your method should expect a UIViewController* and then pass Page from B4i.

    2. List is a wrapper for NSArray*.
     
  20. Alberto Iglesias

    Alberto Iglesias Well-Known Member Licensed User

    What´s a wrapper for UIVIEW* ? I can use a PANEL from B4i?
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice