08 Jul 2020
|
hacking
fix
This post continues tutorial for proper initialization of crash reporters t.
Option to track java exception was to use NSException.registerDefaultJavaUncaughtExceptionHandler(). It builds string presentation of exception creates NSException with this text as reason. At same time stack traces will be messed and will point to code where NSException is created.
FIRExceptionModel
Firebase Crashlytics provide special API FIRExceptionModel:
The Firebase Crashlytics Exception Model provides a way to report custom exceptions to Crashlytics that came from a runtime environment outside of the native platform Crashlytics is running in.
Exact case for RoboVM. To have it working its enough to setup Thread.setDefaultUncaughtExceptionHandler as bellow to convert exception stack traces into FIRExceptionModel elements:
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> {
FIRExceptionModel model = new FIRExceptionModel(ex.getClass().getName(), ex.getMessage() != null ? ex.getMessage() : "");
NSMutableArray<FIRStackFrame> modelFrames = new NSMutableArray<>();
StackTraceElement[] stackTraces = ex.getStackTrace();
if (stackTraces != null) {
for (StackTraceElement st : stackTraces) {
String symbol = st.getClassName() + '.' + st.getMethodName();
String fileName;
int lineNo = st.getLineNumber();
if (lineNo < 0)
lineNo = 0;
if (st.isNativeMethod()) {
fileName = "Native Method";
} else {
fileName = st.getFileName();
if (fileName == null)
fileName = "Unknown Source";
}
FIRStackFrame frame = new FIRStackFrame(symbol, fileName, lineNo);
modelFrames.add(frame);
}
}
model.setStackTrace(modelFrames);
FIRCrashlytics.crashlytics().recordExceptionModel(model);
// kill app
throw new ThreadDeath();
});
Note
Crashes will be reported as non-fatal !
Bonus
To get debug output from Crashlytics in simulator its not enought to provide -FIRDebugEnabled. Adding -FIRLoggerForceSTDERR helps.
30 Jun 2020
|
tutorial
Short guide how to get framework binary to use with RoboVM if it is provided by the vendor.
Building using Carthage
Carthage is intended to be the simplest way to add frameworks to your Cocoa application
Most github hosted projects provide a way to build artifacts using Carthage. Follow quick start guide for installation. Process is following:
- create
Cartfile and set its content up to project info, e.g.: github "Alamofire/Alamofire" ~> 4.7.2
- run
carthage update to build.
Result – dynamic framework.
Getting from CocoaPods
CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects
CocoaPods project is highly intended to be used as part of Xcode project.
Often vendors provide instructions on how to include their artifacts into project dependencies using CocoaPods. Xcode project is required, follow step to create one and build the pod:
- create new
iOS - Single View App project in XCode and exit it;
- create
Podfile in same folder where ${projectname}.xcodeproj is located;
- fill it with references from vendor (PersonalizedAdConsent here as example):
platform :ios, '9.0'
target 'pods_tutorial' do
use_frameworks!
use_modular_headers!
pod 'PersonalizedAdConsent'
end
- run
pod install --repo-update to add dependencies to project;
cd Pods
- build for simulator:
xcodebuild -configuration Release -sdk iphonesimulator13.5 -scheme PersonalizedAdConsent build \
CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
CONFIGURATION_BUILD_DIR=artifacts_sim/
- build for devices:
xcodebuild -configuration Release -sdk iphoneos13.5 -scheme PersonalizedAdConsent build \
CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
CONFIGURATION_BUILD_DIR=artifacts_device/
Few moments here:
target 'pods_tutorial' do specifies the target as it is in your Xcode project;
-scheme PersonalizedAdConsent specifies the scheme added by pods, to list all schemes in project use xcodebuild -list;
-sdk iphoneos13.5 specifies SDK available in Xcode, use xcodebuild -showsdks to list all available;
CONFIGURATION_BUILD_DIR=artifacts_sim/ specifies the directory, where to put binaries;
use_frameworks! and use_modular_headers! command to build a dynamic framework, if removed it will result in static library.
Building out of sources
Clone/download Google Mobile Ads Consent SDK as an example:
- Navigate to folder where
PersonalizedAdConsent.xcodeproj is located;
- use
xcodebuild -list to get list of targets – here is PersonalizedAdConsent;
- build same way as in case of cocoapods, simulator:
xcodebuild -configuration Release -sdk iphonesimulator13.5 -scheme PersonalizedAdConsent build \
CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
CONFIGURATION_BUILD_DIR=artifacts_sim/
- build for devices:
xcodebuild -configuration Release -sdk iphoneos13.5 -scheme PersonalizedAdConsent build \
CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
CONFIGURATION_BUILD_DIR=artifacts_device/
Note: this way static library/framework will be build.
23 Jun 2020
|
bro-gen
binding
robopods
altpods
AltPods were updated to v1.8.0-SNAPSHOTS to sync with recent releases. Part of list didn’t receive any API update hovewer bindings were re-generated against recent version of frameworks. Update list look as bellow:
New frameworks
Updated pods
These pods were pushed to https://oss.sonatype.org/content/repositories/snapshots maven repo under 1.8.0-SNAPSHOTS version.
Source code @github
Updates are not fully tested, please open issue if bug found.
NB: AltPods – are robopods kept at my personal standalone repo. This is done to prevent main robopods repo from turning into code graveyard. As these pods have low interest of community and low chances to be updated.
17 Jun 2020
|
target-framework
tutorial
Previous rework broke JNI. Rework added logic to start JVM automatically once framework is loaded to provide ObjectiveC classes from Java side available as soon as possible. A bad thing is that JVM structures required for JNI call were not exposed. A try to initialize(second) JVM with JNI_CreateJavaVM will fail.
Goal of the fix is to provide ability to have both JNI and ObjectiveC framework operational.
Disabling automatic JVM startup
This can be controlled with another key to robovm.xml. Instead, automatic JVM startup will be disabled if JNI_CreateJavaVM is present in the list of exported symbols:
<exportedSymbols>
<symbol>JNI_CreateJavaVM</symbol>
</exportedSymbols>
Compiler will generate(only in case of Framework target) _bcFrameworkSkipJavaVMStartup symbol that will be recognized by native code and JVM start up will be skipped.
Interoperability
Once JVM is created it is a good idea(but subject for JNI code design) to ping back framework support code to pre-load all objective-c custom classes. In this case JNI based code should call following function:
void rvmInitializeFrameworkWithJVM(JavaVM* externalVm, JNIEnv *externalEnv);
Code
Code was delivered as PR497
16 Jun 2020
|
fix
okhttp
okhttp stopped working again with RoboVM: once project migrated to kotlin and refactored code old issue is back:
java.lang.NoClassDefFoundError: android/os/Build$VERSION
Workaround is same:
package android.os;
public class Build {
public final static class VERSION {
public final static int SDK_INT = 21;
}
}
Worse thing is that okhttp unable to connect to cloudflare protected servers:
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x7fae5b122560: Failure in SSL library, usually a protocol error
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
While old good URL u = new URL(url); u.openConnection(); works.
Root case
Usually handshake failed when parameter of connection failed to negotiate. Good tool for investigating such things is a sniffer like Wireshark. Longs story short – it fails as its not able to negotiate a cipher to be used for connection. Cloudflare supports following ones:
25 May 2020
|
fix
RoboVM compiler is quite memory hungry by its nature: creates and manipulates a bunch of strings, have to keep parsed classes structures while compiling etc. Also there is a bug opened #150. Usually its enough to give JVM bigger heap (-Xmx4g) to make everyone happy.
There was a report on gitter channel about OOM that happens on a single file that can’t be fixed by increasing heap size. File just contained about 8K static final string fields.
To reproduce the case dummy file with 8000 strings was generated:
public class DBkeys
{
static public final String T0001 = "";
static public final String T0002 = "";
// lot of more these
static public final String T8000 = "";
}
Compilation using default heap settings just produce Out of memory exception but once JVM granted plenty of ram (-Xmx32g) it ends up with exception:
java.lang.OutOfMemoryError: Required array length too large
RoboVM went beyond of JVM limits trying to allocate byte array with size longer than JVM supported (Integer.MAX_VALUE - 2 gig). In other words – giving all memory you have to JVM heap will not solve this issue.
Root case
OOM happens when RoboVM compiles java code into LLVM IR code. This constant string only class doesn’t fit in MAX possible 2 GB java string. Let’s see what is in there.
20 May 2020
|
tutorial
watchos
DISCLAIMER: its not possible to create watchOS application with RoboVM. This tutorial describes how embed one created in Xcode as companion to RoboVM iOS application.
To package watchOS app RoboVM was adapted to handle packaging, signing and launching (on a simulator).
Code: experimental code was pushed to dkimitsa/watchkit-support branch.
(skip long read and start embedding watchOS app)
Changes to RoboVM
17 May 2020
|
bro-gen
tutorial
binding
Backtrack: issue, gitter
It becomes problematic to marshal methods that return NSArray<id<PROTOCOL>> like bellow:
- (void)readerSession:(NFCReaderSession *)session
didDetectTags:(NSArray<__kindof id<NFCTag>> *)tags;
Issue 1: NSArray
Direct binding will not compile:
13 May 2020
|
fix
idea
Issue
There was a rare bug when debug session in Intellij Idea failed and hang. It was not possible to terminate it without reopening the project.
Root case
Issue was reproduced while by having broken deployment to simulator step that allowed to investigate and locate the root case.
The root case – Idea hangs on waiting for stdout/stderr to be closed on terminated (due fail to deploy to sim) process.
Root case 1: the way stdout was redirected:
stdout -> FIFO FILE -> input stream
Root case 2: wrapper process class ProcessProxy around launcher process that was never returned stream to Idea but was never closing it (here it hangs).
The fix: launcher classes rework
- Currently, there are three launchers: ios simulator, ios device, console. All use own launcher. The fix introduced common functionality as separated
AbstractLauncherProcess class. Specific implementations just extends it;
- FIFO and redirect logic massively simplified. No FIFO, just simple Piped streams. Added observable stream to allow a plugin to monitor
stdout (as part of FIFO replacement) - required by JUNIT plugin;
Console target fixes
- fixed not working debugging of console target;
- added support for STDIN pipe to console target;
- console template fixed to include required
<target>console</target>.
Code
PR481
06 May 2020
|
bro-gen
binding
robopods
altpods
AltPods were update to v1.7.0-SNAPSHOTS to sync with recent releases. Part of list didn’t receive any API update hovewer bindings were re-generated against recent version of frameworks. Update list look as bellow:
New framework
Updated pods
Unchanged pods
These pods were pushed to https://oss.sonatype.org/content/repositories/snapshots maven repo under 1.7.0-SNAPSHOTS version.
Source code @github
Updates are not fullty tested, please open issue if bug found.
NB: AltPods – are robopods kept at my personal standalone repo. This is done to prevent main robopods repo from turning into code graveyard. As these pods have low interest of community and low chances to be updated.