UIImageWriteToSavedPhotosAlbum unable to callback to Lua

Hi there,

I was wondering if anyone knows how I could send a message back to Lua in the completionSelector after calling UIImageWriteToSavedPhotosAlbum?

Unfortunately the code below does not work, a bad memory access error is thrown when the success block is called in MyHelper imageSavedToCameraRoll function.

The problem occurs when the code tries to access L in the block, I assume that L has been cleaned up and thrown away.

Any advice would be greatly appreciated.

PluginLibrary code (.mm):

int PluginLibrary::moveImageToCameraRoll( lua\_State \*L ) {     // my singleton helper class which provides helpful functions     MyHelper \* helper = [MyHelper getSingleton];     // get the corona documents path     NSString \* filePath = [helper getCoronaDocumentsPath];     // append the filename of the file provided by the calling lua function     filePath = [filePath stringByAppendingPathComponent:[ NSString stringWithUTF8String:lua\_tostring( L, -1 )] ];     Self \* library = ToLibrary( L );     // completion block to be called if the image is saved correctly     void (^moveFileCompletionBlock)(void) = ^{             NSString \* finishedMessage = @"Event:FinishedMovingFile";             CoronaLuaNewEvent( L, kEvent );             lua\_pushstring( L, [finishedMessage UTF8String] );             lua\_setfield( L, -2, "message" );             CoronaLuaDispatchEvent( L, library-\>GetListener(), 0 );     };     // completion block to be called if the image is NOT saved correctly     void (^moveFileErrorBlock)(void) = ^{             NSString \* finishedMessage = @"Event:ErrorMovingFile";             CoronaLuaNewEvent( L, kEvent );             lua\_pushstring( L, [finishedMessage UTF8String] );             lua\_setfield( L, -2, "message" );             CoronaLuaDispatchEvent( L, library-\>GetListener(), 0 );     };          // store the completion blocks in array     NSArray \* blocks = [[ NSArray alloc] initWithObjects:moveFileCompletionBlock, moveFileErrorBlock, nil];     // load image     UIImage \* image = [UIImage imageNamed:filePath];     // pass the blocks to the helper object     UIImageWriteToSavedPhotosAlbum(image, helper, @selector(imageSavedToCameraRoll: didFinishSavingWithError: contextInfo:), blocks); }

MyHelper code (.mm):

#import "MyHelper.h" @implementation MyHelper { } // ... init code omitted ...    - (NSString \*)getCoronaDocumentsPath {     NSArray \* paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES );     NSString \* rootPath = paths[0];     NSString \* coronaDocumentsPath = [rootPath stringByDeletingLastPathComponent];     coronaDocumentsPath = [coronaDocumentsPath stringByAppendingPathComponent:@"Documents"];     return coronaDocumentsPath; } - (void)imageSavedToCameraRoll:(UIImage \*)image didFinishSavingWithError:(NSError \*)error contextInfo:(NSArray \*)contextInfo {     NSString \*message;     NSString \*title;          if (!error) { // success      // grab the success completion block from the array      void (^ successBlock)() = [contextInfo objectAtIndex:0];      successBlock();     } else { // error      // grab the error completion block from the array         void (^ errorBlock)() = [contextInfo objectAtIndex:1];         errorBlock();     } } @end

I can’t tell exactly but it looks like you built your code from the Corona Plugin project template. If so, you have a C++ namespace issue:

kEvent is within the class PluginLibrary.

CoronaLuaNewEvent( L, kEvent ); should be CoronaLuaNewEvent( L, PluginLibrary::kEvent ); 

If that is not the problem try dispatching a test Corona Lua event outside your Block and see if that works.

I just did a test dispatching a Lua event within an Objective C Block and namespace is not the issue, so disregard my previous speculation on namespace.

Ah dam! Thanks for looking, I was really hoping that to be the answer!

Ok, I did some more work on it and thought I had solved it, but unfortunately I haven’t.

Essentially the idea was to get hold of the CoronaRuntime in the “Open” method of the PluginLibrary, then store this for later use.

In the following example code I use a singleton to store the CoronaRuntime, and use the singleton in the blocks to grab the runtime.

But unfortunately this does not work either. I have provided screenshots of the line and error. They are EXC_BAD_ACCESS and SIGABRT.

Example 1:

screenshot-1.png?raw=1

Example 2:

screenshot-2.png?raw=1

It also throws errors in the Corona Runtime such as “tired to concatenate string” when I get the message back from Objective-C and manipulate it.

It’s also almost like the event is corrupting other parts of the Lua Stack. Is that possible?

Here is the code:

MyHelper.h

#import \<Foundation/Foundation.h\> #include "CoronaRuntime.h" #import \<UIKit/UIKit.h\> @interface MyHelper : NSObject { } @property ( nonatomic, retain ) id\<CoronaRuntime\> coronaRuntime; + (id)getSingleton; - (NSString \*)getCoronaDocumentsPath; - (void)imageSavedToCameraRoll:(UIImage \*)image didFinishSavingWithError:(NSError \*)error contextInfo:(NSArray \*)contextInfo; @end

MyHelper.mm

#import "MyHelper.h" #import "CoronaLua.h" #import "CoronaRuntime.h" @implementation MyHelper { } @synthesize coronaRuntime; + (id)getSingleton { &nbsp;&nbsp;&nbsp; static MyHelper \* sharedInstance = nil; &nbsp;&nbsp;&nbsp; static dispatch\_once\_t onceToken; &nbsp;&nbsp;&nbsp; dispatch\_once( &onceToken, ^{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sharedInstance = [[self alloc] init ]; &nbsp;&nbsp;&nbsp; }); &nbsp;&nbsp;&nbsp; return sharedInstance; } - (id)init { &nbsp;&nbsp;&nbsp; if ( self = [super init] ) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // init variables here &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; return self; } - (NSString \*)getCoronaDocumentsPath { &nbsp;&nbsp;&nbsp; NSArray \* paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES ); &nbsp;&nbsp;&nbsp; NSString \* rootPath = paths[0]; &nbsp;&nbsp;&nbsp; NSString \* coronaDocumentsPath = [rootPath stringByDeletingLastPathComponent]; &nbsp;&nbsp;&nbsp; coronaDocumentsPath = [coronaDocumentsPath stringByAppendingPathComponent:@"Documents"]; &nbsp;&nbsp;&nbsp; return coronaDocumentsPath; } - (void)imageSavedToCameraRoll:(UIImage \*)image didFinishSavingWithError:(NSError \*)error contextInfo:(NSArray \*)contextInfo { &nbsp;&nbsp;&nbsp; NSString \*message; &nbsp;&nbsp;&nbsp; NSString \*title; &nbsp;&nbsp;&nbsp; if (!error) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // success &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // grab the success completion block from the array &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (^ successBlock)() = [contextInfo objectAtIndex:0]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; successBlock(); &nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // error &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // grab the error completion block from the array &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (^ errorBlock)() = [contextInfo objectAtIndex:1]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errorBlock(); &nbsp;&nbsp;&nbsp; } } @end

PluginLibrary.h

#ifndef \_PluginLibrary\_H\_\_ #define \_PluginLibrary\_H\_\_ #include "CoronaLua.h" #include "CoronaMacros.h" // This corresponds to the name of the library, e.g. [Lua] require "plugin.library" // where the '.' is replaced with '\_' CORONA\_EXPORT int luaopen\_plugin\_testplugin( lua\_State \*L ); #endif // \_PluginLibrary\_H\_\_

PluginLibrary.mm

#import "PluginLibrary.h" #include "CoronaRuntime.h" #import \<UIKit/UIKit.h\> #include "MyHelper.h" // ---------------------------------------------------------------------------- class PluginLibrary { &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef PluginLibrary Self; &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static const char kName[]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static const char kEvent[]; &nbsp;&nbsp;&nbsp; protected: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PluginLibrary(); &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool Initialize( CoronaLuaRef listener ); &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaRef GetListener() const { return fListener; } &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int Open( lua\_State \*L ); &nbsp;&nbsp;&nbsp; protected: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int Finalizer( lua\_State \*L ); &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static Self \*ToLibrary( lua\_State \*L ); &nbsp;&nbsp;&nbsp; public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int init( lua\_State \*L ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int moveImageToCameraRoll( lua\_State \*L ); &nbsp;&nbsp;&nbsp; private: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaRef fListener; }; // ---------------------------------------------------------------------------- // This corresponds to the name of the library, e.g. [Lua] require "plugin.library" const char PluginLibrary::kName[] = "plugin.testplugin"; // This corresponds to the event name, e.g. [Lua] event.name const char PluginLibrary::kEvent[] = "TestPlugin"; PluginLibrary::PluginLibrary() :&nbsp;&nbsp; fListener( NULL ) { } bool PluginLibrary::Initialize( CoronaLuaRef listener ) { &nbsp;&nbsp;&nbsp; // Can only initialize listener once &nbsp;&nbsp;&nbsp; bool result = ( NULL == fListener ); &nbsp;&nbsp;&nbsp; if ( result ) &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fListener = listener; &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; return result; } int PluginLibrary::Open( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; // =================================== &nbsp;&nbsp;&nbsp; // ! Important code ! &nbsp;&nbsp;&nbsp; // get shared instance of the singleton &nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp; // get the corona runtime &nbsp;&nbsp;&nbsp; void \* platformContext = CoronaLuaGetContext( L ); &nbsp;&nbsp;&nbsp; // now store the corona runtime in the singleton &nbsp;&nbsp;&nbsp; [helper setCoronaRuntime:(id\<CoronaRuntime\>)platformContext]; &nbsp;&nbsp;&nbsp; // =================================== &nbsp;&nbsp;&nbsp; // Register \_\_gc callback &nbsp;&nbsp;&nbsp; const char kMetatableName[] = \_\_FILE\_\_; // Globally unique string to prevent collision &nbsp;&nbsp;&nbsp; CoronaLuaInitializeGCMetatable( L, kMetatableName, Finalizer ); &nbsp;&nbsp;&nbsp; // Functions in library &nbsp;&nbsp;&nbsp; const luaL\_Reg kVTable[] = &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { "init", init }, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { "moveImageToCameraRoll", moveImageToCameraRoll }, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { NULL, NULL } &nbsp;&nbsp;&nbsp; }; &nbsp;&nbsp;&nbsp; // Set library as upvalue for each library function &nbsp;&nbsp;&nbsp; Self \*library = new Self; &nbsp;&nbsp;&nbsp; CoronaLuaPushUserdata( L, library, kMetatableName ); &nbsp;&nbsp;&nbsp; luaL\_openlib( L, kName, kVTable, 1 ); // leave "library" on top of stack &nbsp;&nbsp;&nbsp; return 1; } int PluginLibrary::Finalizer( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; Self \*library = (Self \*)CoronaLuaToUserdata( L, 1 ); &nbsp;&nbsp;&nbsp; CoronaLuaDeleteRef( L, library-\>GetListener() ); &nbsp;&nbsp;&nbsp; delete library; &nbsp;&nbsp;&nbsp; return 0; } PluginLibrary \* PluginLibrary::ToLibrary( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; // library is pushed as part of the closure &nbsp;&nbsp;&nbsp; Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) ); &nbsp;&nbsp;&nbsp; return library; } // [Lua] library.init( listener ) int PluginLibrary::init( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; int listenerIndex = 1; &nbsp;&nbsp;&nbsp; if ( CoronaLuaIsListener( L, listenerIndex, kEvent ) ) &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Self \*library = ToLibrary( L ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaRef listener = CoronaLuaNewRef( L, listenerIndex ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; library-\>Initialize( listener ); &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; return 0; } int PluginLibrary::moveImageToCameraRoll( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; // my singleton helper instance &nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp; // get the corona documents path &nbsp;&nbsp;&nbsp; NSString \* filePath = [helper getCoronaDocumentsPath]; &nbsp;&nbsp;&nbsp; // append the filename of the file provided by the calling lua function &nbsp;&nbsp;&nbsp; filePath = [filePath stringByAppendingPathComponent:[ NSString stringWithUTF8String:lua\_tostring( L, -1 )] ]; &nbsp;&nbsp;&nbsp; Self \* library = ToLibrary( L ); &nbsp;&nbsp;&nbsp; // completion block to be called if the image is saved correctly &nbsp;&nbsp;&nbsp; void (^moveFileCompletionBlock)(void) = ^{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // notice we are not using the param L, we are &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // creating our own reference to the lua\_State &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // from our CoronaRuntime stored in the singleton &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id\<CoronaRuntime\> runtime = [helper coronaRuntime]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_State \* state = runtime.L; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSString \* finishedMessage = @"Event:FinishedMovingFile"; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaNewEvent( state, kEvent ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_pushstring( state, [finishedMessage UTF8String] ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_setfield( state, -2, "message" ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaDispatchEvent( state, library-\>GetListener(), 0 ); &nbsp;&nbsp;&nbsp; }; &nbsp;&nbsp;&nbsp; // completion block to be called if the image is NOT saved correctly &nbsp;&nbsp;&nbsp; void (^moveFileErrorBlock)(void) = ^{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id\<CoronaRuntime\> runtime = [helper coronaRuntime]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_State \* state = runtime.L; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSString \* finishedMessage = @"Event:ErrorMovingFile"; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaNewEvent( state, kEvent ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_pushstring( state, [finishedMessage UTF8String] ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_setfield( state, -2, "message" ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaDispatchEvent( state, library-\>GetListener(), 0 ); &nbsp;&nbsp;&nbsp; }; &nbsp;&nbsp;&nbsp; // store the completion blocks in array &nbsp;&nbsp;&nbsp; NSArray \* blocks = [[ NSArray alloc] initWithObjects:moveFileCompletionBlock, moveFileErrorBlock, nil]; &nbsp;&nbsp;&nbsp; // load image &nbsp;&nbsp;&nbsp; UIImage \* image = [UIImage imageNamed:filePath]; &nbsp;&nbsp;&nbsp; // save image to camera roll, pass the blocks to the helper object &nbsp;&nbsp;&nbsp; UIImageWriteToSavedPhotosAlbum(image, helper, @selector(imageSavedToCameraRoll: didFinishSavingWithError: contextInfo:), blocks); return 0; } // ---------------------------------------------------------------------------- CORONA\_EXPORT int luaopen\_plugin\_testplugin( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; return PluginLibrary::Open( L ); }

The errors are not consistent, they are intermittent. Sometimes it works fine, other times the lines:

CoronaLuaNewEvent( state, kEvent );

CoronaLuaDispatchEvent( state, library->GetListener(), 0 );

in the moveFileCompletionBlock cause the problems, sometimes the first, other times the second.

Are you sure the Lua state is valid at the time of executing the listener? I’d start by checking that.

That’s certainly a good idea. How would I check that the lua_State is valid?

Check that it isn’t NULL. That would suffice. 

Sorry to get your hopes up. I just did another test dispatching a Lua event within a Block and the only way I was able to get an EXC_BAD_ACCESS code 1 error was to pass NULL for the Lua state:

CoronaLuaDispatchEvent( NULL, listenerRef, 0 );

Surely that means that the error you are getting is caused by the Lua State variable you are using to be NULL at the time you call CoronaLuaDispatchEvent ?

That’s what I believe from your reply at least

Yes, you are correct. That was the only way I could reproduce mpayne002 error within my test.

Ok - just tried it:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( state == NULL ) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSLog( @"state is NULL" ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSLog( @"state is not NULL" ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaNewEvent( state, kEvent ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_pushstring( state, [finishedMessage UTF8String] ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_setfield( state, -2, "message" ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaDispatchEvent( state, library-\>GetListener(), 0 );

The state variable was not NULL.

What happened was I got a EXC_BAD_ACCESS on line:

CoronaLuaDispatchEvent( state, library->GetListener(), 0 );

However, Corona got the Event, was able to print() the “message” sent from Objective-C, then the EXC_BAD_ACCESS was triggered.

Right - pretty sure I have managed to fix it. I have tested it repeatedly and it doesn’t crash. I am using a different approach this time.

Rather than grabbing the CoronaRuntime from the “Open” function, I am grabbing the lua_State and CoronaLuaRef from the “Init” function. (Saw the AppLovin source code and noticed that’s what they do).

The code is reworked and can be found below.

To use the code:

• find any JPG and add it to the main bundle

• first you must call the pluginlibrary createTestImage from Corona

• then call the pluginlibrary moveImageToCameraRoll from Corona

You can download the basic project here:

https://www.dropbox.com/s/0quwejk7ugfx8hr/CoronaRuntimeTest.zip?dl=0

MyHelper.h

#import \<Foundation/Foundation.h\> #include "CoronaRuntime.h" #import \<UIKit/UIKit.h\> #include "CoronaLua.h" @interface MyHelper : NSObject { &nbsp;&nbsp; &nbsp; } @property ( nonatomic ) lua\_State \* luaState; @property ( nonatomic ) CoronaLuaRef luaRef; + (id)getSingleton; - (NSString \*)getCoronaDocumentsPath; - (void)imageSavedToCameraRoll:(UIImage \*)image didFinishSavingWithError:(NSError \*)error contextInfo:(void (^)(NSString \*))contextInfo; - (void)createTestImage; @end

MyHelper.mm

#import "MyHelper.h" #import \<UIKit/UIKit.h\> #import "CoronaLua.h" #import "CoronaRuntime.h" @implementation MyHelper { &nbsp;&nbsp; &nbsp; } + (id)getSingleton { &nbsp;&nbsp;&nbsp; static MyHelper \* sharedInstance = nil; &nbsp;&nbsp;&nbsp; static dispatch\_once\_t onceToken; &nbsp;&nbsp;&nbsp; dispatch\_once( &onceToken, ^{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sharedInstance = [[self alloc] init ]; &nbsp;&nbsp;&nbsp; }); &nbsp;&nbsp;&nbsp; return sharedInstance; } - (id)init { &nbsp;&nbsp;&nbsp; if ( self = [super init] ) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // init variables here &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; return self; } - (NSString \*)getCoronaDocumentsPath { &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; NSArray \* paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES ); &nbsp;&nbsp;&nbsp; NSString \* rootPath = paths[0]; &nbsp;&nbsp;&nbsp; NSString \* coronaDocumentsPath = [rootPath stringByDeletingLastPathComponent]; &nbsp;&nbsp;&nbsp; coronaDocumentsPath = [coronaDocumentsPath stringByAppendingPathComponent:@"Documents"]; &nbsp;&nbsp;&nbsp; return coronaDocumentsPath; &nbsp;&nbsp; &nbsp; } - (void)imageSavedToCameraRoll:(UIImage \*)image didFinishSavingWithError:(NSError \*)error contextInfo:(void (^)(NSString \*))contextInfo { &nbsp;&nbsp;&nbsp; if (!error) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // success &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; contextInfo( @"Event: Success" ); &nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // error &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; contextInfo( @"Event: Failure" ); &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp; &nbsp; } - (void)createTestImage { &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; // load image from project &nbsp;&nbsp;&nbsp; UIImage \* image = [UIImage imageNamed:[ [ NSBundle mainBundle] pathForResource:@"image" ofType:@"jpg" ] ]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; NSString \* path = [self getCoronaDocumentsPath]; &nbsp;&nbsp;&nbsp; path = [path stringByAppendingPathComponent:@"image.jpg"]; &nbsp;&nbsp;&nbsp; NSError \* error; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; bool success = [UIImageJPEGRepresentation( image, .9f ) writeToFile:path options:NSDataWritingAtomic error:&error]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; if ( success == YES ) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSLog( @"Created Test Image." ); &nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSLog( @"Could not create Test Image." ); &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp; &nbsp; } @end

PluginLibrary.h

// //&nbsp; PluginLibrary.h //&nbsp; TemplateApp // //&nbsp; Copyright (c) 2012 \_\_MyCompanyName\_\_. All rights reserved. // #ifndef \_PluginLibrary\_H\_\_ #define \_PluginLibrary\_H\_\_ #include "CoronaLua.h" #include "CoronaMacros.h" // This corresponds to the name of the library, e.g. [Lua] require "plugin.library" // where the '.' is replaced with '\_' CORONA\_EXPORT int luaopen\_plugin\_library( lua\_State \*L ); #endif // \_PluginLibrary\_H\_\_

PluginLibrary.mm

// //&nbsp; PluginLibrary.mm //&nbsp; TemplateApp // //&nbsp; Copyright (c) 2012 \_\_MyCompanyName\_\_. All rights reserved. // #import "PluginLibrary.h" #include "CoronaRuntime.h" #import \<UIKit/UIKit.h\> #import "MyHelper.h" // ---------------------------------------------------------------------------- class PluginLibrary { &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef PluginLibrary Self; &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static const char kName[]; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static const char kEvent[]; &nbsp;&nbsp; &nbsp;protected: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;PluginLibrary(); &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;bool Initialize( CoronaLuaRef listener ); &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;CoronaLuaRef GetListener() const { return fListener; } &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static int Open( lua\_State \*L ); &nbsp;&nbsp; &nbsp;protected: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static int Finalizer( lua\_State \*L ); &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static Self \*ToLibrary( lua\_State \*L ); &nbsp;&nbsp; &nbsp;public: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static int init( lua\_State \*L ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int moveImageToCameraRoll( lua\_State \*L ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int printDocumentsDirectory( lua\_State \*L ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static int createTestImage( lua\_State \*L ); &nbsp;&nbsp; &nbsp;private: &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;CoronaLuaRef fListener; }; // ---------------------------------------------------------------------------- // This corresponds to the name of the library, e.g. [Lua] require "plugin.library" const char PluginLibrary::kName[] = "plugin.library"; // This corresponds to the event name, e.g. [Lua] event.name const char PluginLibrary::kEvent[] = "pluginlibraryevent"; PluginLibrary::PluginLibrary() :&nbsp;&nbsp; &nbsp;fListener( NULL ) { } bool PluginLibrary::Initialize( CoronaLuaRef listener ) { &nbsp;&nbsp; &nbsp;// Can only initialize listener once &nbsp;&nbsp; &nbsp;bool result = ( NULL == fListener ); &nbsp;&nbsp; &nbsp;if ( result ) &nbsp;&nbsp; &nbsp;{ &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fListener = listener; &nbsp;&nbsp; &nbsp;} &nbsp;&nbsp; &nbsp;return result; } int PluginLibrary::Open( lua\_State \*L ) { &nbsp;&nbsp; &nbsp;// Register \_\_gc callback &nbsp;&nbsp; &nbsp;const char kMetatableName[] = \_\_FILE\_\_; // Globally unique string to prevent collision &nbsp;&nbsp; &nbsp;CoronaLuaInitializeGCMetatable( L, kMetatableName, Finalizer ); &nbsp;&nbsp; &nbsp;// Functions in library &nbsp;&nbsp; &nbsp;const luaL\_Reg kVTable[] = &nbsp;&nbsp; &nbsp;{ &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{ "init", init }, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { "moveImageToCameraRoll", moveImageToCameraRoll }, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { "printDocumentsDirectory", printDocumentsDirectory }, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { "createTestImage", createTestImage }, &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{ NULL, NULL } &nbsp;&nbsp; &nbsp;}; &nbsp;&nbsp; &nbsp;// Set library as upvalue for each library function &nbsp;&nbsp; &nbsp;Self \*library = new Self; &nbsp;&nbsp; &nbsp;CoronaLuaPushUserdata( L, library, kMetatableName ); &nbsp;&nbsp; &nbsp;luaL\_openlib( L, kName, kVTable, 1 ); // leave "library" on top of stack &nbsp;&nbsp; &nbsp;return 1; } int PluginLibrary::Finalizer( lua\_State \*L ) { &nbsp;&nbsp; &nbsp;Self \*library = (Self \*)CoronaLuaToUserdata( L, 1 ); &nbsp;&nbsp; &nbsp;CoronaLuaDeleteRef( L, library-\>GetListener() ); &nbsp;&nbsp; &nbsp;delete library; &nbsp;&nbsp; &nbsp;return 0; } PluginLibrary \* PluginLibrary::ToLibrary( lua\_State \*L ) { &nbsp;&nbsp; &nbsp;// library is pushed as part of the closure &nbsp;&nbsp; &nbsp;Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) ); &nbsp;&nbsp; &nbsp;return library; } // [Lua] library.init( listener ) int PluginLibrary::init( lua\_State \*L ) { &nbsp;&nbsp; &nbsp;int listenerIndex = 1; &nbsp;&nbsp; &nbsp;if ( CoronaLuaIsListener( L, listenerIndex, kEvent ) ) &nbsp;&nbsp; &nbsp;{ &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Self \*library = ToLibrary( L ); &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;CoronaLuaRef listener = CoronaLuaNewRef( L, listenerIndex ); &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;library-\>Initialize( listener ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ======================================== &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ! Important Code ! &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [helper setLuaState:L]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [helper setLuaRef:listener]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ======================================== &nbsp;&nbsp; &nbsp;} &nbsp;&nbsp; &nbsp;return 0; } int PluginLibrary::createTestImage( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp; [helper createTestImage]; &nbsp;&nbsp;&nbsp; return 0; } int PluginLibrary::moveImageToCameraRoll( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; // my singleton helper instance &nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; // get the corona documents path &nbsp;&nbsp;&nbsp; NSString \* filePath = [helper getCoronaDocumentsPath]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; // append the filename of the file provided by the calling lua function &nbsp;&nbsp;&nbsp; filePath = [filePath stringByAppendingPathComponent:[ NSString stringWithUTF8String:lua\_tostring( L, -1 )] ]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; NSFileManager \*fileManager = [NSFileManager defaultManager]; &nbsp;&nbsp;&nbsp; if ( [fileManager fileExistsAtPath:filePath] ){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSLog( @"File Exists." ); &nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NSLog( @"File does not Exist." ); &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; void (^moveFileCompletionBlock)(NSString \*) = ^(NSString \* message){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_State \* state = [helper luaState]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaNewEvent( state, kEvent ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_pushstring( state, [message UTF8String] ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua\_setfield( state, -2, "message" ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaRef listener = [helper luaRef]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CoronaLuaDispatchEvent( state, listener, 0 ); &nbsp;&nbsp;&nbsp; }; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; // load image &nbsp;&nbsp;&nbsp; UIImage \* image = [UIImage imageNamed:filePath]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; // save image to camera roll, pass moveFileCompletionBlock to function &nbsp;&nbsp;&nbsp; UIImageWriteToSavedPhotosAlbum( image, helper, @selector(imageSavedToCameraRoll: didFinishSavingWithError: contextInfo:), moveFileCompletionBlock ); &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; return 0; } int PluginLibrary::printDocumentsDirectory( lua\_State \*L ) { &nbsp;&nbsp;&nbsp; MyHelper \* helper = [MyHelper getSingleton]; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; NSLog( @"%@", [helper getCoronaDocumentsPath] ); &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; return 0; } // ---------------------------------------------------------------------------- CORONA\_EXPORT int luaopen\_plugin\_library( lua\_State \*L ) { &nbsp;&nbsp; &nbsp;return PluginLibrary::Open( L ); }

 

Right, I should have recommended that you look at projects like that. I wrote the AppLovin plugin and several others. They serve as a good base to make your own plugins off.

Glad it was useful :slight_smile:

Oh wow that’s cool I didn’t realize that!

:slight_smile: It certainly was useful!

Great, glad you solved it. Existing plugins are pretty much the best (or only, depending on what you want to do) reference for doing your own. Hats off to InfuseDreams for posting plugin source!

I can’t tell exactly but it looks like you built your code from the Corona Plugin project template. If so, you have a C++ namespace issue:

kEvent is within the class PluginLibrary.

CoronaLuaNewEvent( L, kEvent ); should be CoronaLuaNewEvent( L, PluginLibrary::kEvent ); 

If that is not the problem try dispatching a test Corona Lua event outside your Block and see if that works.

I just did a test dispatching a Lua event within an Objective C Block and namespace is not the issue, so disregard my previous speculation on namespace.

Ah dam! Thanks for looking, I was really hoping that to be the answer!