Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disk Cache with Generics #37025

Closed
insinfo opened this issue May 20, 2019 · 4 comments
Closed

Disk Cache with Generics #37025

insinfo opened this issue May 20, 2019 · 4 comments
Labels
type-question A question about expected behavior or functionality

Comments

@insinfo
Copy link

insinfo commented May 20, 2019

I am a programmer who comes from JavaScript and PHP, and am developing an App on Flutter, and am having difficulty implementing a cache on the phone's internal storage.

I would like to understand and know if it is possible to create a Generic type class with serialization for JSON so that it can be stored on file.
I've done the Cache implementation in memory and it works fine, plus the implementation of Cache on Disk I'm having difficulty.

How to call the serialization method from a Generic

class DiskCache<T> extends Cache<T> {
 
  Duration cacheValidDuration = Duration(minutes: 30);
  DateTime lastFetchTime = DateTime.fromMillisecondsSinceEpoch(0);
  RList<T> allRecords = RList<T>();
  
  //is update the cache 
  bool isShouldRefresh() {
    return (null == allRecords ||
        allRecords.isEmpty ||
        null == lastFetchTime ||
        lastFetchTime.isBefore(DateTime.now().subtract(cacheValidDuration)));
  }

  @override
  Future<RList<T>> getAll() async {
    await _readFromDisk();
    return Future.value(allRecords);
  }

  @override
  Future<void> putAll(RList<T> objects) async {
    allRecords.addAll(objects);
    lastFetchTime = DateTime.now();
    await _writeToDisk();
  }

  @override
  Future<void> putAllAsync(Future<RList<T>> objects) async {
    allRecords = await objects;
    lastFetchTime = DateTime.now();
    await _writeToDisk();
  }

  //parth
  Future<String> get _localPath async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path;
  }

  //file pointer
  Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/allRecords.json');
  }

  //write to file
  Future<File> _writeToDisk() async {
    try {
      File recordedFile;
      if (allRecords != null) {
        var map = allRecords.map((c) {
          var item = c as ISerialization;
          return item.toJson();
        }).toList();

        var jsonString = jsonEncode(map);

        final file = await _localFile;
        // Write the file
        recordedFile = await file.writeAsString(jsonString);
      }
      return recordedFile;
    } catch (e) {
      print("writeToDisk: " + e.toString());
      return null;
    }
  }
// ************************** issues on this part ******************
  Future<RList<T>> _readFromDisk() async {
    try {
      final file = await _localFile;

      // Read the file
      String contents = await file.readAsString();
      var parsedJson = jsonDecode(contents);
      
      if (allRecords == null) {
        allRecords = RList<T>();
      }
      allRecords.clear();

      for (var item in parsedJson) {
       // ************************** issues on this part ******************
        print(T.fromMap(item));
        allRecords.add(T.fromMap(item));
      }
   
      return allRecords;
    } catch (e) {
      print("readFromDisk: " + e.toString());
      return null;
    }
  }
}
@lrhn
Copy link
Member

lrhn commented May 23, 2019

You cannot call static methods on type parameters.
Since there is no interface for static members, there is nothing ensuring that whatever T is bound to has a fromMap method at all.

The solution is to pass the function to the class constructor along with the type:

class DiskCache<T> extends Cache<T> {
   ....
   final T Function(Map<String, dynamic>) _fromMap;
   DiskCache(T fromMap(Map<String, dynamic>)) : _fromMap = fromMap;
   ...
        for (var item in parsedJson) {
          allRecords.add(_fromMap(item);
        }
   ...
}

If you don't know the type where the cache is created (say, if T is a type parameter there too) then I'm afraid there is no simple solution.

@lrhn lrhn added the type-question A question about expected behavior or functionality label May 23, 2019
@eernstg
Copy link
Member

eernstg commented May 23, 2019

See also recent discussions in dart-lang/language#356, which got into the same topic area.

@insinfo
Copy link
Author

insinfo commented May 28, 2019

Serialization is a key part in almost all applications nowadays, I find it fundamental to dart / flutter implement a native serialization, similar or that kotlin did with a serialization plugin for the compiler which avoids the overhead of reflection at runtime , more invisibly or transparently to the programmer facilitates a lot of boilerplate code

Kotlin serialization consists of a compiler plugin, which automatically produces visitor code for classes, and runtime library, which uses generated code to serialize objects without reflection.

Supports Kotlin classes marked as @serializable and standard collections.
Supports JSON, CBOR, and Protobuf formats out-of-the-box.
The same code works on Kotlin/JVM, Kotlin/JS and Kotlin/Native

https://github.com/Kotlin/kotlinx.serialization

or a light reflection focused on serializing to json

@vsmenon
Copy link
Member

vsmenon commented May 31, 2019

You may also want to look at built_value or json_serializable:

https://flutter.dev/docs/development/data-and-backend/json

@vsmenon vsmenon closed this as completed May 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-question A question about expected behavior or functionality
Projects
None yet
Development

No branches or pull requests

4 participants