amirrajan / rubymotion-applied

RubyMotion documentation provided by the community. Submit a pull request to the docs for a free one year indie subscription.
Apache License 2.0
49 stars 10 forks source link

Reference RubyMotion library in iOS (swift/objc) proj? #51

Open amirrajan opened 6 years ago

amirrajan commented 6 years ago

This is possible. Just need to write a tutorial for it.

hboon commented 6 years ago

For such issues, do you want the community to write somewhere else and then provide a link or is there somewhere official?

amirrajan commented 6 years ago

If it's written here, it'll make it on to rubymotion.com/rubymotion-applied (when I get that wired up).

hboon commented 6 years ago

For Obj-C:

  1. $ motion create rm-static-lib

  2. Add foo.rb to your RubyMotion project:

class Foo
  def foo
    NSLog "This is the #foo method written in RubyMotion"
  end
end

class NSString
  def bar
    NSLog "This is the #bar added to NSString extension method, written in RubyMotion"
  end
end
  1. $ rake static

  2. Copy rm-static-lib-universal.a into Xcode project directory

  3. Add (drag) rm-static-lib-universal.a into Xcode project

  4. Click on project in Xcode, Target, Build Phases, under Link Binary With Libraries, add:

  1. Rename your AppDelegate class, to eg. RMAppDelegate.

  2. Change main() in main.m to:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        void RubyMotionInit(int, char **);
        RubyMotionInit(argc, argv);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([RMAppDelegate class]));
    }
}
  1. Create and add a header file RM-Lib.h to your Xcode project:

Fill it with:

#import <UIKit/UIKit.h>

@interface Foo : NSObject

- (void)foo;

@end

@interface NSString()

- (void)bar;

@end
  1. In the appropriate .m file (e.g RMAppDelegate.m),

Add:

#import "RM-Lib.h"

Add:

Foo* f = [Foo new];
[f foo];
[@"abc" bar];
  1. This should appear in the console:
This is the #foo method written in RubyMotion
This is the #bar added to NSString extension method, written in RubyMotion
katsuyoshi commented 5 years ago

I am just trying it.

When I execute rake static, I got an error.

TypeError: no implicit conversion of nil into String
/Users/ISD/.rubymotion/rubymotion-templates/motion/project/template/ios.rb:345:in `+'
/Users/ISD/.rubymotion/rubymotion-templates/motion/project/template/ios.rb:345:in `block (2 levels) in <top (required)>'
/Users/ISD/.rubymotion/rubymotion-templates/motion/project/template/ios.rb:344:in `map'
/Users/ISD/.rubymotion/rubymotion-templates/motion/project/template/ios.rb:344:in `block in <top (required)>'

I found, If I put motion-cocoapods to Gemfile, rake static is failed.

I make a new project. Put gem 'motion-cocoapods' to Gemfile. Then reproduce the error.

amirrajan commented 5 years ago

do you have a sample? Was there a specific template you used?

katsuyoshi commented 5 years ago

I put a sample here.

https://github.com/katsuyoshi/rm-static-lib-error-with-cocoapod

Although I didn't specify any template, it uses ios template.

amirrajan commented 5 years ago

I'll take a look Monday 👍

katsuyoshi commented 5 years ago

I tried @hboon 's steps with Xcode 10.2 (10E125) RM version is 6.1

1. libstdc++.tbd is missing. I used libc++.tbd instead of it.

2. There is an error with building for Simulator.

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_Foo", referenced from:
      objc-class-ref in RMAppDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Although it seems to include x86_64 by lipo command, Foo class is missing.

$ xcrun lipo -info build/rm-static-lib-universal.a 
Architectures in the fat file: build/rm-static-lib-universal.a are: i386 armv7 armv7s x86_64 arm64 

3. There is an error with building for iOS device.

ld: '/Users/ISD/tmp/RubyMotion/rm-static-lib/build/rm-static-lib-universal.a(init.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

It seems to be required to build with bitcode.

amirrajan commented 5 years ago

init.mm from DerivedData:

#import <Foundation/Foundation.h>

extern "C" {
    void ruby_init(void);
    void ruby_init_loadpath(void);
    void ruby_script(const char *);
    void *rb_vm_top_self(void);
    void rb_define_global_const(const char *, void *);
    void rb_rb2oc_exc_handler(void);
    void ruby_init_device_repl(void);
void MREP_00DC0AF073824F279AC3FE11C723793D(void *, void *);
int rm_repl_port = 50751;
}

extern "C"
void
RubyMotionInit(int argc, char **argv)
{
    static bool initialized = false;
    if (!initialized) {
        ruby_init();
        ruby_init_loadpath();
        if (argc > 0) {
            const char *progname = argv[0];
            ruby_script(progname);
        }
#if !__LP64__
        try {
#endif
            void *self = rb_vm_top_self();
ruby_init_device_repl();
rb_define_global_const("RUBYMOTION_ENV", @"development");
rb_define_global_const("RUBYMOTION_VERSION", @"6.1");
MREP_00DC0AF073824F279AC3FE11C723793D(self, 0);
#if !__LP64__
        }
        catch (...) {
            rb_rb2oc_exc_handler();
        }
#endif
        initialized = true;
    }
}

main.mm from DerivedData:

#import <UIKit/UIKit.h>

extern "C" {
    void rb_exit(int);
    void RubyMotionInit(int argc, char **argv);
}
int
main(int argc, char **argv)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int retval = 0;
    setenv("VM_OPT_LEVEL", "0", true);
    RubyMotionInit(argc, argv);
    retval = UIApplicationMain(argc, argv, nil, @"AppDelegate");
    rb_exit(retval);
    [pool release];
    return retval;
}
amirrajan commented 5 years ago

You can compile a single .rb file like this:

/usr/bin/env RM_DATADIR_PATH="/Users/amiralirajan/RubyMotionSrc/RubyMotion/data/ios/12.2" \
             VM_PLATFORM="iPhoneOS" \
             VM_KERNEL_PATH="/Users/amiralirajan/RubyMotionSrc/RubyMotion/data/ios/12.2/iPhoneOS/kernel-arm64.bc" \
             VM_OPT_LEVEL="0" /usr/bin/arch -arch x86_64 \
             "/Users/amiralirajan/RubyMotionSrc/RubyMotion/bin/ruby" --emit-llvm ./main.rb.arm64.s MREP_00DC0AF073824F279AC3FE11C723793D ./main.rb

And then use clang:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang  -miphoneos-version-min=12.2 -fexceptions -c -arch arm64 "./build/iPhoneOS-12.2-Development/objs/Users/amiralirajan/RubyMotionSrc/RubyMotion/BridgeSupportApp/app/app_delegate.rb.arm64.s" -o "./build/iPhoneOS-12.2-Development/objs/Users/amiralirajan/RubyMotionSrc/RubyMotion/BridgeSupportApp/app/app_delegate.rb.arm64.o"
/usr/bin/lipo -create "./build/iPhoneOS-12.2-Development/objs/Users/amiralirajan/RubyMotionSrc/RubyMotion/BridgeSupportApp/app/app_delegate.rb.arm64.o" -output "./build/iPhoneOS-12.2-Development/objs/Users/amiralirajan/RubyMotionSrc/RubyMotion/BridgeSupportApp/app/app_delegate.rb.o"
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ "./init.mm" -arch arm64 -isysroot "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk" -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks -miphoneos-version-min=12.2 -O0 -fexceptions -fblocks -fmodules -g -fobjc-legacy-dispatch -fobjc-abi-version=2 -c -o "./build/iPhoneOS-12.2-Development/objs/init.o"
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ "./main.mm" -arch arm64 -isysroot "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk" -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks -miphoneos-version-min=12.2 -O0 -fexceptions -fblocks -fmodules -g -fobjc-legacy-dispatch -fobjc-abi-version=2 -c -o "./build/iPhoneOS-12.2-Development/objs/main.o"
katsuyoshi commented 5 years ago

Thank you @amirrajan I tried to follow the description you wrote. But still, it shows undefined symbol:_OBJCCLASS$_Foo

I want to resolve #118.

Then I thought whether to make a static library and build by Xcode.

If #118 is fixed, I don't care about this issue.

amirrajan commented 5 years ago

I’ll have updates about 118 on Tuesday hopefully