I upgraded to Unity 4.6.0f3 and Xcode 6.1.1. After doing this, I get a linker error when trying to build my Xcode project.
I should point out the following up front:
Yes, I have scoured the forums and SO (and Google for that matter), and tried every recommended fix I could find. None of them have worked.
I can build and run the project in Xcode 6.1.1 that was exported from Unity 4.4 without seeing this issue
The Android project still exports, builds and runs perfectly after the Unity upgrade
I have not made any changes to the Unity or Xcode projects, other than editing my
CMVideoSampling class to use <OpenGLES/ES2/glext.h> instead of <OpenGLES/ES2/glext.h>
Here is the error:
Undefined symbols for architecture armv7:
“_SetSceneData”, referenced from:
RegisterMonoModules() in RegisterMonoModules.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
the “SetSceneData” function is the gateway between my native (in this case Objective C) code and the Unity C code:
Here’s the method (I removed the rest of the class for brevity but can share upon request):
using UnityEngine;
using System.Collections.Generic;
using System.Runtime.InteropServices;
[System.Serializable]
public class IOSCommunicator : MonoBehaviour, SceneStateCaching
{
//This is how we define messages that we will send to iOS. iOS will implement this method signature in C.
#if UNITY_IPHONE || UNITY_ANDROID
[DllImport("__Internal")]
private static extern void SetSceneData(string[] sliceTitles, int sliceCount, string[] videoURLs, int videoCount, string description);
#endif.....
If anyone else has dealt with this issue please let me know. I’m a newbie to Unity, and fairly new to Xcode, so any assistance will be greatly appreciated. Thanks in advance.
I would’ve thought it would still work as well. I only posted the top portion of the C class to keep the post short. Here’s the class in its entirety:
using UnityEngine;
using System.Collections.Generic;
using System.Runtime.InteropServices;
[System.Serializable]
public class IOSCommunicator : MonoBehaviour, SceneStateCaching
{
//This is how we define messages that we will send to iOS. iOS will implement this method signature in C.
#if UNITY_IPHONE || UNITY_ANDROID
[DllImport("__Internal")]
private static extern void SetSceneData(string[] sliceTitles, int sliceCount, string[] videoURLs, int videoCount, string description);
#endif
public DefectType defectType;
public ModelType modelType;
public List<SlicePlane> slicePlanes;
public TextAsset summaryHTMLAsset;
public string[] videoURLs;
private bool interestPointsEnabled;
private SlicePlane currentSlice;
//Script references
private ModelSlice sliceScript;
private InteractiveObject interactiveObject;
private InteractiveCamera interactiveCamera;
void Start ()
{
if (SceneState.Instance().isFirstScene == false)
{
sliceScript = GameObject.FindWithTag("BaseModel").GetComponent<ModelSlice>();
interactiveCamera = Camera.main.GetComponent<InteractiveCamera>();
interactiveObject = GameObject.FindWithTag("BaseModel").GetComponent<InteractiveObject>();
sendSceneSpecificInfoToIOS();
if (SceneState.Instance().isFirstHeartScene == false)
{
loadStateForNewScene();
}
}
}
void Update()
{
#if UNITY_ANDROID
if (Input.GetKeyDown (KeyCode.Escape)) {
AndroidJavaClass jc = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject> ("currentActivity");
jo.Call ("closeActivity");
}
#endif
}
/** SceneStateCaching **/
public void saveStateForNewScene()
{
SceneState.Instance().interestPointsEnabled = interestPointsEnabled;
if (currentSlice != null)
SceneState.Instance().savedSliceName = currentSlice.title;
}
public void loadStateForNewScene()
{
interestPointsEnabled = SceneState.Instance().interestPointsEnabled;
string savedSliceName = SceneState.Instance().savedSliceName;
if (savedSliceName != null)
{
setInterestPointsActive(false); //disable old points
SlicePlane slice = slicePlaneWithName(savedSliceName);
if (slice != null)
{
currentSlice = slice;
setInterestPointsActive(interestPointsEnabled);
if (slice.isNoSlice == true)
{
sliceScript.reset();
}
else
{
sliceScript.sliceModelWithPlane(new Plane(slice.normal, slice.distance), false);
}
}
}
}
public List<GameObject> getCurrentInterstPoints()
{
if (interestPointsEnabled == true && currentSlice != null)
return currentSlice.interestPoints;
else
return null;
}
public void updatePOIsFromModelRotate()
{
if (currentSlice != null)
{
Plane visibilityPlane;
if (currentSlice.isNoSlice == true)
visibilityPlane = new Plane(new Vector3(0f, 0f, 1f), interactiveCamera.target.position);
else
visibilityPlane = sliceScript.slicePlane;
currentSlice.updateVisibility(visibilityPlane);
}
}
private SlicePlane slicePlaneWithName(string name)
{
foreach (SlicePlane slicePlane in slicePlanes)
if (slicePlane.title == name)
return slicePlane;
return null;
}
private void setInterestPointsActive(bool active)
{
if (currentSlice != null)
{
foreach (GameObject interestPoint in currentSlice.interestPoints)
interestPoint.SetActive(active);
}
}
//----- Messages sent to iOS -----
/// <summary>
/// Sends all plane names and description text to iOS.
/// </summary>
private void sendSceneSpecificInfoToIOS()
{
string[] names = null;
int namesLength = 0;
if (slicePlanes != null)
{
namesLength = slicePlanes.Count;
names = new string[namesLength];
for (int i = 0; i < slicePlanes.Count; i++)
names[i] = slicePlanes[i].title;
}
int videoURLsLength = 0;
if (videoURLs != null)
{
videoURLsLength = videoURLs.Length;
}
#if UNITY_IPHONE
SetSceneData(names, namesLength, videoURLs, videoURLsLength, summaryHTMLAsset.text);
#elif UNITY_ANDROID
AndroidJavaClass mUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = mUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
activity.Call("SetSceneData", names, namesLength, videoURLs, videoURLsLength, summaryHTMLAsset.text);
#endif
}
//----- Handlers for messages received from IOS -----
/// <summary>
/// Loads a scene with the provided name. Caches the current state of the camera
/// so that switching between Normal, Defect, and Repair is seamless, even though
/// these are different scenes.
/// </summary>
public void loadScene(string sceneName)
{
if (SceneState.Instance().isFirstScene == true)
{
SceneState.Instance().isFirstScene = false;
SceneState.Instance().isFirstHeartScene = true;
}
else if (SceneState.Instance().isFirstHeartScene == true)
{
SceneState.Instance().isFirstHeartScene = false;
}
if (interactiveCamera != null && interactiveObject != null)
{
interactiveCamera.saveStateForNewScene();
interactiveObject.saveStateForNewScene();
saveStateForNewScene();
}
Application.LoadLevel(sceneName);
}
/// <summary>
/// Slices to the plane named sliceName if there is one. Also, all interest points
/// from the current slice are deactivated, and interest points for the next slice are
/// turned on (if interestPointsEnabled == true).
/// </summary>
public void slice(string sliceName)
{
setInterestPointsActive(false); //disable old points
SlicePlane slice = slicePlaneWithName(sliceName);
if (slice != null)
{
currentSlice = slice;
setInterestPointsActive(interestPointsEnabled);
if (slice.isNoSlice == true)
{
sliceScript.reset();
interactiveObject.reset(true);
}
else
{
sliceScript.sliceModelWithPlane(new Plane(slice.normal, slice.distance), true);
}
}
}
/// <summary>
/// Performs the initial defect slice. This is called when an overall defect is
/// initially selected (like Tetralogy of Fallot). It enables the current interest
/// points, sets the selected slice to slice 0, and resets the slice and camera.
/// </summary>
public void performInitialDefectSlice()
{
currentSlice = slicePlanes[0];
interestPointsEnabled = true;
setInterestPointsActive(true);
sliceScript.reset();
interactiveCamera.reset();
interactiveObject.reset(false);
//calculate the initial visibility so they all don't appear visibile at start
updatePOIsFromModelRotate();
}
/// <summary>
/// Stores if interest points are on/off and activates/deactivates them.
/// </summary>
public void toggleInterestPoints()
{
interestPointsEnabled = !interestPointsEnabled;
setInterestPointsActive(interestPointsEnabled);
}
}
Still can’t figure out why updating Unity and Xcode would cause this error. The Android project came through the Unity upgrade just fine. Anyone else dealing with this??
The C# half is fine, but Xcode is looking for a C definition/implementation for _SetSceneData. It should be in the .h/.m files in Plugins/iOS/. That’s what I think you’re missing. But again, not sure why those files would have disappeared on you.