Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

ScriptableObject properties are null on iOS but valid in the Editor.

Discussion in 'Scripting' started by maeln, Jan 14, 2022.

  1. maeln

    maeln

    Joined:
    Oct 20, 2021
    Posts:
    11
    Hello !

    In a app that I am developing, I have to pass some apis url to a lot of different script. I also wanted to be able to switch between the dev env url and the prod env.
    So I created a ScriptableObject with a propertie that return either the dev url or the prod url base on a simple boolean switch.
    This works perfectly in the editor, but for whatever reason, whenever I try the app on iOS, the prod url works (they are hardcoded in the scriptableobject) but the dev url return null.

    I tried cleaning the build, removing the scriptable object and recreating it, unistalling the app from the iphone i am testing it on, ... nothing change it. I am using Unity 2020.3.18f1.

    Here is my SO:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. [CreateAssetMenu(fileName = "New api adresses", menuName = "Config/APIAdresses")]
    5. public class APIAdressesSO : ScriptableObject
    6. {
    7.     public bool useProd = true;
    8.  
    9.     private string _graphQlProd = "https://prod.url.org/api/graphql/";
    10.     private string _discordProd = "https://prod.url.org/api/auth/discord-login";
    11.  
    12.     private string _graphql;
    13.     private string _discord;
    14.  
    15.     public string GraphQLURI
    16.     {
    17.         set
    18.         {
    19.             _graphql = value;
    20.         }
    21.         get
    22.         {
    23.             return useProd ? _graphQlProd : _graphql;
    24.         }
    25.     }
    26.  
    27.     public string DiscordAuthURI
    28.     {
    29.         set
    30.         {
    31.             _discord = value;
    32.         }
    33.         get
    34.         {
    35.             return useProd ? _discordProd : _discord;
    36.         }
    37.     }
    38. }
    39.  
    And here is the code of the editor :
    Code (CSharp):
    1.  
    2. using UnityEditor;
    3.  
    4. [CustomEditor(typeof(APIAdressesSO))]
    5. public class APIAdressesEditor : Editor
    6. {
    7.     public override void OnInspectorGUI()
    8.     {
    9.         // base.OnInspectorGUI();
    10.        
    11.         APIAdressesSO apis = (APIAdressesSO) target;
    12.         bool useProd = EditorGUILayout.Toggle("Use Prod ?", apis.useProd);
    13.         if (useProd != null && useProd != apis.useProd)
    14.         {
    15.             apis.useProd = useProd;
    16.         }
    17.        
    18.         if (!apis.useProd)
    19.         {
    20.             string graphql = EditorGUILayout.TextField("GraphQL URI", apis.GraphQLURI);
    21.             if (graphql != null && graphql.Trim() != "" && graphql != apis.GraphQLURI)
    22.             {
    23.                 apis.GraphQLURI = graphql;
    24.             }
    25.            
    26.             string discord = EditorGUILayout.TextField("Discord URI", apis.DiscordAuthURI);
    27.             if (discord != null && discord.Trim() != "" && discord != apis.DiscordAuthURI)
    28.             {
    29.                 apis.DiscordAuthURI = discord;
    30.             }
    31.         }
    32.        
    33.         EditorGUILayout.LabelField("current graphql uri:", apis.GraphQLURI);
    34.     }
    35. }
    36.  
    Do you have any idea why it would do that ?
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,915
    Your custom editor does not communicate your changes to Unity's Undo and serialization system since you use the target reference directly. You have to use Undo.RecordObject before you apply any changes to your ScriptableObject instance.

    Code (CSharp):
    1. if (useProd != null && useProd != apis.useProd)
    2. {
    3.     Undo.RecordObject(apis, "Changed production state");
    4.     apis.useProd = useProd;
    5. }
    likewise:
    Code (CSharp):
    1.             {
    2.                 Undo.RecordObject(apis, "Changed graphql URI");
    3.                 apis.GraphQLURI = graphql;
    4.             }
    5.             // [...]
    6.             {
    7.                 Undo.RecordObject(apis, "Changed discord auth URI");
    8.                 apis.DiscordAuthURI = discord;
    9.             }
    Otherwise your changes would only last until you close Unity or reload the project.
     
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,915
    Ohh and another little detail.... your backing fields for your URIs are not serialized. So you have to mark your two fields
    _graphql
    and
    _discord
    with the SerializeField attribute. Otherwise they won't be serialized at all.

    edit
    ps: If you want your production URIs to be hardcoded in code, you may also want to either mark the production URI fields with NonSerialized to ensure they are never serialized or make them consts instead.
     
  4. maeln

    maeln

    Joined:
    Oct 20, 2021
    Posts:
    11
    Thank you for all your advice Bunny83. I did what you said and it worked, the only thing was that marking the field with [SerializeField] didn't work, I had to make them public for it to really work for whatever reason.
     
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,915
    That sounds strange. I doubt that this would ever be necessary. Are you sure you haven't done something else wrong? You had the attribute on each of your two variables?
     
  6. maeln

    maeln

    Joined:
    Oct 20, 2021
    Posts:
    11
    Hey, sorry for the wait.

    I tried again and no dice, for whatever reason, If i put them in private with the [SerializeField] decorator, they don't get serialized. Here is the code:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. [CreateAssetMenu(fileName = "New api adresses", menuName = "Config/APIAdresses")]
    5. public class APIAdressesSO : ScriptableObject
    6. {
    7.     [SerializeField]
    8.     public bool useProd = true;
    9.    
    10.     private const string _graphQlProd = "....";
    11.     private const string _discordProd = ".....";
    12.    
    13.     [SerializeField]
    14.     private string _graphql;
    15.    
    16.     [SerializeField]
    17.     private string _discord;
    18.  
    19.     public string GraphQLURI
    20.     {
    21.         set => _graphql = value;
    22.         get => useProd ? _graphQlProd : _graphql;
    23.     }
    24.  
    25.     public string DiscordAuthURI
    26.     {
    27.         set => _discord = value;
    28.         get => useProd ? _discordProd : _discord;
    29.     }
    30. }
    31.  

    EDIT: After looking a bit more, I think it is unrelated to private/public field. I can trigger the same issue with public fields. It seems more related to when the object is serialized. It seems that in many case, the object is not serialized and threfore, its values are lost. I thing it is my editor code who is probably at fault here
     
    Last edited: Jan 21, 2022
  7. maeln

    maeln

    Joined:
    Oct 20, 2021
    Posts:
    11
    So I moved everything in the editor to use `serializedObject` and `SerializedField` and now it is working much better.

    Edit: Well, actually no, for whatever reason, the serialized object seem to retain some of the old value ...
     
  8. Putcho

    Putcho

    Joined:
    Jun 1, 2013
    Posts:
    246
    here my normal way to write Editor for scriptableObject, well I currently switch to the UITool kit.

    here the version I pimp

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu(fileName = "New api adresses", menuName = "Config/APIAdresses")]
    4. public class APIAdressesSO : ScriptableObject
    5. {
    6.     public bool useProd = true;
    7.  
    8.     private string _graphQlProd = "https://prod.url.org/api/graphql/";
    9.     private string _discordProd = "https://prod.url.org/api/auth/discord-login";
    10.  
    11.     [SerializeField]
    12.     private string _graphql;
    13.     [SerializeField]
    14.     private string _discord;
    15.  
    16.     public string GraphQLURI
    17.     {
    18.         set
    19.         {
    20.             _graphql = value;
    21.         }
    22.         get
    23.         {
    24.             return useProd ? _graphQlProd : _graphql;
    25.         }
    26.     }
    27.  
    28.     public string DiscordAuthURI
    29.     {
    30.         set
    31.         {
    32.             _discord = value;
    33.         }
    34.         get
    35.         {
    36.             return useProd ? _discordProd : _discord;
    37.         }
    38.     }
    39. }
    here editor (old GIUD)

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. using UnityEditor;
    4.  
    5. [CustomEditor(typeof(APIAdressesSO))]
    6. public class APIAdressesEditor : Editor
    7. {
    8.     public override void OnInspectorGUI()
    9.     {
    10.         // base.OnInspectorGUI();
    11.  
    12.         //refresh serializedObject from the target before draw layout for correct value
    13.         serializedObject.Update();
    14.  
    15.  
    16.         APIAdressesSO apis = (APIAdressesSO)target;
    17.         //Find SerializedProperty
    18.         SerializedProperty useProdProperty = serializedObject.FindProperty(nameof(apis.useProd));
    19.         SerializedProperty graphqlProperty = serializedObject.FindProperty("_graphql");
    20.         SerializedProperty discordProperty = serializedObject.FindProperty("_discord");
    21.  
    22.         EditorGUILayout.PropertyField(useProdProperty, new GUIContent("Use Prod ?"));
    23.  
    24.         if (!useProdProperty.boolValue)
    25.         {
    26.             EditorGUILayout.PropertyField(graphqlProperty, new GUIContent("GraphQL URI"));
    27.             EditorGUILayout.PropertyField(discordProperty, new GUIContent("Discord URI"));
    28.         }
    29.  
    30.         EditorGUILayout.LabelField("current graphql uri:", apis.GraphQLURI);
    31.         EditorGUILayout.LabelField("current discord uri:", apis.DiscordAuthURI);
    32.  
    33.         //apply changed to the serializedObject before the next frame
    34.         serializedObject.ApplyModifiedProperties();
    35.     }
    36. }
     
    Bunny83 likes this.