Forum Xamarin.Forms

PCLStorage failing on Visual Studio for Xamarin.Forms project

I've created a Xamarin.Forms project that uses PCLStorage. I've added PCLStorage from Nuget to all projects in the solution (iOS, Android, Win Phone and shared). When I run the app in the iOS simulator or on my iPhone 5s device I get "System.NotImplementedException: This functionality is not implemented in the portable version of this assembly.".

I've tried changing"Linke behaviour" to "Don't link" in the project settings and tried adding a "LinkerPleaseInclude" class to my iOS project for PCLStorage (as described here: http://forums.xamarin.com/discussion/18356/build-fails-for-iphone-but-is-fine-with-android-and-iphonesimulator) without success.

If I run the app/solution with Xamarin Studio on my Mac everything works.

Posts

  • ChaseFlorellChaseFlorell CAInsider, University mod
    edited June 2014

    The exception has nothing to do with Linking. It's saying that a method that is being called by the DI Interface hasn't been implemented on the platform.

    On first glance, it appears as thought they're throwing the exception on 'Portable' (whatever that means).
    It's really quite simple to roll your own Storage Service.. you can actually just rip out the necessary bits from here.

    Just put the Interface in your PCL

    public interface ISettingsService
    {
        T GetValueOrDefault<T>(string key, T defaultValue = default(T));
        bool AddOrUpdateValue(string key, Object value);
        void Save();
    }
    

    And implement it on each platform.

    Here's the Android Implementation

    public class SettingsService : ISettingsService
    {
        private readonly object _locker = new object();
    
        /// <summary>
        /// Gets the current value or the default that you specify.
        /// </summary>
        /// <typeparam name="T">Vaue of t (bool, int, float, long, string)</typeparam>
        /// <param name="key">Key for settings</param>
        /// <param name="defaultValue">default value if not set</param>
        /// <returns>Value or default</returns>
        public T GetValueOrDefault<T>(string key, T defaultValue = default(T))
        {
            lock (_locker)
            {
                if (NSUserDefaults.StandardUserDefaults[key] == null)
                    return defaultValue;
    
                var typeOf = typeof (T);
                if (typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof (Nullable<>))
                {
                    typeOf = Nullable.GetUnderlyingType(typeOf);
                }
                object value = null;
                var typeCode = Type.GetTypeCode(typeOf);
                var defaults = NSUserDefaults.StandardUserDefaults;
                switch (typeCode)
                {
                    case TypeCode.Boolean:
                        value = defaults.BoolForKey(key);
                        break;
                    case TypeCode.Int64:
                        var savedval = defaults.StringForKey(key);
                        value = Convert.ToInt64(savedval);
                        break;
                    case TypeCode.Double:
                        value = defaults.DoubleForKey(key);
                        break;
                    case TypeCode.String:
                        value = defaults.StringForKey(key);
                        break;
                    case TypeCode.Int32:
                        value = defaults.IntForKey(key);
                        break;
                    case TypeCode.Single:
                        value = defaults.FloatForKey(key);
                        break;
    
                    case TypeCode.DateTime:
                        var savedTime = defaults.StringForKey(key);
                        var ticks = string.IsNullOrWhiteSpace(savedTime) ? -1 : Convert.ToInt64(savedTime);
                        if (ticks == -1)
                            value = defaultValue;
                        else
                            value = new DateTime(ticks);
                        break;
                }
    
    
                return null != value ? (T) value : defaultValue;
            }
        }
    
        /// <summary>
        /// Adds or updates the value 
        /// </summary>
        /// <param name="key">Key for settting</param>
        /// <param name="value">Value to set</param>
        /// <returns>True of was added or updated and you need to save it.</returns>
        public bool AddOrUpdateValue(string key, object value)
        {
            lock (_locker)
            {
                var typeOf = value.GetType();
                if (typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof (Nullable<>))
                {
                    typeOf = Nullable.GetUnderlyingType(typeOf);
                }
                var typeCode = Type.GetTypeCode(typeOf);
                var defaults = NSUserDefaults.StandardUserDefaults;
                switch (typeCode)
                {
                    case TypeCode.Boolean:
                        defaults.SetBool(Convert.ToBoolean(value), key);
                        break;
                    case TypeCode.Int64:
                        defaults.SetString(Convert.ToString(value), key);
                        break;
                    case TypeCode.Double:
                        defaults.SetDouble(Convert.ToDouble(value), key);
                        break;
                    case TypeCode.String:
                        defaults.SetString(Convert.ToString(value), key);
                        break;
                    case TypeCode.Int32:
                        defaults.SetInt(Convert.ToInt32(value), key);
                        break;
                    case TypeCode.Single:
                        defaults.SetFloat(Convert.ToSingle(value), key);
                        break;
                    case TypeCode.DateTime:
                        defaults.SetString(Convert.ToString(((DateTime) value).Ticks), key);
                        break;
                }
            }
    
            return true;
        }
    
        /// <summary>
        /// Saves all currents settings outs.
        /// </summary>
        public void Save()
        {
            lock (_locker)
            {
                var defaults = NSUserDefaults.StandardUserDefaults;
                defaults.Synchronize();
            }
        }
    }
    

    And here's the iOS implementation

    public class SettingsService : ISettingsService
    {
        private readonly object _locker = new object();
    
        public SettingsService()
        {
            SharedPreferences = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
            SharedPreferencesEditor = SharedPreferences.Edit();
        }
    
        private static ISharedPreferences SharedPreferences { get; set; }
        private static ISharedPreferencesEditor SharedPreferencesEditor { get; set; }
    
        public T GetValueOrDefault<T>(string key, T defaultValue = default(T))
        {
            lock (_locker)
            {
                var typeOf = typeof (T);
                if (typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof (Nullable<>))
                {
                    typeOf = Nullable.GetUnderlyingType(typeOf);
                }
                object value = null;
                var typeCode = Type.GetTypeCode(typeOf);
                switch (typeCode)
                {
                    case TypeCode.Boolean:
                        value = SharedPreferences.GetBoolean(key, Convert.ToBoolean(defaultValue));
                        break;
                    case TypeCode.Int64:
                        value = SharedPreferences.GetLong(key, Convert.ToInt64(defaultValue));
                        break;
                    case TypeCode.String:
                        value = SharedPreferences.GetString(key, Convert.ToString(defaultValue));
                        break;
                    case TypeCode.Int32:
                        value = SharedPreferences.GetInt(key, Convert.ToInt32(defaultValue));
                        break;
                    case TypeCode.Single:
                        value = SharedPreferences.GetFloat(key, Convert.ToSingle(defaultValue));
                        break;
                    case TypeCode.DateTime:
                        var ticks = SharedPreferences.GetLong(key, -1);
                        if (ticks == -1)
                            value = defaultValue;
                        else
                            value = new DateTime(ticks);
                        break;
                }
    
                return null != value ? (T) value : defaultValue;
            }
        }
    
        public bool AddOrUpdateValue(string key, object value)
        {
            lock (_locker)
            {
                var typeOf = value.GetType();
                if (typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof (Nullable<>))
                {
                    typeOf = Nullable.GetUnderlyingType(typeOf);
                }
                var typeCode = Type.GetTypeCode(typeOf);
                switch (typeCode)
                {
                    case TypeCode.Boolean:
                        SharedPreferencesEditor.PutBoolean(key, Convert.ToBoolean(value));
                        break;
                    case TypeCode.Int64:
                        SharedPreferencesEditor.PutLong(key, Convert.ToInt64(value));
                        break;
                    case TypeCode.String:
                        SharedPreferencesEditor.PutString(key, Convert.ToString(value));
                        break;
                    case TypeCode.Int32:
                        SharedPreferencesEditor.PutInt(key, Convert.ToInt32(value));
                        break;
                    case TypeCode.Single:
                        SharedPreferencesEditor.PutFloat(key, Convert.ToSingle(value));
                        break;
                    case TypeCode.DateTime:
                        SharedPreferencesEditor.PutLong(key, ((DateTime) value).Ticks);
                        break;
                }
            }
    
            return true;
        }
    
        public void Save()
        {
            lock (_locker)
            {
                SharedPreferencesEditor.Commit();
            }
        }
    }
    
  • Thanks chase!

    I think it's strange that it works in Xamarin Studio but not in Visual Studio?

    I'm using PCLStorage to implement settings so maybe it's better to use your implenetation instead.

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai

    The non-mvvmcross version of settings is here: https://github.com/jamesmontemagno/Xam.PCL.Plugins/tree/master/Settings

  • @JamesMontemagno‌ Great! I'm using it now with success. Thanks!

  • InquisitorJaxInquisitorJax USMember

    I also got this error (windows phone project).
    Setup:
    Core PCL
    Win Phone App
    Win Phone Background Agent.

    The error happened on startup of the app - but the problem turned out to be that I hadn't added the nuget reference to the background agent project.

  • BobBanksBobBanks USMember, University ✭✭

    Thanks @BenoitPaquin‌! In case link rot occurs, the solution to problem is to set CopyLocal = False on the PCLStorage and PCLStorage.Abstractions references in your PCL project. Be sure to add the package to each platform-specific project.

  • MartinStievenartMartinStievenart USMember, University ✭✭

    I have the same issue with Windows Phone 8.1. Setting CopyLocal to false for the PclStorage package in the PCL project did not resolve it.
    Anyone having the same problem as me ?

Sign In or Register to comment.