Titanium & Unity3D for iOS: Notes on Flurry Analytics – Parameters Take a Week to Show Up in Reports

I used an open source Flurry module developed by SoftGravity. It was easy to implement and seems to be working well. One potentially confusing thing with Flurry’s dashboard is that it’s not immediately clear how long it takes data to populate the various charts & reports.

“You currently have no event parameters to track.”

For example, I can see my parameter Name / Value pairs Event Logs. The little pie chart icon for Event Parameters shows up next to my events that have parameters. However, when I try to look at any of the reports for parameters that show up in the Event Logs I constantly get this message with no additional details: “You currently have no event parameters to track.” None of the FAQ pages on flurry.com mention this situation.

After installing the module, I’m calling logEvent, after initiating flurry w/ my API KEY, like so:

//in app.js: 
var flurry = require('sg.flurry');
	flurry.secureTransport(true); //use https to send request to make them more safe
	flurry.logUncaughtExceptions(true); //logs exception in objective-c code
	flurry.startSession('YOUR_API_KEY_HERE'); 
...

//in a content .js file:
flurry.logEvent( "My Footer Event" , {myfooter_link: 'Contact Us'} ); 

48 hrs after I created my application in the Flurry admin dashboard, this problem still persisted. At the time I was using Titanium SDK 2.1.3.GA and Titanium Studio, build: 2.1.2.201208301612.

Give it at least a week

5 days after I created the app in Flurry’s dashboard and started posting data to the Event Logs, my Event Parameters finally started showing up in the reports and the “You currently have no event parameters to track” message went away.

I first tried Flurry a while back with a test Unity3D project, using Prime31’s great Flurry for iOS plugin. It seemed to work well, but at the time, I didn’t pay much attention to the Event Parameters and how long they take to show up in the reports. Looking at my old Unity test data in Flurry’s dashboard, they’re certainly there now.

Advertisements

Unity3D for iOS: Relative Positioning with UIToolkit

Here’s one way to horizontally center a UIToolkit object, such as a UIButton, using positionCenterX(). Should work pretty good on the various iPad sizes, iPhone 4, iPhone 5. The one thing to keep in mind is the scale of the actual graphic will need to be adjusted, either via UIToolkit’s UI.cs class that listens for HD, SD or supper HD screen sizes or manually via your own code.

Javascript version

#pragma strict
public var buttonToolkit:UIToolkit;
private var newGameBtn:UIButton;

function Start() {
    newGameBtn = UIButton.create( buttonToolkit, "playBtnOff.png", "playBtnOn.png", 0, 0 );	
	newGameBtn.zIndex = 0;

	//centers it horizontally on iPad and iPhone: 
	newGameBtn.positionCenterX();
	//relatively positions it along y axis; slightly differnt actual position on iPads, iPhone 4, iPhone 5 
	newGameBtn.position.y = UIRelative.yPercentFrom( UIyAnchor.Bottom, 1.25f );	

	newGameBtn.highlightedTouchOffsets = new UIEdgeOffsets( 10 );	
	newGameBtn.onTouchUpInside += newGameBtnHandler;	
	newGameBtn.alphaFrom( 1.0f, 0.0f, Easing.Quintic.easeOut );
}

function newGameBtnHandler( sender:UIButton ) 
{
	Debug.Log("newGameBtn clicked!");
}

C# version

using UnityEngine;
using System;
using Prime31;

public class ExampleManagerScript : MonoBehaviour {

	//set via Inspector panel
	public UIToolkit buttonToolkit; 

    private UIButton newGameBtn;

	void Start() {	

		newGameBtn = UIButton.create( buttonToolkit, "playBtnOff.png", "playBtnOn.png", 0, 0 );	
		newGameBtn.zIndex = 0;

		//centers it horizontally on iPad and iPhone: 
		newGameBtn.positionCenterX();
		//relatively positions it along y axis; slightly differnt actual position on iPads, iPhone 4, iPhone 5 
		newGameBtn.position.y = UIRelative.yPercentFrom( UIyAnchor.Bottom, 1.25f );	

		newGameBtn.highlightedTouchOffsets = new UIEdgeOffsets( 10 );	
		newGameBtn.onTouchUpInside += newGameBtnHandler;	
		newGameBtn.alphaFrom( 1.0f, 0.0f, Easing.Quintic.easeOut );
	}

	void newGameBtnHandler( UIButton sender ) 
	{
		Debug.Log("newGameBtn clicked!");
	}
}

Unity3D for iOS: “The Executable was signed with invalid entitlements” message from Xcode (Xcode install) and “Failed to Install” from device (iTunes install)

When to install via iTunes, Device says this after almost finishing install progress bar when installing with Dev Provisioning Profile:

"Failed to Install" 

When tried via Xcode’s Organizer, get this message:

The entitlements specified in your application’s Code Signing Entitlements file do not match those specified in your provisioning profile. (0xE8008016).

Looks like Code Signing issue when I tried moving .p12 files to another machine and publishing from that other machine.

When I looked in KeyChain Access on my main machine, under “Keys” in the left side Category menu, the MyCompanyPrivateKeyName had “iPhone Developer: SoAndSo ($*@#&&#$)” certificate. BUT on my 2nd machine, the one I tried moving the .p12 files to, MyCompanyPrivateKeyName listed the “iPhone Distribution: Company Name” certificate. I had to delete the Certificates and Private Keys in KeyChain Access and then re-install them, making sure I install the Dev .p12 file first.

This didn’t fix the problem, however.

After some more poking around, I noticed that, since I had more than one developer certificate installed on that machine (my personal one and 2 company ones), Xcode was by default picking up the wrong Dev Provisioning Certificate and trying to incorrectly use it to code sign my .ipa. To fix this, in Xcode I had to

  • select the project name in the Navigator (left side bar in Xcode)
  • select Unity-iPhone under Project or Targets
  • select the Build Settings tab
  • find the Code Signing section & make sure the “iPhone Developer” profile specified there is correct and matches your app’s Bundle Identifier (com.yourCompany.YourAppName, unless you’re using a generic one via the “*” wildcard)

Unity3D for iOS: Black Screen After Loading Screen on iPad 3, iOS 6

I’ve heard of this error happening when you accidentally have a sprite sheet set to about 2048 pixels in width or height.

This just started happening to me, after I upgraded my Mac OS from 10.6.8 to 10.7.5, running Unity 3.5.6f4 and upgraded to TexturePacker Pro 3.0.1.

The error didn’t show for me on iPhone 4S or iPad 2. As soon as I tried to load my dev .ipa onto an iPad 3 running iOS 6 via iTunes, I started seeing the black screen.

Then I opened Xcode and hit Run with my iPad 3 plugged in and here’s the error that came up in Xcode’s Console:

- Completed reload, in  0.212 seconds
-> applicationDidBecomeActive()
UI texture is being autoloaded and it doesn't exist. Cannot find texturePackerConfigName: imgSheet4x

(Filename: /Applications/buildAgent/work/14194e8ce88cdf47/Runtime/ExportGenerated/iPhonePlayer-armv7/UnityEngineDebug.cpp Line: 43)

(lldb) 

Saw this in Thread 1 too:

My_App_Name`UISpriteManager_loadTextureAndPrepareForUse + 260 at Assembly-CSharp-firstpass.dll.s:17293: <<<<<< Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)

Now, I’m not using any 4x sprite sheets in this particular project due to file size constraints.

I just had my production machine upgraded from OS X 10.6.8 to OS X 10.7.5. Looks like this is what did it. 10.6 couldn’t really detect iPad3’s high res display, so stuff published out of Unity3D on 10.6 just auto scaled everything and used the 2x sprite sheet. Now that I’m on OS X 10.7, the OS suddenly allows iPad3 detection, UIToolkit responds by trying to load the 4x texture, which I don’t have due to .ipa file size constraints.

Changing the following in UI.cs:

	isHD = true;
				scaleFactor = 4; 
				hdExtension = "4x"; 

to this, by itself, just makes everything super tiny on iPad3’s display:

	isHD = true;
				scaleFactor = 2; //scaleFactor = 4
				hdExtension = "2x"; //hdExtension = "4x";

The quick fix here seems to be to republish from Unity installed on a machine w/ OS X 10.6.8. Your Unity license allows up to 2 installs per license. Remember, that operating system upgrades on the same machine count as “an install” in this case. If you’ve reached your limit, you can email Unity’s support and ask to deactivate one machine associated with your email address. They seem to be able to do it within a day or so. Then you can install on a new machine as needed. Not pretty, but an option.

Long term, you have to either add a 4x sprite sheet or update code to auto scale your UI.

Unity3D for Android: Using a Native Java Plugin to Grab the Android Device ID

The java code returns an MEID (Android Device ID) on CDMA phones (Verizon, Sprint), an IMEI on GSM phones (AT&T, T Mobile) and an Android ID on WIFI-only devices (no cellular network).

This code is based on Android Java Plugin for Unity example project in the Unity manual.

Why bother with a plugin?

I tried using Unity 3.5’s built-in SystemInfo.deviceUniqueIdentifier getter for obtaining a unique device ID but it didn’t meet our ad network’s requirements.

The set up in Unity 3.5

  • Drag the AndroidJava.jar file inside /Assets/Plugins/Android/. /Assets/Plugins/Android/bin/ worked too. This .jar file is created via Eclipse or command line javac based on the Java source code
  • Drag the example libjni.so file inside /Assets/Plugins/Android/. Make sure it’s inside /Plugins/Android or you’ll get a “DllNotFoundException: jni” error when you test on device with adb.
  • Drag JNI.cs into /Assets/Plugins/
  • Drag JavaVM.cs into /Assets/Plugins/
  • Make sure your project has an AndroidManifest.xml file inside /Assets/Plugins/Android/ with android.permission.READ_PHONE_STATE permission set. android.permission.READ_PHONE_STATE is needed to access an Android phone/tablet’s Android ID, IMEI or MEID. If you’re making HTTP calls you’ll also need additional permissions, etc.

Code that worked:

...
	private static IntPtr	JavaClass; 
	private static int		GetDeviceId;
...

	///if you're calling the plugin from a static method, you still have to make sure an instance of this script is attached to 
	///an empty GameObject in Hierarchy so Start() method runs & the below code initializes:	
	void Start() {
		
		#if UNITY_ANDROID 		
		
			// attach our thread to the java vm; obviously the main thread is already attached but this is good practice..
			JavaVM.AttachCurrentThread();
			
			// first we try to find our main activity..
			IntPtr cls_Activity		= JNI.FindClass("com/unity3d/player/UnityPlayer");
			int fid_Activity		= JNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
			IntPtr obj_Activity		= JNI.GetStaticObjectField(cls_Activity, fid_Activity);
			Debug.Log("obj_Activity = " + obj_Activity);		
			
			// create a JavaClass object...
               //"com/yourCompany/yourProjectName" should be your Bundle ID in Unity's Player Settings & should be the package name in your Java class
			IntPtr cls_JavaClass	= JNI.FindClass("com/yourCompany/yourProjectName/JavaClass"); 	
			int mid_JavaClass		= JNI.GetMethodID(cls_JavaClass, "<init>", "(Landroid/app/Activity;)V");
			IntPtr obj_JavaClass	= JNI.NewObject(cls_JavaClass, mid_JavaClass, obj_Activity);
			Debug.Log("java Helper object = " + obj_JavaClass);		
			
			// create a global reference to the JavaClass object and fetch method id(s)..
			JavaClass					= JNI.NewGlobalRef(obj_JavaClass);
			GetDeviceId					= JNI.GetMethodID(cls_JavaClass, "GetDeviceId", "()Ljava/lang/String;");
			Debug.Log("JavaClass global ref = " + JavaClass);
			Debug.Log("JavaClass GetDeviceId method id = " + GetDeviceId);						
		
		#endif			
	}	
...

	/// <summary>
	/// Gets the unique device for Android devices (IMEI, MEID or Android ID).
	/// </summary>
	/// <returns>
	/// Returns the id as a string.
	/// </returns>
	private string GetTheDeviceID()
	{		
		//didn't work... supposed to return IMEI but didn't 
		//return SystemInfo.deviceUniqueIdentifier;
		
		String dID = "";
		
		#if UNITY_ANDROID 
	
			// again, make sure the thread is attached..
			JavaVM.AttachCurrentThread();
						
			// get the Java String object from the Helper java object
			IntPtr str_cacheDir 	= JNI.CallObjectMethod(JavaClass, GetDeviceId);
			Debug.Log("str_cacheDir = " + str_cacheDir);		
			
			// convert the Java String into a Mono string
			IntPtr stringPtr = JNI.GetStringUTFChars(str_cacheDir, 0);
			Debug.Log("stringPtr = " +stringPtr);
			dID = Marshal.PtrToStringAnsi(stringPtr);
			JNI.ReleaseStringUTFChars(str_cacheDir, stringPtr); 		
					
		#endif

		Debug.Log(" Device ID value is = " + dID);
		
		return devID;		
	}		

/// call GetTheDeviceID() when you need to use it
...

The Java Code

The below code is based on requirements by ad networks that need to use a specific device ID for metrics like conversion tracking. It’s the main source file for generating the AndroidJava.jar you need to import into Unity’s Plugins folder.

The Device ID & hashing methods implementations come directly from Millennial Media‘s public wiki.

package com.yourCompany.yourProjectName;

import android.app.Activity;
import android.content.Context;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class JavaClass
{
	private Activity mActivity;
	
	public JavaClass(Activity currentActivity)
	{
		Log.i("JavaClass", "Constructor called with currentActivity = " + currentActivity);
		mActivity = currentActivity;
	}
	
	public String GetDeviceId()
	{		
		// Get the device ID
        String auid = android.provider.Settings.Secure.ANDROID_ID + "android_id";
        
        Context context = mActivity.getApplicationContext();
        
        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        if(tm != null)
        {
             try{
             auid = tm.getDeviceId();
             }
             catch (SecurityException e){
                  e.printStackTrace();
             }
             tm = null;
        }
        if(((auid == null) || (auid.length() == 0)) && (context != null))
             auid = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
        if((auid == null) || (auid.length() == 0))
             auid = null;
        
        return auid;	
	}
	
	public String GetHashedDeviceIdSHA1()
	{
		String deviceId = GetDeviceId();
		
		String hashedDeviceId = hashInputSHA1(deviceId);
		
		return hashedDeviceId;
	}
	
	public String GetHashedDeviceIdMD5()
	{
		String deviceId = GetDeviceId();
		
		String hashedDeviceId = hashInputMD5(deviceId);
		
		return hashedDeviceId;
	}

	
	/**
     * <p>
     * Hashes the given plain text using the MD5 algorithm.
     * </p>
     * 
     * <p>
     * Example:
     * </p>
     * 
     * <p>
     * 098f6bcd4621d373cade4e832627b4f6
     * </p>
     * 
     * @param input
     *            The raw input which must be hashed.
     * @return A String representing the MD5 hashed output.
     */
    public static String hashInputMD5(String input)
    {
        String rv = null;
          
        if (input != null)
        {
            try
            {
                // Hash the user ID
                byte[] hashBytes = null;
                MessageDigest md = MessageDigest.getInstance("MD5");
                  
                synchronized (md)
                {
                    hashBytes = md.digest(input.getBytes());
                }
                 
                // Convert the hashed bytes into a properly formatted String
                if (hashBytes != null)
                {
                    StringBuilder sb = new StringBuilder();
                    
                    for (byte b : hashBytes)
                    {
                        String hexString = Integer.toHexString(0x00FF & b);
                        sb.append((hexString.length() == 1) ? "0" + hexString
                                                           : hexString);
                    }
                         
                    rv = sb.toString();
                }
            }
            catch (NoSuchAlgorithmException exception)
            {
                // This exception should never occur
            }
        }
         
        return rv;
    }
    
    /**
     * <p>
     * Hashes the given plain text using the SHA-1 algorithm.
     * </p>
     *
     * <p>
     * Example:
     * </p>
     *
     * <p>
     * a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
     * </p>
     *
     * @param userId
     *            The raw input which must be hashed.
     * @return A String representing the SHA-1 hashed input.
     */
    public static String hashInputSHA1(String input)
    {
        String rv = null;
          
        if (input != null)
        {
            try
            {
                // Hash the user ID
                byte[] hashBytes = null;
                MessageDigest md = MessageDigest.getInstance("SHA1");
             
                synchronized (md)
                {
                    hashBytes = md.digest(input.getBytes());
                }
              
                // Convert the hashed bytes into a properly formatted String
                if (hashBytes != null)
                {
                    StringBuilder sb = new StringBuilder();
              
                    for (byte b : hashBytes)
                    {
                        String hexString = Integer.toHexString(0x00FF & b);
                        sb.append((hexString.length() == 1) ? "0" + hexString
                                                           : hexString);
                    }
                 
                    rv = sb.toString();
                }
            }
            catch (NoSuchAlgorithmException exception)
            {
                // This exception should never occur
            }
        }
             
        return rv;
    }
}

AndroidManifest.xml

If you don’t specify the correct permissions for your app and try to do things like make an HTTP request or grab the Device ID, you’ll get an error, like this:

10-02 15:06:49.223  7776  7784 W System.err: java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10109 nor current process has android.permission.READ_PHONE_STATE.
10-02 15:06:49.223  7776  7784 W System.err: 	at android.os.Parcel.readException(Parcel.java:1322)
10-02 15:06:49.223  7776  7784 W System.err: 	at android.os.Parcel.readException(Parcel.java:1276)
10-02 15:06:49.223  7776  7784 W System.err: 	at com.android.internal.telephony.IPhoneSubInfo$Stub$Proxy.getDeviceId(IPhoneSubInfo.java:150)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java:216)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.yourCompany.yourProjectName.JavaClass.GetDeviceId(JavaClass.java:44)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)

I grabbed the default one from inside Unity.app on my Mac:

/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidDevelopmentPlayer/AndroidManifest.xml

Kept all as is, just added my permissions and it worked:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
	android:installLocation="preferExternal"
    android:versionCode="1"
    android:versionName="1.0">
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>

    <application
		android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:debuggable="true">

        //...

    </application>

	
	<!-- PERMISSIONS -->
		<uses-permission android:name="android.permission.INTERNET"/>
		<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
		<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
	
</manifest>

Some Other Possible Errors

Here’s a fun one, based on a dumb mistake I made. I put the libjni.so into the wrong folder, /Assets/Plugins/, instead of /Assets/Plugins/Android/:

10-02 14:43:17.147  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.147  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.155  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.163  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.171  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.171  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.179  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.186  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.897  1410  1410 I wpa_supplicant: WPS-AP-AVAILABLE 
10-02 14:43:17.905  1320  1411 V WifiMonitor: Event [WPS-AP-AVAILABLE ]
10-02 14:43:17.905  1320  1411 D WifiMonitor: WPS Event: WPS-AP-AVAILABLE 
10-02 14:43:18.264  1391  1391 D RadioSignalLevel: evdo dbmLevel: 4, snrLevel: 3
10-02 14:43:18.522  7587  7596 I Unity   : DllNotFoundException: jni
10-02 14:43:18.522  7587  7596 I Unity   :   at (wrapper managed-to-native) JavaVM:AttachCurrentThread ()
10-02 14:43:18.522  7587  7596 I Unity   :   at MyUnityDeviceIdScript.Start () [0x00000] in <filename unknown>:0 
10-02 14:43:18.522  7587  7596 I Unity   :  
10-02 14:43:18.522  7587  7596 I Unity   : (Filename:  Line: -1)
10-02 14:43:18.522  7587  7596 I Unity   : 
10-02 14:43:21.561  7587  7596 I Unity   : checkDeviceID() called
10-02 14:43:21.561  7587  7596 I Unity   :  
10-02 14:43:21.561  7587  7596 I Unity   : (Filename: ./Runtime/ExportGenerated/AndroidManaged/UnityEngineDebug.cpp Line: 43)
10-02 14:43:21.561  7587  7596 I Unity   : 
10-02 14:43:21.671  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.694  7587  7596 I Unity   : DllNotFoundException: jni
10-02 14:43:21.694  7587  7596 I Unity   :   at (wrapper managed-to-native) JavaVM:AttachCurrentThread ()
10-02 14:43:21.694  7587  7596 I Unity   :   at MyUnityDeviceIdScript.GetUniqueDeviceID () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :   at MyUnityDeviceIdScript.callTheUrlWithDeviceIDParam () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :   at GameManager.checkDeviceID () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :  

As far as I’ve seen, this code only works on a device. If you try to test inside Unity, you’ll get a similar error:

DllNotFoundException: jni
MyUnityDeviceIdScript.Start () (at Assets/Scripts/MyUnityDeviceIdScript.cs:21)

Once you test on a device, if all else if good, this error should go away.

NOTE: looks like this code requires some time to load up the JNI .so file. Not absolutely sure yet, but it appears if I have a fairly complex project and I try to call a JNI related method very soon after app loading, the app crashes with weird mystery output in adb logcat. Need to look into this more to be sure about the details.

Unity3D for Android: “JNI ERROR (app bug): attempt to use stale global reference” with Incorrect Use of Prime31’s Social Networking Plugin

I was porting a Unity3D-built iOS game to Android the other day, while using the awesome Social Networking plugin from Prime31, and got this fun Java error:

E/dalvikvm( 5539): JNI ERROR (app bug): attempt to use stale global reference 0x32
E/dalvikvm( 5539): VM aborting
F/libc    ( 5539): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 5583 (UnityMain)
... 

The above error crashed my app consistently.

I was lucky in this case because I was fairly sure where it’s coming from, suspecting an unnecessary call to FacebookAndroid.logout() inside a check for !FacebookAndroid.isSessionValid() check. Not my proudest coding moment, for sure.

Once I put a Debug.Log() right before the FacebookAndroid.logout() call and right after. The error changed to… or perhaps a new error started showing up around the same place in the debug log:

W/dalvikvm( 2910): threadid=9: thread exiting with uncaught exception (group=0x4001d5a0)
E/AndroidRuntime( 2910): FATAL EXCEPTION: GLThread 10
E/AndroidRuntime( 2910): java.lang.NullPointerException
E/AndroidRuntime( 2910): 	at com.prime31.FacebookPlugin.logout(FacebookPlugin.java:187)
E/AndroidRuntime( 2910): 	at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
E/AndroidRuntime( 2910): 	at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
E/AndroidRuntime( 2910): 	at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
E/AndroidRuntime( 2910): 	at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)
W/ActivityManager( 1323):   Force finishing activity com.companyName.MyBundleIDHere/com.unity3d.player.UnityPlayerNativeActivity

The app stopped crashing but the java.lang.NullPointerException didn’t go away either. Still looking into it.

Unity3D for iOS: “Apple LLVM Compiler 3.0 Error”… “iPhone_target_Prefix.pch’ has been modified since the precompiled header was built”

The Error

I upgraded to the latest minor version of Unity 3.5, specifically 3.5.5f. All was fine until I tried to create an Archive so I could publish an .ipa file from Xcode. Once I hit Product > Archive >, I got Build Failed with this error:

Apple LLVM Compiler 3.0 Error:
fatal error: file '/YourPathToProject/YourProjectName/Classes/iPhone_target_Prefix.pch' has been modified since the precompiled header was built

The Solution

After trying to Clean the project & a few other things, nothing worked. Finally, found this stackoverflow solution that fixed it. The Library folder path for the folder that had to be deleted was under
/YourUserName/Library/Developer/Xcode/DerivedData/{project name + gobly-gook}.