Current_User (Slice) - How to conform your app around WHO is using the app

One of the core basic functions, included in just about EVERY app I make, is the ability for the app to know WHO is using the app.

If the app knows who’s using it, then I can easily control many different aspects of the app:

  • Add/edit/delete permissions
  • How data should be filtered
  • What views are shown
  • What buttons, or Actions, are visible
  • Which workflows should fire off
  • Which columns should be shown or editable
  • etc.

Requirements

To accomplish this functionality in your app, you need the following:

  1. A user table
  2. A column (in the User table) that holds the Email the user will use to access the app
  3. User sign in required
  4. A one-to-one match of records in the User table for each actual user of your app

The User Table

Details

Create a table for your app that will hold information specific to the user. This table must contain at least the following:

  1. A column to hold the Email of the person accessing the app
  2. An ID column, to uniquely identify that record out of the User table.
My rules for Table Keys
  • Always a text column
  • Always hidden
  • Always UNIQUEID()

Your User table can contain any other information about the user you may need; for example:

  • User_Role - to denote if someone’s an Admin, User, etc.
  • User_Name - to hold their actual name (to display it in the app)
  • User_Assigned_Accounts - a list of the accounts assigned to the user
  • User_Hourly_Rate - the hourly rate of the user
  • User_Icon - you get the idea…

User Sign-in Required

Why?

In order to restrict when certain things should be allowed or not, the app has to have some way of differentiating one user from another. While there is another way to accomplish this, requiring someone to sign in is the simplest of them all.

By requiring people to sign into the app before they can use it, you’re ensuring the when you use the function USEREMAIL() - there will be an email in response, and from this we can base permissions and such.


One-to-one match of User Records

Explanation

What I mean by this is that for each user accessing your app, you need to have a corresponding record in the User table for that user.

  • For example: if Mary is accessing the app with the email Mary@email.com, then you’ll need to have a record in the User table for Mary with that email in the [User_Login_Email] column (or whatever you name that column).

If you do not have a record for the user, but you’re going to allow users to access the app anyways, then you need to conform your app around that eventuality - handling what happens when someone logs into the app but doesn’t have a User record.

Many of the techniques I’ll describe below (on how to use this system) depends on there only being a single record inside the User table for each user.

  • If you have the possibility of users having multiple records in the same table (with the same login in email), then you’ll have to build in a system for the users to “flag” - or somehow otherwise denote - which record they want to use.

—| The Setup |—

  1. Create a new slice of your User table

  2. Inside the condition formula space, add something like this:
    USEREMAIL() = [User_Login_Email]

  3. (Optional) Remove the “Add” permissions from this slice (keep edit and delete, if you wish for users to able able to have those permissions for their User record)


How do I use this?

Now that you’ve got everything setup and put in place, you’re ready to actually pull information from the Current_User slice and use that in conditions throughout your app.

  • To pull a value
    INDEX(Current_User[User_Role], 1)
    ANY(Current_User[User_Name]) - though I suggest INDEX() in preference to ANY()

  • To pull a list
    SPLIT(Current_User[Related Timesheets], " , ")
    SPLIT(Current_User[User_Assigned_Clients], " , ")

  • To restrict something for Admin role only
    INDEX(Current_User[User_Role], 1) = "Admin"

  • To restrict something for Admin & Team Leader roles only
    IN(INDEX(Current_User[User_Role], 1), list("Admin", "Team Leader"))

  • To see if a Client ID is inside the assigned client’s list for the user
    IN([ClientID], SPLIT(Current_User[User_Assigned_Clients], " , "))


There are an endless amount of example formulas to give, but the one’s I’ve highlighted above provide instructions on how to handle both values and lists in some common scenarios.

Other Uses

This technique of isolating out a record in a slice can be very helpful when you want to conform the UX around a specific context or situation the user might be in.

  • For example: you might create a slice to pull out newly created Timesheets, that lack a “close” time, by the user; this way you could easily create a “Time-clock” type of dashboard, or view, based on the presence of a record inside that Timesheet-isolating slice.
27 Likes
Adding Restrictions for all tables based on email and table column value
Filtering data for my users
Hiding particular field in the app based on the user email
How to create Time specific view
Setting UserSettings via an Action
Users Permission
Modular role based tile view for views
Dynamic Table From Single View
[URGENT] Am unable to see rows, addressed to me as a project lead for approval
Improving my Covid-19 App - (Please Review and Comment)
Comparing coma separated fields between tables as a security filter
Limit Response Data to only user's submissions
Sub-Login Problem
Row Lock and Unlock, and Authorization Feature
Respect `USERROLE()` in App Editor's 'Preview App as' feature
Ref Causing "This Entry Is Invalid" with no explanation
see data by email
Actions User Restriction
Contains useremail()
On User Profile Change Do
Rules dependidng of user tables
Multi-companies for one App
If false do nothing
Can users see how many markers they have placed on the map in a day. As I have created an app for survey
Userrole() default values
Not getting to work USERSETTINGS()-based translation in dereferenced columns. any ideas?
Filter view based on selected row in another view
Questions on Filtering Data
Show Delete and Add action for Current User
Inquire about user segment restrictions
How do I make a filter with two columns?
Tricky Slice of people based on info in another table
User settings being reset to initial values
Multi-person development in AppSheet
Iterating a comparison and look up over a list for Forum Mentions
Use Table To Select Which Views User Can See
Reference User List
Can i make my application view-only for users?
User Option Initial View
Security filter based on groups and not email
Use Table To Select Which Views User Can See

I’ve seen at least a dozen posts in the community in the past week about how to do something like this, and surprisingly there hasn’t been a devoted post about this yet - despite this probly being THE most common system people put in place in their apps.

So here it is - for easy reference from now on. partyparrot (Appsheet)

Quick Poll

Do you have a Current_User (Slice) - or the equivalent?
  • Yes
  • No
  • No (but I will now!)

0 voters

2 Likes

Advance Version - Emulate User

A phrase you might hear in conjunction with “hackers” or “programming,” is the idea of a “back door.”

Many times, users will email me complaining about the app behaving in a strange way - yet when I try and test it out it works just fine for me.

To help me troubleshoot problems like this, I’ve now taken to building in a back door that allows me to hijack the app and manually tell it who is using the app.


—| The Setup |—

  1. Create a new UserSetting

    • Name: Current_Selected_User
    • Type: Ref to the User table
    • Initial Value: LOOKUP(USEREMAIL(), Users, User_Login_Email, UserID)
    • Show If: LOOKUP(USEREMAIL(), Users, User_Login_Email, User_Role) = “Admin”
    • Description: “Select the user you wish to emulate using the app”
  2. Change the condition formula for the Current_User slice

    • [UserID] = UserSettings(Current_Selected_User)

What this does is allow someone that’s got the “Admin” role to see (and be able to edit) this UserSetting.

The UserSetting defaults itself to the ID of the user record that matches the email the person is using to login; this value is then used by the Current_User slice to pull out their corresponding record.

  • Since the UserSetting defaults to the ID that matches the user’s USEREMAIL(), and the Current_User slice defaults to what the UserSetting says, end users see no change in functionality.

But admins have the ability to modify this field; and instead of it defaulting to THEIR [UserID], they can manually select the ID they wish to use - giving them the ability to “emulate” using the app as another user.


This advanced version of the Current_User slice technique has really helped me with troubleshooting problems specific to users.

App on my friends!

18 Likes

Thanks for taking the time to write this out!

1 Like

Amazing info as always Matt, thank you for sharing ! partyparrot (multitech)

The backdoor you mentioned works similarly to the “preview as” function built into the app editor or does it provide better information when you test on it?

4 Likes

It’s similar in function to the “preview app as” in the editor - but this is baked into the app.

The app LITERALLY believes it’s being accessed by that user - in regards to anything that you’ve built out using the Current_User slice.

USEREMAIL() will still return whatever email the user is ACTUALLY using - or whatever you’ve entered in the “preview app as” space.

2 Likes

Why do you prefer index?

2 Likes

It’s been my experience that ANY() can sometimes remove the “meta data” of a column; I’m talking about the deep layered data that AppSheet keeps internally that we can’t see. Stuff like:

  • Referenced table Key column
  • Referenced key column type

But also stuff we can actually set:

  • EnumList base type
  • Enum base type ref table

In the past when I would use ANY(), sometimes this information would be lost and I would loose the reference nature of the data or the base type of what we’re working with.

INDEX(), however, has never had this problem - in fact it seems to retain the type of the data you pull in no matter what.

8 Likes

Now that’s a good tip! I saw that limitation with ANY() before, but didn’t realize there was a way around it with INDEX()! Great find, @MultiTech_Visions!

6 Likes

Wait, it wasn’t ANY()-- I saw this same limitation with ORDERBY().

4 Likes

Interesting - You should drop this as a tip in of itself.

3 Likes

Wow! Now I need to check where I use ANY() - which is a ton and see if I should switch to Index.

2 Likes

Wouldn’t it be better to report this as a bug, instead of changing every ANY() expression to INDEX() ?

4 Likes

The real problem is that it WAS fixed at one point, then broke; then was fixed again, then broke.

I can’t build around something like that for a paying client; there’s few things on the platform that see-saw like this - this just happened to be one of them. :wink:

4 Likes

Mate, i fed up with all the bugs, unexpected behavior of Appsheet. I m so tired… to struggle with it.
My clients claiming and jump over to me (Off course) … again. I fed up.

I go sleep and forget all now…

Something is going wrong with Appsheet, as it did not happen before Google era. I simply hope everyging goes back before the time before Google aquires. Honestly, as things obviously gets worse and worse day by day.

8 Likes

That’s not true; it’s always been like this. Part of the territory of a SaaS that deploys multiple updates a day.

I hear you; that’s why I typically steer clear of any of the “newer” features, they will have tons of bugs that need to be rolled out. It’s maybe about a year before things stabilize to where I feel okay using it in “production” apps.

And when it comes to things like this ANY() situation we’re talking about: once I identify something that’s oscillating like that, I’ll find a solid work around that I can rely on. Such as the INDEX() thing.

3 Likes

I respect you and your opinion, but it is my own opinon.

I simply so tired now.

4 Likes

@praveen See above. I tend to agree. I know this is part of the growing process but the level of attention that has been needed over the past months on apps that have worked forever that are broken or broken features is not good.

Do you see any improvement on this? Does google not have internal testing? or the level of testing that appsheet had in the past?

7 Likes

Hi Matt, why don’t you use Security Filter for that?

1 Like

Because then the Users table is no longer the Users table - it’s more a single user table.

If I put a security filter on the Users table, then I won’t have any of the other Users data in the system - admin users would have problems with that.
:+1:

1 Like