CosmicMind Graph + iCloud and Today Extension - graph

I have an app based on cosmicmind graph database published on the AppStore.
It saves data locally in the shared sandbox, in order to share the database with the today extension.
In the new update I'm trying to add CloudKit feature and it works great, but I have two problems.
I'd like that users who already have the app could sync data that are stored locally in the iCloud database, but I don't really know how to do it.
The second problem is the today extension. Since the cloud data are stored in a second SQLite file, the extension that was working perfectly before, now returns an empty array.
Any help will be really appreciated!
Thank you
EDIT
Thank you for you fast answer.
This is the code I used locally in my datamanager:
func startDataManager() {
let search = Search<Entity>(graph: databaseManager2).for(types: "ElencoItem")
elencoItems = search.sync(completion: nil)
}
and this is the code I use now with icloud:
func startDataManager() {
databaseManager = Graph(cloud: "MyContainer", completion: { (done, error) in
if done {
debugPrint("ok")
let search = Search<Entity>(graph: self.databaseManager).for(types: "ElencoItem")
self.elencoItems = search.sync()
} else {
guard let error = error else { return }
debugPrint("error iCloud: \(error.localizedDescription)")
}
})
I also added all delegate methods and it works perfectly.
I solved the issue with today extension.
The problem was that the extension didn't see the array saved for cloud version. I saved like this:
let nuovo = Entity(type: "ElencoItem", graph: DataManager.shared.databaseManager)
nuovo["value"] = value
Now I save cloud data to a new array like:
let search = Search<Entity>(graph: DataManager.shared.databaseManager).for(types: "ElencoItem")
for item in search.sync() { {
let nuovo = Entity(type: "ElencoItem")
nuovo["value"] = item["value"]
DataManager.shared.elenco.append(nuovo)
DataManager.shared.databaseManager2.sync()
}
and the extension works great.
I'm trying now to migrate data saved locally to cloud but it doesn't work.
Data were saved locally like this:
let nuovo = Entity(type: "ElencoItem")
nuovo["value"] = value
I do the same as above to move data to cloud save like:
let search = Search<Entity>(graph: self.databaseManager10).for(types: "ElencoItem")
for item in search.sync() {
let nuovo = Entity(type: "ElencoItem", graph: DataManager.shared.databaseManager)
nuovo["value"] = item["value"]
DataManager.shared.elencoItems.append(nuovo)
DataManager.shared.databaseManager.sync()
print(DataManager.shared.elencoItems)
}
When I print the array after the for loop I can see all data, but after closing the app I can't find data anymore.
Any solution?
Thanks

So Graph has a sync mechanism with iCloud. If you use that, you would only need to write a migration function to move your data over, and then update your app to tap into the iCloud API. For the Today extension... not sure what that is.

Problem solved.
I moved the migration function from the datamanager to the first viewcontroller loaded, removed the print statement and it works perfectly!

Related

What does "new[]" mean with ReactiveCommand C# and how does this code execute?

I am creating an application using Firebase Firestore and Plugin.CloudFirestore for Xamarin Forms based off of the Sample Project posted on the plugin's GitHub (link:
https://github.com/f-miyu/Plugin.CloudFirestore).
I am trying to learn how to write Async Reactive Commands based off of how they are written in the sample project but I'm having a really hard time reverse engineering this code.
UpdateCommand = new[] {
_todoItem.Select(x => x == null),
Name.Select(x => string.IsNullOrEmpty(x))
}.CombineLatestValuesAreAllFalse()
.ObserveOn(SynchronizationContext.Current)
.ToAsyncReactiveCommand();
UpdateCommand.Subscribe(async () =>
{
var todoItem = _todoItem.Value;
todoItem.Name = Name.Value;
todoItem.Notes = Notes.Value;
_ = CrossCloudFirestore.Current.Instance.Document($"{TodoItem.CollectionPath}/{todoItem.Id}")
.UpdateAsync(todoItem)
.ContinueWith(t =>
{
System.Diagnostics.Debug.WriteLine(t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
await navigationService.GoBackAsync();
});
This code snippet is from the "TodoItemDetailPageViewModel.cs" file within the sample project if you would like more context.
The code above updates the field values of the document that is selected from the main page via the Firestore API. The code is working but I don't have any idea what the above code means and how the command works. Could someone break it down for me and explain what is going on? I am sure someone else could benefit as well.
Thanks

Is it a good / working practice to use Firebase's documentReference.get(GetOptions(source: cache)) in Flutter?

My issue was, that with the default GetOptions (omitting the parameter), a request like the following could load seconds if not minutes if the client is offline:
await docRef.get()...
If I check if the client is offline and in this case purposefully change the Source to Source.cache, I have performance that is at least as good, if not better, than if the client was online.
Source _source = Source.serverAndCache;
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
_source = Source.serverAndCache;
}
} on SocketException catch (_) {
_source = Source.cache;
}
and then use this variable in the following way:
docRef.get(GetOptions(source: _source))
.then(...
This code works perfectly for me now, but I am not sure, if there are any cases in which using the code like this could raise issues.
Also it seems like a lot of boilerplate code (I refactored it into a function so I can use it in any Database methods but still...)
If there are no issues with this, why wouldn't this be the Firebase default, since after trying the server for an unpredictably long time it switches to cache anyways.

problem in Flutter with Sqflite exception

I have a problem with my code, I wrote simple flutter app which is f note app, and I have included SQLite as a database , I run the app at first via the emulator and everything went cool , but when I tried to run it on my real device (which is an android device), the database did not respond (i.e I could not add new notes to the database ) and when I went back to run my app via the emulator .. the app did the same thing I found in my real device and in console I found this error
Error: Can't use 'SqfliteDatabaseException' because it is declared more than once.
I need help Please
I saw your code and the problem is that the exception you get is probably related to this one:
PlatformException(sqlite_error, UNIQUE constraint failed: Notetable.id
And it's because you need to manage the unicity of your primary key when you insert a new row. You can have a look at this SO question for a quick reference.
So just to quickly make your code working I've made this changes (please take this code only for reference, write a better one):
void createDataBase(Database db,int newVersion) async{
await db.execute('CREATE TABLE IF NOT EXISTS $noteTable ($col_id INTEGER PRIMARY KEY ,'+
'$col_title TEXT , $col_description TEXT , $col_date TEXT,$col_priority INTEGER)');
}
And
Future<int> insertData(myNote note)async{
var mdatabase = await database;
var _newNoteMap = note.convertToMap();
_newNoteMap['id'] = null;
var result = await mdatabase.insert(noteTable, _newNoteMap);
return result;
}
Pay attention that you always call a DB insert even when you update an existing note.
UPDATE: added additional modification (not listed before)
in databaseObject.dart
Map<String,dynamic> convertToMap(){
var mapObject = Map<String,dynamic>();
mapObject["id"] = _id;
mapObject["title"] = _title;
mapObject["description"] = _description;
mapObject["date"] = _date;
mapObject["priority"] = _priority;
return mapObject;
}
in Note.dart
if(res >= 1){
showAlertDialog('Status', "New note added successfully and the value of result is $res");
}

Cloud Functions database event always contains empty data

I have an issue with my cloud functions where in all my database events all return empty. For example, in the following event the event.data.val() would return null. I am doing an update operation and have tested the update by testing the cloud function using the shell as well as after deploying.
export const createSubscription = functions.database.ref('/users/{userId}/subscription').onWrite( event => {
if(!event.data.val()) {
return;
}
});
But I can easily hook into the auth.user() events like the following and receive the data.
export const createStripeUser = functions.auth.user().onCreate(event => {
const user = event.data;
});
Edit: Passing data into the collection for example like the one below on the emulator console
createSubscription({
testKey: 'testValue'
})
or the following on from my frontend
db.ref(`/users/23213213213/subscription`).update({ testKey: 'testValue'});
would return null on the function.
DougStevenson is correct. For the .onCreate() you would be doing it correct wit a myDatabaseFunction('new_data').
With the .onWrite() you need to pass in the before and after like my example below.
You may have got stuck were I did. Note that I have a curly bracket around the before and after the final JSON. It didn't work properly without them.
addComment({before: {"comment":"before comment","role":"guest"}, after:{"comment":"After Comment","role":"guest"}})
Hope my example helps a bit more than a generic string parameter.
Good Luck!

Cloud Functions for Firebase: onWrite() executed without new data

I use Cloud Functions for Firebase for some server-side-tasks. Using the database-trigger onWrite() I experience some unexpected behaviour.
exports.doStuff = functions.database.ref('/topic/{topicId}/new').onWrite((event) => {
// If data, then continue...
if (event.data.val()){
// doStuff
//
} else {
console.log("started, but no content!");
}
When new data is added to the specified folder the function is started at least once without any new content ("started, but no content!" is logged to the console). Sometimes even two, three or four times. Then it's run again, automatically (a couple of seconds later) and this time everything works as expected.
EDIT:
The code that writes to the specified node is as follows:
functionA(topicId){
return this.db.object('/topic/'+topicId+'/new').update({
timestamp: firebase.database.ServerValue.TIMESTAMP
});
}
The timestamp is only set once. So before that operation is called, the node new does not exist. So there is no edit or delete. However, this means, by calling the above function first the node new is created, some miliseconds later timestamp and then the value for timestamp. Does Firebase call the onWrite() function for each of these events?
Does this make sense to anybody? Any idea how to make sure, that the function is only executed, when there is really new data available?
onWrite() has a new format, since version 1.0 of Firebase SDK
Your original code was based on tutorials or help from an earlier, beta, version of Firebase.
exports.doStuff = functions.database.ref('/topic/{topicId}/new')
.onWrite((event) => {
if (event.data.val()){
// do stuff
}
}
However since version 1.0, the first parameter in the onWrite function is a "change" object which has two properties: before and after. You want the after. You can get it as follows:
exports.doStuff = functions.database.ref('/topic/{topicId}/new')
.onWrite((change) => {
if (change.after.val()){
// do stuff
}
}
Source: https://firebase.google.com/docs/reference/functions/functions.Change

Resources