CloudKit et Flutter... souci avec les double version Apple

Hello,

Je cherche à récupérer sur mon application universelle (iOS+Android, sous Flutter) une série de données qui étaient auparavant sur CloudKit (dans la version iOS seule).
J’ai trouvé le package cloudkit_flutter (pub.dev/packages/cloudkit_flutter) qui fait une grosse partie du travail assez proprement… mais il subsiste un souci qui vient plutôt côté Apple, je dirais : dans une colonne de données, calibrée en double, j’ai des nombres sans décimale qui ne passent donc pas le criblage de transformation appliqué à l’import : il attend des doubles, on lui donne des integer, il n’est pas d’accord :slight_smile:

Le code de la routine qui plante (dans le package, donc si j’y touche, ça va disparaître au prochain flutter pub get) :

/// Execute the record query operation.
  @override
  Future<CKOperationCallback> execute() async
  {
    CKOperationCallback apiCallback = await super.execute();

    List<T> newLocalObjects = [];
    if (apiCallback.state == CKOperationState.success)
    {
      await Future.forEach(apiCallback.response["records"], (recordMap) async {
        var newObject = CKRecordParser.recordToLocalObject<T>(recordMap as Map<String,dynamic>, database: _database);

        if (_shouldPreloadAssets) await CKRecordParser.preloadAssets<T>(newObject);

        newLocalObjects.add(newObject);
      });
    }

    return CKOperationCallback(apiCallback.state, response: newLocalObjects);
  }

et le message d’erreur :

C’est dommage parce que sinon, ça semblait être bien parti :

Si quelqu’un a une idée… je prends :wink:

Je suis remonté à la source, en copiant chaque fichier original dans un fichier « _fixed » pour toute l’arborescence qui mène jusqu’au ck_record_parser.dart et j’ai donc fait cela :

/// Convert a single CloudKit record field to a local value.
  static dynamic convertToLocalValue(CKFieldType field, dynamic rawValue,
      {CKDatabase? database}) {
     var convertedValue = rawValue;

    switch (field) {
      case CKFieldType.STRING_TYPE:
      case CKFieldType.INT_TYPE:
      case CKFieldType.DOUBLE_TYPE:
        // setter below will automatically work for these types; others might also automatically work (other types of lists?)

///// THIS PART IS A FIX FOR DOUBLE INPUTS THAT COME AS INTEGER FROM APPLE SIDE

        if (field == CKFieldType.DOUBLE_TYPE) {
          // print("-DEBUG-" + "convertToLocalValue - " + "step  2");
          if (rawValue is int) {
            rawValue = rawValue.toDouble();
          }
        }

///// END OF  FIX

        convertedValue = rawValue;
        break;

[...]

    }
}

Ça marche bien, mais ce n’est pas satisfaisant de devoir faire ce genre de fix : je le soumets donc au dév.

Pour info, si vous utilisez le package, il y a un autre fix nécessaire si vous voulez faire des recherches avec des filtres sur des champs non automatiques CKConstants.isSystemFieldName == false
Je l’ai également soumis au dév, mais du. coup, ça. commence à faire pas mal de fix en local :frowning:

class CKFilter {
  final CKComparator _comparator;
  final String _fieldName;
  final Map<String, dynamic> _fieldValueDictionary;
  final double? _distance;

  ///// THIS PART IS A FIX FOR NON-SYSTEMFIELD FILTERS

  dynamic _fieldValue;

  CKFilter(this._fieldName, CKFieldType fieldType, dynamic fieldValue,
      this._comparator,
      {double? distance})
      : _fieldValue = (fieldValue != null)
            ? {'value': fieldValue, 'type': fieldType.record}
            : ((fieldType is num) ? 0 : ""),
        _fieldValueDictionary = {
          'value': {_fieldName: fieldValue},
          'type': fieldType.record
        },
        _distance = distance;

  /// Convert the filter to JSON.
  Map<String, dynamic> toJSON() => {
        'comparator': _comparator._comparatorString,
        (CKConstants.isSystemFieldName(_fieldName)
            ? 'systemFieldName'
            : 'fieldName'): _fieldName,
        'fieldValue': (CKConstants.isSystemFieldName(_fieldName)
            ? _fieldValueDictionary
            : _fieldValue),
        'distance': _distance
      };

  ///// END OF FIX
}

au lieu de :

class CKFilter
{
  final CKComparator _comparator;
  final String _fieldName;
  final Map<String,dynamic> _fieldValueDictionary;
  final double? _distance;

  CKFilter(this._fieldName, CKFieldType fieldType, dynamic fieldValue, this._comparator, {double? distance}) : _fieldValueDictionary = {'value': {_fieldName: fieldValue}, 'type': fieldType.record}, _distance = distance;

  /// Convert the filter to JSON.
  Map<String, dynamic> toJSON() => {
    'comparator': _comparator._comparatorString,
    (CKConstants.isSystemFieldName(_fieldName) ? 'systemFieldName' : 'fieldName'): _fieldName,
    'fieldValue': _fieldValueDictionary,
    'distance': _distance
  };
}

Morale de cette histoire :

quand Maxime recommande d’éviter les solutions propriétaires pour stocker des données, il vaut mieux l’écouter

:grin:

Je te taquine, mais ça montre les risques d’utiliser un webservice qu’on ne contrôle pas. Cela dit, je m’y suis fait prendre aussi avec Realm Platform et c’est pour ça que maintenant je suis plus prudent

J’avoue que je n’avais pas vu les choses sous cet angle-là dans le cas présent… mais en effet :slight_smile:
Ça a déjà été une lourdeur au quotidien à gérer dans la version native iOS, donc content de m’en affranchir dans la version flutter.
Mais l’exercice de mettre les mains dans un package flutter pour le faire mieux fonctionner était assez intéressant, formateur même :slight_smile:

C’est vrai qu’un webservice c’est souvent du travail et ça n’est pas le plus simple à gérer.
Mais créer soi-même son webservice n’est pas beaucoup plus compliqué qu’apprendre à utiliser celui de quelqu’un d’autre (CloudKit, Firebase, MongoDB, etc.) et au moins on reste indépendant.

C’est aussi pour ça que je vous parle de Directus depuis quelques temps : c’est un très bon compromis avec un outil tout prêt, mais comme on le gère nous même on n’est pas prisonnier d’un hébergeur qui fixe les règles en termes de fonctionnalités et de tarifs.