Forum Xamarin Xamarin.Forms

What best storage system for timeline feeds?

NandaSamuderaNandaSamudera USMember
edited April 2017 in Xamarin.Forms

I want to make an apps that have timeline feature like twitter, instagram, or facebook. I have used them when there is no connectivity available, but app still show last feeds even when it's offline (I opened freshly open, not on the background).

My question is what is the best method to store last feeds on local? What's better using SQLite or PCL Storage? Or any other options?
Thanks in advance

Answers

  • PatKPatK USMember ✭✭
    edited April 2017

    Hi Nanda.
    Caused by the fact, that adding numerous NuGet packages can get a bit messy, I do store all application data using System.IO. If it comes to parsing, I'd recommend using JSON format (see http://www.newtonsoft.com/json).

    Inside your base project create an interface like:

    using System;
    using System.Collections.Generic;
    
    namespace You.YourApp
    {
        public interface IStringSerializer
        {
            void Save (string filename, string text);
    
            string Load (string filename);
    
            bool Exists (string filename);
    
            IEnumerable<string> GetFileNames ();
    
            void Delete (string filename);
        }
    }
    

    Following, implement it inside your concrete projects (iOS, Android etc.) like:

    using System;
    using You.YourApp;
    using Xamarin.Forms;
    using System.IO;
    using System.Collections.Generic;
    
    [assembly: Dependency (typeof (YourApp.iOS.StringSerializer))]
    namespace YourApp.iOS
    {
        public class StringSerializer : IStringSerializer
        {
            public void Save (string filename, string text)
            {
                try {
                    System.IO.File.WriteAllText (GetPath (filename), text);
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                }
            }
    
            public string Load (string filename)
            {
                return System.IO.File.ReadAllText (GetPath (filename));
            }
    
            public bool Exists (string filename)
            {
                try {
                    return System.IO.File.Exists (GetPath (filename));
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                    return false;
                }
            }
    
            private string GetPath (string filename)
            {
                return Path.Combine (Environment.GetFolderPath (
                    Environment.SpecialFolder.Personal), filename);
            }
    
            public IEnumerable<string> GetFileNames ()
            {
                try {
                    return System.IO.Directory.EnumerateFiles (
                        Environment.GetFolderPath (Environment.SpecialFolder.Personal));
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                    return new List<string> ();
                }
            }
    
            public void Delete (string filename)
            {
                try {
                    System.IO.File.Delete (GetPath (filename));
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                }
            }
        }
    }
    

    Finally you can access it using a class like this

    using System;
    using Xamarin.Forms;
    using System.Collections.Generic;
    
    namespace You.YourApp
    {
        public static class StringSerializer
        {
            private static IStringSerializer _native;
    
            static StringSerializer ()
            {
                _native = DependencyService.Get<IStringSerializer> (); 
            }
    
            public static void Save (string filename, string text)
            { 
                _native.Save (filename, text);
            }
    
            public static string Load (string filename)
            {
                return _native.Load (filename);
            }
    
            public static bool Exists (string filename)
            {
                return _native.Exists (filename);
            }
    
            public static IEnumerable<string> GetFileNames ()
            {
                return _native.GetFileNames ();
            }
    
            public static void Delete (string filename)
            {
                _native.Delete (filename);
            }
        }
    }
    

    As I'd said earlier, I recommend using JSON format. Get in touch with Newtonsoft JSON package. Following, this class might get handy for you:

    using System;
    using Newtonsoft.Json;
    
    namespace You.YourApp
    {
        public class JsonSerializerData<T>
        {
            public JsonSerializerData ()
            {
                Timestamp = 0;
                Filename = string.Empty;
            }
    
            [JsonProperty ("data")]
            public T Data { get; set; }
    
            [JsonProperty ("timestamp")]
            public long Timestamp { get; set; }
    
            [JsonIgnore]
            public DateTime DateTime {
                get {
                    return DateUtils.GetByUnixTimestamp (Timestamp);
                }
            }
    
            [JsonIgnore]
            public string Filename { get; set; }
    
            public void Save ()
            {
                Timestamp = DateUtils.UnixTimestamp;
                JsonSerializer.Save (this);
            }
    
            public void Delete ()
            {
                JsonSerializer.Delete (this.Filename);
            }
        }
    
        public static class JsonSerializer
        {
            public static void Save<T> (JsonSerializerData<T> data)
            {
                StringSerializer.Save (data.Filename, JsonConvert.SerializeObject (data));
            }
    
            public static JsonSerializerData<T> Load<T> (string filename)
            {
                var obj = JsonConvert.DeserializeObject<JsonSerializerData<T>> (
                              StringSerializer.Load (filename));
                obj.Filename = filename;
                return obj;
            }
    
            public static bool TryLoad<T> (string filename, ref JsonSerializerData<T> data)
            {
                try {
                    data = Load<T> (filename);
                    return true;
                } catch {
                    return false;
                }
            }
    
            public static bool Exists (string filename)
            {
                return StringSerializer.Exists (filename);
            }
    
            public static void Delete (string filename)
            {
                StringSerializer.Delete (filename);
            }
        }
    }
    

    You can use it like this:

    JsonSerializerData<User> _userData;
    
    [...]
    _userData = new JsonSerializerData<User> {
        Data = new User {
            Name = "PatK",
            Age = 11110b
        },
        Filename = "my_userdata.json"
    };
    _userData.Save ();
    
    [...]
    if (JsonSerializer.Exists (UserDataFilename)) {
        JsonSerializer.TryLoad<User> (UserDataFilename, ref _userData);
    //  var load = JsonSerializer.Load<User> (UserDataFilename);
    //  if (load != null) {
    //      _userData = load;
    //  }
    }
    

    BTW you can also easily de-/serialize collections using Newtonsoft JSON:

    JsonSerializerData<IDictionary<string, IList<Location>>> _locationData;
    
    public IDictionary<string, IList<Location>> Locations {
        get {
            lock (_lock) {
                return _locationData.Data;
            }
        }
        set {
            lock (_lock) {
                _locationData.Data = value;
                _locationData.Save ();
            }
        }
    }
    
    [...]
    if (JsonSerializer.Exists (LocationDataFilename)) {
        JsonSerializer.TryLoad<IDictionary<string, IList<Location>>> (
            LocationDataFilename, ref _locationData);
    } 
    
  • PatKPatK USMember ✭✭

    Hi Nanda,
    I prefer storing application data on the local filesystem using JSON formatted strings. Here is how I do solve this problem.

    First of all, create those two classes inside your base project:

    using System;
    using System.Collections.Generic;
    
    namespace YourApp.Forms
    {
        public interface IStringSerializer
        {
            void Save (string filename, string text);
    
            string Load (string filename);
    
            bool Exists (string filename);
    
            IEnumerable<string> GetFileNames ();
    
            void Delete (string filename);
        }
    }
    
    
    using System;
    using System.Collections.Generic;
    using Xamarin.Forms;
    
    namespace YourApp.Forms
    {
        public static class StringSerializer
        {
            private static IStringSerializer _native;
    
            static StringSerializer ()
            {
                _native = DependencyService.Get<IStringSerializer> (); 
            }
    
            public static void Save (string filename, string text)
            { 
                _native.Save (filename, text);
            }
    
            public static string Load (string filename)
            {
                return _native.Load (filename);
            }
    
            public static bool Exists (string filename)
            {
                return _native.Exists (filename);
            }
    
            public static IEnumerable<string> GetFileNames ()
            {
                return _native.GetFileNames ();
            }
    
            public static void Delete (string filename)
            {
                _native.Delete (filename);
            }
        }
    }
    

    Caused by the fact, that you only have access to System.IO inside your native app projects, you will need to implement the interfaces on all your the native app projects. There are none differences on iOS or Android:

    using System;
    using Xamarin.Forms;
    using System.IO;
    using System.Collections.Generic;
    using YourApp.Forms;
    
    [assembly: Dependency (typeof (YourApp.iOS.StringSerializer))]
    namespace YourApp.iOS
    {
        public class StringSerializer : IStringSerializer
        {
            public void Save (string filename, string text)
            {
                try {
                    System.IO.File.WriteAllText (GetPath (filename), text);
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                }
            }
    
            public string Load (string filename)
            {
                return System.IO.File.ReadAllText (GetPath (filename));
            }
    
            public bool Exists (string filename)
            {
                try {
                    return System.IO.File.Exists (GetPath (filename));
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                    return false;
                }
            }
    
            private string GetPath (string filename)
            {
                return Path.Combine (Environment.GetFolderPath (
                    Environment.SpecialFolder.Personal), filename);
            }
    
            public IEnumerable<string> GetFileNames ()
            {
                try {
                    return System.IO.Directory.EnumerateFiles (
                        Environment.GetFolderPath (Environment.SpecialFolder.Personal));
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                    return new List<string> ();
                }
            }
    
            public void Delete (string filename)
            {
                try {
                    System.IO.File.Delete (GetPath (filename));
                } catch (Exception ex) {
                    LogUtils.Error (this, ex.Message);
                }
            }
        }
    }
    

    So, now you can save strings to the local filesystem. Following you can easily de-/serialize your objects using Newtonsoft JSON package:
    public IEnumerable Locations { get; private set; }

    Locations = JsonConvert.DeserializeObject<List<Location>> (
        StringSerializer.Load("locations.json"));
    
    [...]
    StringSerializer.Save("locations.json", JsonConvert.Serialize(Locations));
    
  • PatKPatK USMember ✭✭
    edited April 2017

    Due to Xamarin Forum Server issues, my previous post got screwed up. Thus, sorry for the double post.

    Editing URl should be https://forums.xamarin.com/post/editcomment/265631
    It gives me a

    Permission Problem
    You don't have permission to do that.
    

    Anyways, I don't see any chance to edit / delete my secondary created post. Maybe a mod might accomplish this for me please.

Sign In or Register to comment.