Documentation : Permissions - iOS

This example shows how to protect your shared data.

Application consists of only one model, which has two fields. First field - title - has default set of permissions, which means that it can be read, created and updated by any user. However, second field - director - can be read, created and updated only by user, who owns this record. In this example we will see that first field is visible for both users, where second is visible only for creator. Main screen of application contains list of model entities and two buttons. User can add new entity and synchronize with mobeelizer cloud. There are two users in the system and there's a possibility to switch user on application life time.

Design Your App

First of all, create new application in Mobeelizer App Designer called Permissions.

Secondly, create new model called PermissionsEntity.

We will need two fields. Firstly, create text field, name it title and use default credentials. Secondly, create text field called director and set credentials to own for each option.

For now leave conflict resolving property on overwrite. By default, there is one group - users, device category - mobile and role - users-mobile define. Deploy application to test environment. On test environment create two users with passwords: 

  • a - usera
  • b - userb

Finally, download configured template for iOS project. Change class prefix to MD and model prefix to MMD.

Use the Mobeelizer SDK

Open project in XCode. We can see that several files has been generated and configured.  

Configure AppDelegate

First of all, there is app delegate with configured creation and destruction of Mobeelizer object. Generated application does not have any view. We will add storyboard: MDStoryboad to our application and set newly created storyboard as Main Storyboard in project settings. Furthermore, we have to remove setting new window from application: didFinishLaunchingWithOptions: in MDAppDelegate

MDAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Initialize Mobeelizer SDK
    [Mobeelizer create];
 
    return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application
{   
    // Terminate Mobeelizer SDK
    [Mobeelizer destroy];
}

MDAppDelegate.m

Configure Mobeelizer entity

Both application.xml and Mobeelizer.plist were generated. In Mobeelizer.plist we have to change mode from development to production. Moreover, model has been generated for PermissionsEntity. We will add guid and owner fields together with custom costructor. We will also overwrite isEqual function to compare guid.

MMDPermissionsEntity.h
@property(strong, nonatomic) NSString* guid;
@property(strong, nonatomic) NSString* owner;
@property(strong, nonatomic) NSString* director;
@property(strong, nonatomic) NSString* title;


- (MMDPermissionsEntity*) initWithTitle:(NSString*)title andDirector:(NSString*)director;
MMDPermissionsEntity.m
- (MMDPermissionsEntity*) initWithTitle:(NSString*)title andDirector:(NSString*)director{
    self.title = title;
    self.director = director;
    return self;
}

- (BOOL)isEqual:(id)object {
    if ([object isKindOfClass:[MMDPermissionsEntity class]]) {
        MMDPermissionsEntity* entity = object;
        if ([entity.guid isEqualToString:self.guid] && [entity.title isEqualToString:self.title]) {
            return YES;
        }
    }
    return NO;
}

MMDPermissionsEntity.h MMDPermissionsEntity.m

Create user login and logout manager

Next, we will handle user functionality. We will create singleton class MDUserManager, which will hold information about currently logged user. There are two use cases to handle user functionality. Firstly, when application starts user a will be logged in. Secondly, during runtime, users can be switched.

MDUserManager.h
@property (copy, nonatomic) NSString* user;
 
+ (SSUserManager*)instance;
- (BOOL) performLoginAsUser:(NSString *)login;
- (void) switchUser;
MDUserManager.m
- (BOOL) performLoginAsUser:(NSString *)login{
    self.user = login;
    NSString* password = nil;
    if ([login isEqualToString:USER_A]) {
        password = @"usera";
    } else {
        password = @"userb";
    }
 
	//Login to Mobeelizer instance
    MobeelizerOperationError *error = [Mobeelizer loginToInstance:@"test" withUser:login andPassword:password];
 
    if(error != nil) {
        NSLog(@"Joining failed: %@ - %@", error.code, error.message);
    }
    return error == nil;
}
 
-(void) switchUser{
    if ([self.user isEqualToString:USER_A]) {
        self.user = USER_B;
    } else {
        self.user = USER_A;
    }
 
    [self performLogout];
    [self performLoginAsUser:self.user];
}
 
- (void) performLogout {
	//Logout from Mobeelizer
    [Mobeelizer logout];
}

MDUserManager.h MDUserManager.m

Next, we will move to user interface. Add a new file to project. Choose the UITableViewController subclass template and name the class MDUserContextController. We will add button to navigation bar, which will handle switching users. Controller will hold reference to button, because we will change appearance of button to show currently logged user.

MDUserContextController.h
#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
#define USER_A_COLOR RGB(255, 128, 0)
#define USER_B_COLOR RGB(0, 128, 0)
 
@property (strong, nonatomic) UIBarButtonItem *userButton;
MDUserContextController.m
- (void)viewDidLoad{
    [super viewDidLoad];
    [[self navigationController] setNavigationBarHidden:NO animated:NO];
    userButton = [[UIBarButtonItem alloc] initWithTitle:@"User" style:UIBarButtonItemStyleBordered target:self action:@selector(userButtonClicked:)];
    self.navigationItem.rightBarButtonItem = userButton;
 
    [[MDUserManager instance] performLoginAsUser:USER_A];
}

Now add function to handle button click:

MDUserContextController.m
- (IBAction)userButtonClicked:(id)sender {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [[MDUserManager instance] switchUser];
 
        dispatch_async(dispatch_get_main_queue(), ^{
            [self refreshUserButton];
 
            [UIView beginAnimations:@"View Flip" context:nil];
            [UIView setAnimationDuration:0.75];
            [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
            [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES];
            [self reloadData];
            [UIView commitAnimations];
        });
    });
}
 
- (void)refreshUserButton {
    if ([[MDUserManager instance].user isEqualToString:USER_B]) {
        [userButton setTitle:@"B"];
        [userButton setTintColor:USER_B_COLOR];
    } else {
        [userButton setTitle:@"A"];
        [userButton setTintColor:USER_A_COLOR];
    }
}

MDUserContextController.h MDUserContextController.m

Currently, if any controller extends MDUserContextController, it will have functionality of switching users.

Create entity table view controller

Now, we will create view. Add navigation controller with table view controller to storyboard. Set prototype cell identifier to PermissionsSyncCell and set style to custom. Add label to prototype cell, change its name to Title and set tag value to 1. Then add another label to prototype cell. Name second label Director, change its font color to blue and its tag value to 2. Next, add a new file to the project. Choose the MDUserContextController subclass template and name the class MDPermissionsController. Connect newly created controller to its design in storyboard. MDPermissionsController will hold an array of table records. Moreover, we will mark added records with user image.

MDPermissionsController.h
@property (strong, nonatomic) NSMutableArray *currentItems;
@property (strong, nonatomic) UIImage* userAImage;
@property (strong, nonatomic) UIImage* userBImage;

 

MDPermissionsController.m
-(void)addToolbarButtons{
    UIBarButtonItem* newButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(newClicked:)];
    [newButton setStyle:UIBarButtonItemStyleBordered];
 
    UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
    [space setWidth:10];
 
    UIBarButtonItem* syncButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(syncClicked:)];
    [syncButton setStyle:UIBarButtonItemStyleBordered];
 
    self.toolbarItems = @[newButton, space, syncButton];
    self.navigationController.toolbarHidden = NO;
}
- (void)viewDidLoad{
    [super viewDidLoad];
    [self addToolbarButtons];
 
    self.currentItems = [[NSMutableArray alloc] initWithArray:[self getItemsList]];
    userAImage = [UIImage imageNamed:@"userA.png"];
    userBImage = [UIImage imageNamed:@"userB.png"];
}

Current items array is intialized with MMDPermissionsEntities from Mobeelizer database.

MDPermissionsController.m
- (NSArray*)getItemsList {
	//Get MMDPermissionsEntity from Mobeelizer database
    MobeelizerCriteriaBuilder *criteria = [[Mobeelizer database] find:[MMDPermissionsEntity class]];
    criteria = [criteria addOrder:[MobeelizerOrder asc:@"title"]];
    return [criteria list];
}

Creating new item takes random movie and creates MMDPermissionsEntity. We will introduce two objects MDMovie and MDMovies. MDMovie is simple object consisting of title and director property.

MDMovie.h
@property(copy, nonatomic) NSString* title;
@property(copy, nonatomic) NSString* director;


-(MDMovie*) initWithDictionary:(NSDictionary*)dictionary;
MDMovie.m
-(MDMovie*) initWithDictionary:(NSDictionary*)dictionary {
    self.title = [dictionary valueForKey:@"title"];
    self.director = [dictionary valueForKey:@"director"];
    return self;
}

MDMovie.h MDMovie.m

MDMovies is singleton, which generates random movie from sample plist. 

MDMovies.m
- (id)init {
    if (movies == nil) {
        self = [super init];
        if (self) {
            NSString *path = [[NSBundle mainBundle] pathForResource:@"Movies" ofType:@"plist"];
            movies = [[NSArray alloc] initWithContentsOfFile:path];
        }
    }
    return self;
}

- (MDMovie*)getRandomMovie {
    int randomIndex = arc4random() % [movies count];
    return [[MDMovie alloc] initWithDictionary:movies[randomIndex]];
}

MDMovies.h MDMovies.m

New entity is saved in Mobeelizer database and then is inserted into table. Movie titles are sorted, that is why we insert new title into correct place.

MDPermissionsController.m
- (void)newClicked:(id)sender {
    MDMovie* movie = [[MDMovies instance] getRandomMovie];
    MMDPermissionsEntity* newItem = [[MMDPermissionsEntity alloc] initWithTitle:movie.title andDirector:movie.director];


    [[Mobeelizer database] save:newItem];

    currentItems = [[NSMutableArray alloc] initWithArray:[self getItemsList]];

    NSInteger row = -1;
    for (NSInteger i=0; i<currentItems.count; i++) {
        if ([newItem isEqual:(MMDPermissionsEntity*)currentItems[i]]) {
            row = i;
            break;
        }
    }

    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:row inSection:0];
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}

Second button is responsible for synchronization. After synchronization finishes we have to reload data in table.

MDPermissionsController.m
- (void)syncClicked:(id) sender {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
		//Synchronize with Mobeelizer cloud
        MobeelizerOperationError *error = [Mobeelizer sync];

        if(error != nil) {
            NSLog(@"Sync failure: %@ - %@", error.code, error.message);
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            [self reloadData];
        });
    });
}
-(void)reloadData{
    currentItems = [[NSMutableArray alloc] initWithArray:[self getItemsList]];
    [self.tableView reloadData];
}

Finally, we have to configure our table view controller.

MDPermissionsController.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return currentItems.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MMDPermissionsEntity* item = currentItems[[indexPath row]];
    UITableViewCell* cell = [self createCellForItem:item atRow:indexPath.row];

    if ([item respondsToSelector:@selector(owner)]) {
        UIImageView* userLabel = (UIImageView*)[cell.contentView viewWithTag:1001];
        CGFloat userLabelY = (cell.contentView.frame.size.height - 30) /2; // 7
        CGFloat userLabelX = (cell.contentView.frame.size.width - 37); // 283
        if (userLabel == nil) {
            CGRect userLabelRect = CGRectMake(userLabelX, userLabelY, 30, 30);
            userLabel = [[UIImageView alloc] initWithFrame:userLabelRect];
            [userLabel setTag:1001];
            [cell.contentView addSubview:userLabel];
        }
        if ([item.owner isEqual:USER_A]) {
            userLabel.image = userAImage;
        } else {
            userLabel.image = userBImage;
        }
    }
    return cell;
}

- (UITableViewCell*)createCellForItem:(MMDPermissionsEntity*)permissionsItem atRow:(NSInteger)row {
    static NSString *CellIdentifier = @"PermissionsSyncCell";
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    UILabel* titleLabel = (UILabel*)[cell viewWithTag:1];
    titleLabel.text = permissionsItem.title;

    UILabel* directorLabel = (UILabel*)[cell viewWithTag:2];
    directorLabel.text = permissionsItem.director;

    return cell;
}

MDPermissionsController.h MDPermissionsController.m

There are few files, which are necessary to complete this sample: Movies.plist userA.png userB.png

Conclusion

To conclude, this example presents permissions settings. We can observe that with Mobeelizer permissions settings, we can easily control visibility and data access. 

Attachments:

Permissions.png (image/png)
PermissionsEntity.png (image/png)
TitleField.png (image/png)
DirectorField.png (image/png)
AddUser.png (image/png)
PermissionsTemplate.png (image/png)