Flutter: Provider: How to handle model dependencies? - flutter

I'm looking for suggestions on how to handle loading things after login, and combine data from multiple endpoints without mixing too much stuff, specifically the following two things:
1) Login flow
After I get a successful login response with userId, I need to push HomeScreen() and load some initial data, from various providers.
Example:
// home_screen.dart
initState() {
super.initState();
initializeData();
}
Future <void> initializeData() {
var authenticationProvider = Provider.of<AuthenticationProvider>(context);
var accountProvider = Provider.of<AccountProvider>(context);
var albumProvider = Provider.of<AlbumProvider>(context);
var songProvider = Provider.of<SongProvider>(context);
await accountProvider.loadAccount(authenticationProvider.getLoggedInUser().id);
await albumProvider.loadAlbums();
await songProvider.loadSongsForAlbums(albumProvider.getAlbums())
}
This works, but feels ugly?
2) Cleaner data sharing
Imagine the API like this:
api/albums (model contains info about album)
api/albumpurchases (model contains which albumId and userId)
What would be the best way to get purchased albums of the logged in user?
I can think of 3 different ways, none of which seem good:
AlbumProvider having two arrays, albums[], and purchases[], and a method getAlbumByPurchase (String purchaseId) or getPurchaseForAlbum(String albumId) which then does .where() by Id and returns the item.
Having AlbumProvider and AlbumPurchaseProvider, then using ProxyProvider to combine the two.
I'm not sure how exactly would that be implemented, an example would be very appreciated!
Adding purchase property to an Album, then manually mapping it similarly to way #1
.
I've used this so far, and it seems great in the beginning but gets very ugly very quick since your subsequent Album HTTP responses everywhere would be lacking that purchase property, so I either need to get the purchase again, or I need to get it an Album object with the purchase from AlbumProvider based on an Id in response I got.

Related

How to write the query function for checking duplicate registereD in Appwrite using Flutter

I'm a new intern at this small tech company that uses appwrite as a database for developing mobile applications using flutter. The task was to check if there are duplicate registration IDs in Appwrite database and, if there are, notify users that the ID already exists and ask them to enter a different registration ID when completing their user profile. The query function for checking duplicate IDs is proving to be a challenge for me because I'm a newbie to flutter and appwrite.  
It first checks whether the registryID parameter is successfully received, and returns the registration ID under the registryID column in the Appwrite document, but when printing out the result, it returns an empty map. So I believe I somehow wrote the function incorrectly.
Future<dynamic> checkDuplicateID(String registerID) async{
try {
dynamic res = await db.listDocuments(
collectionId: kycCollectionId,
queries: [
Query.equal('registryId', registerID),
]
);
} on AppwriteException catch(e) {
print(e.toString());
}
}
here is the appwrite image that contains document information and registryID row
In submit button section where the user submits her information, I used a provider package and called the checkDuplicateID method and passed the id "UKH00250238", which is repeated twice in the database.
onTap: () {
dynamic result = state.checkDuplicateID('UKH00250238');
}
If the above function is incorrect, how do I write a function in which I can pass a registerID as a parameter and check if the id is already repeated? If my implementation is incorrect, what are the other ways to check duplicate IDs in the Appwrite?
Your checkDuplicateID() function should probably return something to indicate whether there's a duplicate or not. Otherwise, the function seems fine, assuming the user has access to the data.
Your next step is probably to have some sort of UI to collect input from the user so that you can pass it into your checkDuplicateID() function. The Flutter Docs have plenty of resources you can use, like this.
If you still need help from the Appwrite size, feel free to join the Appwrite Discord server.

How to use a single IAP product for multiple items with package:in_app_purchase in Flutter

I'm currently developing an app with thousands of items that users can buy.
What I'd like to do
There are a large number of items and all of them have the same price, so I think it'd be better to use only one IAP product for those different items.
What I won't do
Another solution is to sell credits used to exchange for items.
I won't choose this solution unless it is the only option because I doubt it gives a good user experience.
What I already know
The items are actually not consumed (they remain in the app forever), but because a non-consumable product can be purchased only once, a consumable needs to be used instead.
The purchased items must be stored in a server to be shared across devices since Google and Apple don't provide the mechanism for restoring consumables.
Question
Below is a simplified version of the code from the Codelab.
The data of the item that a user is about to purchase has to be passed from [a] to [b], but it looks impossible. How can I do it instead?
Is it only that the Flutter package lacks the feature while the native side has a support for it? (Should I file an issue about it on GitHub?)
class DashPurchases {
/// A method that is called when a user presses a purchase button.
Future<void> buy(PurchasableProduct product) async {
// [a]
// Neither PurchaseParam nor buyConsumable() has a parameter/property
// to hold additional data like the item that a user is about to buy.
final purchaseParam = PurchaseParam(productDetails: product.productDetails);
await iapConnection.buyConsumable(purchaseParam: purchaseParam);
}
/// A callback function that is called when the purchase is finished, cancelled, etc.
Future<void> _onPurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) async {
for (var purchaseDetails in purchaseDetailsList) {
await _handlePurchase(purchaseDetails);
}
}
Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
if (purchaseDetails.status == PurchaseStatus.purchased) {
final validPurchase = await _verifyPurchase(purchaseDetails);
if (validPurchase) {
// [b]
// The item should be given to the user here, but I have
// no idea how to identify the item that the user purchased.
}
}
if (purchaseDetails.pendingCompletePurchase) {
await iapConnection.completePurchase(purchaseDetails);
}
}
}
I came up with the idea of adding the item to a queue (local DB or somewhere) at [a] and taking it out at [b].
However, it has several problems as follows.
It is quite cumbersome to track which item is pending or make the code robust. The purchase ID has to be added to the stored data to make it associated with the item as soon as _handlePurchase() is called so that the function can be retried after some failure in the backend.
purchaseDetails has no purchase ID on PurchaseStatus.error. It is difficult to tell whether an item with no associated ID is the result of an error or it is still just pending.
Multiple purchase details may be passed to _onPurchaseUpdate() in a different order than the actual purchases.
Resolving these will lead to very complicated code, and it doesn't seem very safe either.

How to add new data into Future<dynamic> in dart / flutter?

I am developing a chat system in mobile application using Flutter / Dart.
I have fetched a user's message records from server by API and received result as Future<dynamic>. This result has list of chat data. If I pass it to FutureBuilder widget. It works and lists chat records in listTile. Everything is working well.
When user adds a new chat message then I post the that text message to server by API to store into database. It works and response status is 200 which means message has been added on server's database.
I have instance of newly added chat message, I want to append / add it to previously fetched Future<dynamic> result.
Kindly suggest me how can I do this? Can we update Future<dynamic> typed data? Thanks.
Future can emit only one item by design. If you want to emit multiple items, use Stream: https://api.flutter.dev/flutter/dart-async/Stream-class.html
To get to know how to generate streams have a look on this: https://dart.dev/articles/libraries/creating-streams
Most likely what you want to do is use rxdart's BehaviorSubject or dart's StreamController (they share api, so just substitute the name, except for ValueStream, which is specific to rxdart, this one will have to be replaced with just Stream):
class Api {
final _subject = BehaviorSubject<DataToDisplay>();
ValueStream<DataToDisplay> get data => _subject.stream;
void fetchData() {
final data = downloadDataFromSomewhere();
_subject.add(data);
}
}
Then just create a StreamBuilder similarly to FutureBuilder.

Building user database model in Firebase

so I already finished all of the actual app for this. I just need to setup the backend. I figured Firebase was the best solution since Parse is no longer a thing. What I wanted was:
Users with profiles - These profiles can be viewed by added friends but only edited (written) to by the actual profile owner.
So I read through the Firebase Docs and still cannot really figure out how to do this. They only have 1 Swift application example that does not do anything similar and the one Obj C twitter one, will not even build. All of their docs still have println for Swift which just makes me think it is not updated frequently.
Does anyone have any good examples / tutorials of this? I keep trying to search for things but nothing is as similar enough to what I want. I am more looking on how to setup the db for each user and access it rather actually using Firebase in Swift.
As I wrote in my comment to your question, this answer is based on what we do in a real social app Impether using Swift + Firebase.
Data structure
Let's assume that you want to store the following information for a single user:
email
username
name
followers - number of people who follow a particular user
following - number of people who a particular user follows
avatar_url - url of their avatar
bio - some additional text
Since in Firebase everything is stored a JSON objects, you can store the above structure under node with path like users/$userId, where $userId is Firebase User UID which is created for each registered user if you use simple email/password Firebase authorization.
Firebase email/password authorization is described in their docs:
https://www.firebase.com/docs/ios/guide/user-auth.html
https://www.firebase.com/docs/ios/guide/login/password.html
Notice that there are both Obj-C and Swift snippets. I find Firebase documentation really great as it helped me a lot when I was building our app.
For the purpose of this answer let's assume that we have user with username jack and Firebase User UID equal to jack_uid (in reality this will be a string generated by Firebase).
Then an example data for this user will be store under a path users/jack_uid and can look like this:
{
"email" : "jack#example.com",
"username" : "jack",
"name" : "Jack",
"followers" : 8,
"following" : 11,
"avatar_url" : "http://yourstoragesystem.com/avatars/jack.jpg",
"bio" : "Blogger, YouTuber",
}
Firebase email/password authorization works really well, but let's be honest, if user wants to sign in into the app, it's a lot better for him to use his username than his email he gave while he registering his account.
In order to do that, we decided to store a mapping from usernames to user ids. The idea is that if user inputs his username and password in a login form, we use that mapping to retrieve his user id and then we try to sign him in using his user id and provided password.
The mapping can be stored for example under a path username_to_uid and looks like this:
{
"sample_username_1": "firebase_generated_userid_1",
"sample_username_2": "firebase_generated_userid_2",
...
"jack": "jack_uid",
"sample_username_123": "firebase_generated_userid_123"
}
Then creating a profile may looks like this and it's done as soon as registration of a new account was successful (this snippet is very close to the exact code we use in the production):
func createProfile(uid: String, email: String,
username: String, avatarUrl: String,
successBlock: () -> Void, errorBlock: () -> Void) {
//path to user data node
let userDataPath = "/users/\(uid)"
//path to user's username to uid mapping
let usernameToUidDataPath = "/username_to_uid/\(username)"
//you want to have JSON object representing user data
//and we do use our User Swift structures to do that
//but you can just create a raw JSON object here.
//name, avatarUrl, bio, followers and following are
//initialized with default values
let user = User(uid: uid, username: username, name: "",
avatarUrl: avatarUrl, bio: "",
followers: 0, following: 0)
//this produces a JSON object from User instance
var userData = user.serialize()
//we add email to JSON data, because we don't store
//it directly in our objects
userData["email"] = email
//we use fanoutObject to update both user data
//and username to uid mapping at the same time
//this is very convinient, because either both
//write are successful or in case of any error,
//nothing is written, so you avoid inconsistencies
//in you database. You can read more about that technique
//here: https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html
var fanoutObject = [String:AnyObject]()
fanoutObject[userDataPath] = userData
fanoutObject[usernameToUidDataPath] = uid
let ref = Firebase(url: "https://YOUR-FIREBASE-URL.firebaseio.com/images")
ref.updateChildValues(fanoutObject, withCompletionBlock: {
err, snap in
if err == nil {
//call success call back if there were no errors
successBlock()
} else {
//handle error here
errorBlock()
}
})
}
In addition to this you possibly want to store for each user a list of his followers and a separate list of users he follows. This can be done just by storing user ids at a path like followers/jack_uid, for example it can look like this:
{
"firebase_generated_userid_4": true,
"firebase_generated_userid_14": true
}
This is the way we store sets of values in our app. It very convenient, because it is really user to update it and check if some value is there.
In order to count the number of followers, we put this counter into user's data directly. This makes reading the counter very efficient. However, updating this counter requires using transactional writes and the idea is almost exactly the same as in my answer here: Upvote/Downvote system within Swift via Firebase
Read/write permissions
A part of your question is how to handle permissions to data you store. The good news is that Firebase is exceptionally good here. If you go to your Firebase dashboard there is a tab named Security&Rules and this is the place where you control permissions to your data.
What's great about Firebase rules is that they are declarative, which makes them very easy to use and maintain. However, writing rules in pure JSON is not the best idea since it's quite hard to control them when you want to combine some atomic rules into a bigger rule or your app simple grows and there are more and more different data you store in your Firebase database. Fortunately, Firebase team wrote Bolt, which is a language in which you can write all rules you need very easily.
First of all I recommend to read Firebase docs about Security, especially how does permission to a node influences permission for its children. Then, you can take a look at Bolt here:
https://www.firebase.com/docs/security/bolt/guide.html
https://www.firebase.com/blog/2015-11-09-introducing-the-bolt-compiler.html
https://github.com/firebase/bolt/blob/master/docs/guide.md
For example, we use rules for managing users data similar to this:
//global helpers
isCurrentUser(userId) {
auth != null && auth.uid == userId;
}
isLogged() {
auth != null;
}
//custom types, you can extend them
//if you want to
type UserId extends String;
type Username extends String;
type AvatarUrl extends String;
type Email extends String;
type User {
avatar_url: AvatarUrl,
bio: String,
email: Email,
followers: Number,
following: Number,
name: String,
username: Username,
}
//user data rules
path /users/{$userId} is User {
write() { isCurrentUser($userId) }
read() { isLogged() }
}
//user's followers rules
//rules for users a particular
//user follows are similar
path /followers/{$userId} {
read() { isLogged() }
}
path /followers/{$userId}/{$followerId} is Boolean {
create() { isCurrentUser($followerId) && this == true }
delete() { isCurrentUser($followerId) }
}
//username to uid rules
path /username_to_uid {
read() { true }
}
path /username_to_uid/{$username} is UserId {
create() { isCurrentUser(this) }
}
The bottom line is that you write rules you want using Bolt, then you compile them into JSON using Bolt compiler and then you deploy them into your Firebase, using command line tools or by pasting them into dashboard, but command line is way more efficient. A nice additional feature is that you can test your rules by using tools in Simulator tab in your dashboard.
Summary
For me Firebase is a great tool for implementing a system you want. However, I recommend to start with simple features and learn how to use Firebase in the first place. Implementing social app with functionality like for example Instagram is quite a big challenge, especially if you want to do it right :) It's very tempting to put all functionality there very quickly and Firebase makes it relatively easy to do, but I recommend to be patient here.
In addition, take your time and invest in writing tools. For example, we have two separated Firebase databases, one for production and second for testing, which is really important if you want to write unit and UI tests efficiently.
Also, I recommend building permission rules from the beginning. Adding them later may be tempting, but also quite overwhelming.
Last but not least, follow Firebase blog. They post regularly and you can be up to date with their latest features and updates - this is how I learnt how to use concurrent writes using fanout technique.

Data Security using Entity Framework

What options/solutions are there for securing data using Entity Framework?
I do not talk about forms login and such here, just assume that the users are authenticated or not.
To illustrate, i attached one of my web api controllers and i wonder if this is the way to do it. The reason why i ask is that i wonder if there are easier ways to do this than writing all this logic to what data to expose in all my controllers.
Also, when looking into a system like breezejs and odata where i can add $expand=TrafficImages to my queries, i would not want users to be able to get my hole database.
So to summarize, what ways are there to securing the data exposed such the users cant download sensible data.
[AllowAnonymous]
public object GetTheoryTests()
{
var identity = ((ClaimsIdentity)((ClaimsPrincipal)HttpContext.Current.User).Identity);
//if (HttpContext.Current.User.Identity.IsAuthenticated)
if (!identity.IsAuthenticated)
return db.TheoryTests.Include(t=>t.TrafficImages).Where(t=>t.PublicAvalible)
.Select(t => new { Id = t.Id, Title = t.Title, Images = t.TrafficImages }).AsEnumerable();
if (User.IsInRole("WebAdmins"))
return db.TheoryTests.AsEnumerable();
var key = identity.GetProvider();
var member = db.Members.Include(m=>m.PayedTheoryTests).SingleOrDefault(m=>m.Identities.Any(
i=>i.identityprovider == key.provider &&
i.nameidentifier == key.id));
if(member!=null)
return db.TheoryTests.Include(t => t.TrafficImages).Where(t => t.PublicAvalible).Select(t => new { Id = t.Id, Title = t.Title, Images = t.TrafficImages }).AsEnumerable();
else
return db.TheoryTests.Include(t => t.TrafficImages).Where(t => t.PublicAvalible)
.Union(member.PayedTheoryTests).Select(t => new { Id = t.Id, Title = t.Title, Images = t.TrafficImages }).AsEnumerable();
}
When thinking about it, what i miss is something like a viewmodel untop of my database depending on the state of the user. Would it be a solution to create two entity frameworks ontop of the same database, one for limited data display and one for more advanced operations?
Meanwhile, until QueryInterceptors arrive, you should take other steps. First, you should look into the techniques for securing a Web API controller or method, a subject beyond the scope of this answer.
Second, w/r/t $expand, you are quite right to be wary of that feature. You may want to inspect which expansions are requested for some controller methods and/or disallow it altogether for others.
Fortunately, this is relatively easy to do. You have access to the request query string. You can detect the presence of "$expand" in that string and analyze it if you want to allow certain expansions and forbid others.
Breeze will add helpers for this in future. You'll have to process the string until then.
You may want to create your own action filter for this purpose if you're up to it.
Great question!. We are currently working on something called QueryInterceptors that will allow you to examine and possibly change or reject the query that was submitted to the server. The "Principal" would be a available context object within each QueryInterceptor method. Please vote for this feature on the "Breeze" website at www.breezejs.com.