How can logging to Crashlytics be done from C++ code on iOS? - crashlytics

The Crashlytics documentation for iOS only mentions custom logging from Swift and Objective-C. It mentions nothing about doing this from C++. On Android, we can use the log() and set() functions from the Crashlytics native header to do logging from C++ code called through the JNI. Is there an equivalent method for iOS? Is there any other way to do it?

Todd from Crashlytics here! Right now this is not possible without some sort of custom adapter on your end to go back to Swift or Objective-C. As C++ becomes more common in advanced apps I'd expect to see the team consider this :)

Yep, I don't think there is a C-function for that but I had the problem some time ago and I used a function pointer. So here it is.
In the C scope (.cpp and .h file)
void yourCFunction(void (*logFunc)(const char*) = NULL);
In the ObjectiveC scope:
CYourCClass *yourCClass = [...];
yourCClass->yourCFunction( [](const char* t){
CLS_LOG(#"%s", t);
});
To call the log function in C++:
if (logFunc!=NULL) {
std::string debugChr("Some message");
(*logFunc)(debugChr.c_str());
}
This can be improved using macro and stuff but you have the idea.

Related

Changing the implementation of Objective C method in Swift subclass

I am using an external library in my project. It is integrated via CocoaPods. I have access to the source code. The library is working well, but I need some modifications at some places in order for it to serve my purposes. It is written in Objective C. My project is in Swift.
I need to change the implementation of one method in the library. The problem is it is in the .m file and uses a lot of stuff declared only in the .m file. For example:
-(NSString*)methodToChange
{
NSArray<NSNumber*>* data = [self someInternalMethod:1];
uint value = data[0].unsignedIntValue;
return [self anotherInternalMethod:value];
}
I tried subclassing it like this:
class MySubclass : MySuperclassWithMethodToChange {
override var methodToChange: String {
//trying to use someInternalMethod and anotherInternalMethod
//unsuccessfully because they are not visible
}
}
The internal methods are using and modifying properties from the .m file that are also not accessible. Is there any way to solve this?
I would suggest forking the original library repository and making the necessary changes in your fork. You can then have your Podfile point to that. If your changes could be useful to others, make them in a way that doesn't break existing functionality and contribute them back to the library by opening a pull request.
If the original library gets updated later, you will have to merge those changes from the so-called "upstream" repository into yours. This does not happen automatically, so you'll have full control (and responsibility) over that process. See https://help.github.com/en/articles/syncing-a-fork for how this would look like.

Confusing Swift type annotations for React Native Promises

I'm playing around with React Native and attempting to write some native code that communicates over bluetooth. I'm confused by the type annotations that I need to use in order for it to work. Could someone please explain why I have to have the "resolver" and "rejecter" bits in the following two code snippets? Is there a way to write this without those unused parts?
My implementation, MyAsyncModule.swift:
#objc(MyAsyncModule)
class MyAsyncModule: NSObject {
#objc func echoAsync(
input: NSNumber,
resolver resolve: RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock
) -> Void {
resolve(input)
}
}
From my bridge file, MyAsyncModuleBridge.m
RCT_EXTERN_METHOD(echoAsync:
(nonnull NSNumber *)input
resolver:(RCTPromiseResolveBlock *)resolve
rejecter:(RCTPromiseRejectBlock *)reject
)
I am coming from scripting land so types are foreign to me, but it seems too weird that React Native refuses to identify my the echoAsync method unless both the implementation and the bridge include the resolver and rejecter bits...
The resolver and reject calls are needed to have the framework generate a "promise". A promise can be thought of as a placeholder for a value that will be made available in the future. The resolver is called when the native code is done doing its work and is ready to pass the results back to the JavaScript land. reject is used when the native side detects an error and is used to report that error from native to JavaScript land.
To get a bit deeper, when you're JavaScript calls a native function, it doesn't pause and wait for native to finish up like a normal function call. It instead just goes on executing the next line of code (notice how React-Native prevents you from setting a return value for your exported functions meaning they are explicitly making sure you don't try and wait for a return value).
So then how does native code ever report the results back to JavaScript? There are two options
callbacks (in native these have the type RCTResponseSenderBlock) when called, cause a JavaScript function to run with the passed arguments
promises (with the types RCTPromiseResolveBlock and RCTPromiseRejectBlock) which causes you success handler to run with the passed arguments when resolver is called or causes your error handler to run when reject is called.
As for async function you MUST use promises.
For more info on JavaScript promises checkout:
http://www.html5rocks.com/en/tutorials/es6/promises/
https://facebook.github.io/react-native/docs/native-modules-ios.html#promises

FMDatabaseAdditions methods not accessible in swift?

I am porting some existing code over to swift. I am using FMDB in swift and everything has been good so far, but I do not have access to FMDatabaseAdditions methods in my project in spite of having access to everything else (FMResultSet, FMDatabasePool and so on).
I have the bridging-header file with the #import "FMDB" statement and it has the includes for all the other FMDB files.
I have looked in my build phases and I can see FMDatabaseAdditions.m in the "Compile Sources" and I have moved it to the top of the list.
Still xCode doesn't recognise it or its methods.
Any ideas?
UPDATE: Head over to the link below (in bold) for an unofficial FMDatabaseAdditionsVariadic.swift file as provided by Robert. Also keep an eye out on the repo for when the fix is officially added in.
Thanks again Rob!
-- ORIGINAL MESSAGE --
I found out what is going on from the guys on github over at FMDB repo.. (see original post here):
I notice that some of the FMDatabaseAdditions methods work fine. For example:
if db.tableExists("test") {
println("test table exists")
} else {
println("test table doesn't exist")
}
It looks like Swift is having issues with the variadic methods within the category. This is a known problem with Swift, but while I created the variadic rendition of executeQuery and executeUpdate, I didn't do so for the FMDatabaseAdditions methods. I will take care of this.
Thanks for bringing it to our attention.
Thanks robertmryan for your help.

Debugging Objective C JNI code

Here is the situation:
I have a client's java project open in eclipse. It uses a JNI library created by an Xcode Objective C project. Is there any good way for me to debug the C code from eclipse when I execute the Java code? Obviously eclipse's default debugger cannot step into the jni library file and we lose the thread (thread meaning investigative thread here, not programming thread).
Any advice or input is appreciated as the code base is large enough that following the client's code will be radically faster than other options.
Thanks.
EDIT:
It should be noted that the reason that the jni library is written in Objective-C is because it is integrating with Mac OSX. It is using the Cocoa framework to integrate with the Apple speech api.
I am not sure that I have fully understood your setup and if you require this to be done from eclipse or not. Anyhow I was interested in doing a little test program using JNI and a Cocoa library doing nothing just to try the debugging of the obj-c/c code.
I succeeded to do this setup and also to debug the code. I use IntelliJ for Java and Xcode for the objc/c part but doing the java part in eclipse is a no-brainer.
So you should be able to set up exactly my project structure and get going with the debugging. And from there you should be able to apply this knowledge to your own more complex code.
This is how I started off:
Create a new project in Xcode by choosing Cocoa Library.
Name the project libnative and make it of Type Dynamic.
Choose a place for your new project. I use ~/Development/ and skip the Create local git... part.
This will create a new project called lib native.xcodeproj in your selected folder. Two files have been automatically created: libnative.h and libnative.m.
First you must change the Project Settings.
Executable Extension in the Packaging section must be changed from dynlib to jnilib.
Framework Search Paths in the Search Paths section must be updated to point to the JNI framework: /System/Library/Frameworks/JavaVM.framework/Frameworks/JavaNativeFoundation.framework/
Now its time to add some code. Be aware that with this setup you will have to use <JavaVM/jni.h>. Update the libnative.m to look like the following code:
//
// libnative.m
// libnative
//
// Created by maba on 2012-10-09.
// Copyright (c) 2012 maba. All rights reserved.
//
#import "libnative.h"
#include <JavaVM/jni.h>
#implementation libnative
#end
#ifdef __cplusplus
extern "C" {
#endif
#ifndef VEC_LEN
#define VEC_LEN(v) (sizeof(v)/sizeof(v[0]))
#endif/*VEC_LEN*/
static JavaVM *javaVM;
static void print();
static JNINativeMethod Main_methods[] =
{
{ "print", "()V", (void*)print },
};
static struct {
const char *class_name;
JNINativeMethod *methods;
int num_methods;
} native_methods[] = {
{ "com/stackoverflow/Main", Main_methods, VEC_LEN(Main_methods) },
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = 0;
jclass cls = 0;
jint rs = 0;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR;
}
javaVM = jvm;
for (unsigned int i = 0; i < VEC_LEN(native_methods); i++) {
cls = (*env)->FindClass(env, native_methods[i].class_name);
if (cls == NULL) {
return JNI_ERR;
}
rs = (*env)->RegisterNatives(env, cls, native_methods[i].methods, native_methods[i].num_methods);
assert(rs == JNI_OK);
}
return JNI_VERSION_1_4;
}
static void print(JNIEnv *env, jclass cls) {
printf("Hello from C");
}
#ifdef __cplusplus
}
#endif
Build the code by pressing ⌘+B.
And now it is time to create the Java code. I simply created a class called Main in package com.stackoverflow.
com.stackoverflow.Main.java
package com.stackoverflow;
/**
* #author maba, 2012-10-09
*/
public class Main {
static native void print();
static {
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("native");
}
public static void main(String[] args) {
System.out.println("Loading native");
Main.print();
}
}
Set a breakpoint on the line before Main.print();. Start the debugger with the following JVM option:
-Djava.library.path="/Users/maba/Library/Developer/Xcode/DerivedData/libnative-cquleqohzyhghnercyqdwpnznjdf/Build/Products/Debug/"
This line is a little long and also user specific. You will have to look for yourself what the directory names are but they will be more or less the same as mine except for the generated libnative-cquleqohzyhghnercyqdwpnznjdf path.
The program should be running and waiting at the breakpoint. Time to attach the Xcode debugger to the running application.
Choose menu Product -> Attach to Process > and point to the running java process in the System part of the drop down. If there are several java processes then it is most likely the one with the highest PID but not always. You'll have to try.
Create a breakpoint in the c code on the line printf("Hello from C");.
Go back to the Java IDE and continue the execution from where it was halting.
Go back to Xcode and see that it is waiting at the breakpoint!
As I stated earlier this is a very simple approach to the obj-c/JNI and your project is probably quite large but with this small test project you can at least see how it works and then continue to your own project setup.
You might be able to attach with gdb (or lldb) from the Terminal. If the launching of the process w/the native code is the result of a fork()/exec() -- i.e. if you can't type gdb /some/command/line -- then you can likely use the --waitfor option (see the man page) to wait for the launch of the inferior.
Loading symbols will be tricky.
This is a Mac OS X project using the cocoa framework. Does that affect
this?
It shouldn't. If anything, it'll make it easier in that, hopefully, the symbol files are of a usable format. The key is typically finding the right spot to break at the boundary between java and native code.
Is the native code in a dylib that is loaded into the JVM or do you have a custom executable that fires up the JVM internally?
In any case, you need to attach the native debugger to whatever process is running that native code. Probably after you've set up the java based debugging session appropriately.
In the past when doing JNI I have built a test-harness to facilitate the development of the native part of the application - and JNI code - which is notorious easy to screw up, avoiding the need to debug simultaneously from both sides.
This was written as a native application that invokes the JVM programmatically rather than starting with a Java application and then attempting to attach to JVM.
You can of course, start this and debug it in Xcode - which is an infinitely preferable experience to Eclipse with CDT.
The Java side of this arrangement is usually pretty simple and non-contriverial - basically a method which is called from the native part of the app that then makes one or more calls back into the native portion through JNI.
Here are the steps I follow to debug JNI (C/C++) under Windows, I presume ObjectiveC need the same. For Linux it's very similar (replace ; by :, %XXX% by ${XXX}...).
Create a file named MyDebug.gdbinit
Add these 4 lines into it:
set args -classpath .;xxxx.jar;yyy.jar path.to.your.Main
show args
run
bt
launch gdb and Java: gdb "%JAVA_HOME%\bin\java"
Use the GUI of the Java layer to reproduce the error
If you want to execute step by step your JNI code, gdb allows you to put some breakpoints

What's the modern equivalent of GetNextEvent in Cocoa?

I'm porting an archaic C++/Carbon program to Obj-C and Cocoa. The current version uses asynchronous usb reads and GetNextEvent to read the data.
When I try to compile this in Objective C, GetNextEvent isn't found because it's in the Carbon framework.
Searching Apple support yields nothing of use.
EDIT TO ADD:
Ok, so what I'm trying to do is run a document scanner through USB. I have set up the USBDeviceInterface and the USBInterfaceInterface (who came up with THAT name???) and I call (*usbInterfaceInterface)->WritePipeTO() to ask the scanner to scan. I believe this works. AT least the flatbed light moves across the page...
Then I try to use *(usbInterfaceInterface))->ReadPipeAsyncTO() to read data. I give this function a callback function, USBDoneProc().
The general structure is:
StartScan()
WaitForScan()
StartScan() calls the WritePipeTO and the ReadPipeAsyncTO
WaitForScan() has this:
while (deviceActive) {
EventRecord event;
GetNextEvent(0,&event);
if (gDataPtr != saveDataPtr) { // more data up the timeout
timeoutTicks = TickCount() + 60 * 60;
saveDataPtr = gDataPtr;
}
if (TickCount() > timeoutTicks) {
deviceActive = false;
}
}
Meanwhile, USBDoneProc incrementing gDataPtr to be the end of the data that we've read so far. It gets called several times during the asynchronous read, called automatically by the callback, as far as I can tell.
If I cake out the GetNextEvent() call in the WORKING code the USBDoneProc doesn't get called until the asynchronous readpipe timesout.
So it looks to me that I need something to give control back to the event handler so that the USBRead interrupts can actually interrupt and make the USBDoneProcget called...
Does that make any sense?
thanks.
I suppose the nearest thing to a Cocoa equivalent would be -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]. But bear in mind that GetNextEvent is archaic even for Carbon. The preferred way of handing events is the "don't call us, we'll call you" scheme, where the app calls NSApplicationMain or RunApplicationEventLoop and events are dispatched to you.
EDIT to add: Does you app have a normal event loop? If so, maybe WaitForScan could start a Carbon timer and return to the event loop. Each time the timer fires, do what you did in the WaitForScan loop.
there is a USB hidapi that works for mac on windows.
http://www.signal11.us/oss/hidapi/
may this could be of help to you?
It works fine (I can list the connected USB devices and connect/write/read to a device);
however, if i USB device is connected/disconnected during the runtime of the application, I don't see the new connected/disconnected devices.
See: https://github.com/signal11/hidapi/issues/14
If I add the following code to hidapi, then hidapi detects the new USB devices.
#include <Carbon/Carbon.h>
void check_apple_events() {
printf("check_apple_events\n");
RgnHandle cursorRgn = NULL;
Boolean gotEvent=TRUE;
EventRecord event;
while (gotEvent) {
gotEvent = WaitNextEvent(everyEvent, &event, 0L, cursorRgn);
}
}
I need to compile this on OSX10.5 because it uses Carbon instead of Cocoa.
I am currently looking how to transform this to Cocoa.
you are also trying to move your code to cocoa, right?
let me know if you find out; I'll post it here if I get it.
regards,
David
Have you considered throwing the whole thing out and using Image Kit's new-in-10.6 scanner support instead? Even if it's custom, writing a TWAIN driver for it might be easier (and is certainly better) than trying to twist Cocoa into a GetNextEvent shape.