codesign failed: resource fork, Finder information, or similar detritus not allowed

After updating AdscendMedia SDK in one of our app, when building the app, stumbled upon this error:

<project_folder>/Frameworks/AdscendMedia.framework: replacing existing signature
<project_folder>/Frameworks/AdscendMedia.framework: resource fork, Finder information, or similar detritus not allowed
Command /usr/bin/codesign failed with exit code 1

Found the solution here: https://developer.apple.com/library/content/qa/qa1940/_index.html

First, find files inside SDK framework folder which have extended attribute containing resource fork or Finder info:

cd <project_folder>/Frameworks
xattr -lr AdscendMedia.framework/

If got files, then delete them:

xattr -cr AdscendMedia.framework/

Creating [object] in a different context than the calling function.

Creating [object] in a different context than the calling function.

This warning occured when a JS callback function is passed to Objective-C as KrollCallback and then executed on non-JS thread.

KrollCallback *callback = [args [email protected]"callback"];

[OneSignal initWithLaunchOptions:[TiApp app].launchOptions appId:appId handleNotificationAction:^(OSNotificationResult *result) {
    [callback call:@[] thisObject:nil];
}];

Problem with this warning is, if the JS callback create Ti.Network.HTTPClient instance, then that instance is null.

function callback() {
    var http = Ti.Network.createHTTPClient();
    http.open('GET', getUrl()); // JS error! http is null
    http.send();
}

To fix, get krollContext instance of current module/proxy and execute callback inside invokeBlockOnThread

// For TiModule / TiProxy descendants
KrollContext *context = [self.executionContext krollContext];

// For TiUIView descendants
KrollContext *context = [self.proxy.executionContext krollContext];

// invoke
[context invokeBlockOnThread:^{
    [callback call:@[] thisObject:nil];
}];

Ti.Network.registerForPushNotifications no response

SDK: 3.5.1.GA, CLI: 4.0.1

Calling Ti.Network.registerForPushNotifications() has no response, whether success, error or callback, basically no response at all. Turned out that the build script can’t detect the method and add USE_TI_NETWORKREGISTERFORPUSHNOTIFICATIONS to defines.h during app compilation.

I created with a plugin that append the macro definition to *.pch file, here it is:

Create plugin folder:

cd $PROJECT_DIR
mkdir -p plugins/ti.pushnotifsymbol/hooks
touch plugins/ti.pushnotifsymbol/package.json
touch plugins/ti.pushnotifsymbol/hooks/pushnotifsymbol.js

pushnotifsymbol.js:

exports.id = 'ti.pushnotifsymbol';
exports.cliVersion = '>=3.2';
exports.init = function (logger, config, cli) {
    var path = require('path');
    var fs = require('fs');
    var util = require('util');
    var os = require('os');
    var pchFile;
    var triggered = false;
    cli.on('build.pre.compile', function(builder, next) {
        pchFile = path.join(builder.buildDir, builder.tiapp.name + '_Prefix.pch');
        next();
    });
    cli.on('build.ios.copyResource', function(builder, next) {
        if (!triggered) {
            triggered = true;
            if (fs.existsSync(pchFile)) {
                fs.appendFileSync(pchFile, os.EOL + '#define USE_TI_NETWORKREGISTERFORPUSHNOTIFICATIONS');
            }
        }
        next();
    });
};

Add <plugin>ti.pushnotifsymbol</plugin> to <plugins> section of your project tiapp.xml

Titanium iOS builder script

Location: TITANIUM_SDK/mobilesdk/osx/3.5.1.GA/iphone/cli/commands/_build.js
(TITANIUM_SDK is commonly at ~/Library/Application Support/Titanium)

1. Cannot change CFBundleVersion

  • CFBundleVersion is the build version number, available to developer and beta testers.
  • CFBundleShortVersionString is the release version number, what the users see on App Store
  • CFBundleVersion is automatically assign to value of <version> in tiapp.xml, but for beta testing purposes, need to change this value everytime we upload new build to iTunes Connect TestFlight
  • To fix this, modify iOS builder script in iOSBuilder.prototype.createInfoPlist() function, around line 2063
plist.CFBundleVersion = String(this.tiapp['build-version'] || this.tiapp.version);

2. If your Titanium SDK location is different from default (custom SDK path), the script doesn’t look up that folder when searching for modules

  • My Titanium SDK is located at ~/Applications/opt/Titanium, when running the ti build -p ios command, it doesn’t lookup for modules there
  • If my project included ti.cloud module, the build script will fail, cannot find the specified module
  • To fix this, in iOSBuilder.prototype.validate() function
// around line 1321, add:
customSDKPaths = config.get('paths.sdks'),

// around line 1331, add:
Array.isArray(customSDKPaths) && customSDKPaths.forEach(addSearchPath);

Titanium app hex color value with alpha channel

Color values are represented in hexadecimal value:

#000000

  • 0 – red, 0 – green, 0 – blue
  • color = black

#ffffff

  • 255 – red, 255 – green, 255 – blue
  • color = white

‘ff’ in hexadecimal is 255 in decimal, (2 ^ 8) – 1 = 255.

In CSS, to represent color with alpha channel, we can use the rgba() syntax:

rgba(0, 0, 0, 0.6)

  • 0 – red, 0 – green, 0 – blue
  • black with 60% opacity

In Titanium, rgba() syntax only available on iOS, but hex value also can be used to represent alpha channel, and it supported by both iOS & Android

#ff000000

  • 0 – red, 0 – green, 0 – blue
  • ff – 255 / 100% opacity

#90ffcc00

  • 255 – red, 204 – green, 0 – blue
  • 90 – 144 / 56% opacity

To easily maintain colors in app, this function is to transform rgba value into Titanium hex color format

function rgbaToHex(r, g, b, a) {
    var toHex = function(n) {
        return ('00' + (n | 0).toString(16)).slice(-2);
    };
    return '#' + toHex(((a * 100) / 100) * 255) + toHex(r) + toHex(g) + toHex (b);
}

Explanation:

  • (n | 0) is shortcut for parseInt(), to ensure the value passed in is integer. We don’t want the hex color value to have decimal point, e.g: #2.4ccc
  • .toString(16) is Number object function (not Object.toString()) to convert number into hexadecimal format
  • ('00' + value).slice(-2) is to add string padding to the left of the string, so that it will always have 2 characters. We don’t want the value to have one character, #0ccff is invalid value

Usage:

var view = Ti.UI.createView({
    backgroundColor: rgbaToHex(255, 204, 0, 0.5)
});