Monthly Archives: September 2011

Custom colored UITabBar icons – an update

In a previous blogpost, which is still one of my most popular ones, I gave a method on how to create a tabBar with complete custom icons. It’s really more like a hack. But so far I haven’t found anything better yet. My method didn’t use any private API’s, and I have already succesfully submitted two apps in the App Store: Kust Afvalgids and Cultuurmarkt. The former one suffers from a little inconvenience: when you touch a given icon on your tabBar, you can still see the sort of “transparent” area covering the location of the icon, to indicate that this icon is active. You can get rid of that though. Here’s how.

A custom UITabBarItem implementation

To get rid of the annoying active state of an icon, all you have to do is implement your own class which inherits from UITabBarItem, and override the different icons. Then in the code you can create an instance of your custom UITabBarItem class, and assign it to the tabBarItem of your controller. Here’s how I implemented the subclass:

// CustomTabBarItem.h
@interface CustomTabBarItem : UITabBarItem {
    UIImage *customHighlightedImage;
    UIImage *customStdImage;
}

@property (nonatomic, retain) UIImage *customHighlightedImage;
@property (nonatomic, retain) UIImage *customStdImage;

@end
// CustomTabBarItem.m
#import "CustomTabBarItem.h"

@implementation CustomTabBarItem

@synthesize customHighlightedImage;
@synthesize customStdImage;

- (void) dealloc
{
    [customHighlightedImage release]; customHighlightedImage=nil;
    [customStdImage release]; customStdImage=nil;   
    [super dealloc];
}

-(UIImage *) selectedImage
{
    //return self.customHighlightedImage;
    return nil;
}

-(UIImage *) unselectedImage
{
    //return self.customStdImage;
    return nil;
}

@end

Two methods need to be overridden: selectedImage, which returns the icon you want to display in the active state; and unselectedImage, which returns the icon you want to display in the inactive state. As you can see in my code, I simply return nil. This indicates that I have no icons for both active & normal state. If you’ve read my previous blog post, you know I have just created different background images for the tabBar. Each background image already has the different images in it, so I don’t need separate icons for tabBarItems. If you do work like that, you just return the correct images in the correct methods (the parts which are commented out).

Now in each sub-controller of your tabBar, you make an instance of the CustomTabBarItem class, and assign it to self.tabBarItem. Example:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    CustomTabBarItem *tabItem = [[CustomTabBarItem alloc] initWithTitle:@"" image:nil tag:0];
        
    //tabItem.customHighlightedImage=[UIImage imageNamed:@"icon_active.png"];
    //tabItem.customStdImage=[UIImage imageNamed:@"icon_inactive.png"];
        
    self.tabBarItem=tabItem;
    [tabItem release]; 
    tabItem=nil;
}

Again, I provided sample code to show you how to specify the different images. Each time your view will appear, your tabBarItem will be set to your custom implementation. There is still one (little) problem: If you tap a selected tabBarItem twice, the active state will show. If you have a view transition, like a flip view animation, the active state will show. I have not found a way around this yet. I guess it’s somewhere in the internals of cocoa and I have no clue how to solve it. If you do find a solution, please let me know via the comments.

Conclusion

In my latest iPhone app, I have used the system of my previous blogpost to display different background images for the tabBar per controller. Then I have combined this with a custom tabBarItem implementation to get rid of the highlight state by returning nil for selectedImage & unselectedImage. If you don’t mind that this isn’t totally bug free, then this method is a good solution I think. Apps get approved with this code, since you don’t call any private API methods.

Update – For those of you who want to see an example of the two methods combined, here’s a piece of code:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self setCustomTabBarItem];
    
	for(UIView *view in self.tabBarController.tabBar.subviews) {
		if([view isKindOfClass:[UIImageView class]]) {
			[view removeFromSuperview];
		}
	}
	
    UIImageView *imgView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"custom_tabbar_image1.png"]] autorelease];
    imgView.frame = CGRectMake(imgView.frame.origin.x, imgView.frame.origin.y - 2.0, imgView.frame.size.width, imgView.frame.size.height);
	[self.tabBarController.tabBar insertSubview:imgView atIndex:0];
}

- (void)setCustomTabBarItem
{
    // Remove highlight from tabbar item:
    CustomTabBarItem *tabItem = [[CustomTabBarItem alloc] initWithTitle:@"" image:nil tag:0];
        
    self.tabBarItem=tabItem;
    [tabItem release]; 
    tabItem=nil;
}

Forcing the translator locale in Twig

I’ve been using Symfony2 for only a week now, but I’m already fully immersed in the SF2 world. Learning Symfony2, YAML, Twig and MongoDB all at the same time. This means I’m spending lots of time either in the official documentation, or on Google. For a test case, I’m localizing a small web application, so it’s available in 2 languages. One problem I was facing was that I needed to force certain translation tags in a specific language. Example: The link text for the French version of the current page doesn’t have to be “French”, but rather “français”, if a French person is to understand it. If you’re also using Twig, you should be familiar with the syntax to provide a translation for a text or tag:

{% trans %}Hello world!{% trans %}

To force this in the French locale (provided you have a French localization in place), you can use the “in” modifier:

{% trans in 'fr' %}Hello %name%!{% trans %}

I couldn’t find this anywhere in the Symfony2 documentation. But luckily the code itself produced the information I needed. Hopefully this saves you a search in the code.

Custom MongoDB Security Provider in Symfony2

At work, we’ve decided to upgrade our entire workflow. One of the main decisions we’ve had to make, was which PHP framework we wanted to start using. I have quite some experience with Zend Framework, so I was kinda biased towards that framework. However, the team didn’t want to create a new codebase on ZF1. Since waiting till ZF2 is released was not an option, we decided to give Symfony2 a try.

To each build up some experience with SF2, each team member was tasked to create a blog system with SF2. I decided to use MongoDB as my preferred method of data persistence, since the rest of the team also showed quite some interest in using this technology. Following the documentation on the Symfony2 website, I quickly set up my BlogBundle, with an administration interface and everything. The next step was to secure the backoffice with user credentials coming from a MongoDB collection. This is when a whole world of hurt opened up: almost no information on the intertubes on how to do this. I figured out I would need a custom UserInterface and UserProviderInterface. People kept referring to the FOSUserBundle. The Friends of Symfony are a cool bunch, but I wanted to write my own code, as part of the learning process. A couple of hours and an almost-headache later, I had figured it out. Since there will probably be other people struggling with this, here’s how I did it.

Setting up the Security configuration

When you want to create parts of an application which need to be accessed by logged in users, you need to create firewall rules for this. Here’s my configuration.
File: /app/config/security.yml

security:
    encoders:
      MediaMates\BlogBundle\Document\User: md5

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        default:
          id: mm_security_provider

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
          pattern: ^/blog/backoffice/login$
          anonymous: ~
          security: false

        secured_area:
            pattern:    ^/
            anonymous: ~
            logout:
              path: /blog/backoffice/logout
              target: /blog
            form_login:
                login_path: /blog/backoffice/login
                check_path: /blog/backoffice/login_check

    access_control:
        - { path: ^/blog/backoffice, roles: ROLE_ADMIN}

Important here is the “providers” part. As you can see, I specified a provider named “default”. I want to use a custom provider, and I refer to it in the services via its ID. The ID here is “mm_security_provider”. The rest of the config is pretty standard. The firewall will allow anonymous users to the entire path, but the access controll requires an admin role if you access anything from the backoffice.

The “mm_security_provider” is still unknown to the dependency injection container, so we have to configure it. Here’s the configuration:
File: /src/MediaMates/BlogBundle/Resources/config/services.yml

services:
  mm_service_blog:
    class: MediaMates\BlogBundle\Service\Blog
    arguments: [@doctrine.odm.mongodb.document_manager]
  mm_service_user:
    class: MediaMates\BlogBundle\Service\User
    arguments: [@doctrine.odm.mongodb.document_manager]
  mm_security_provider:
    class: MediaMates\BlogBundle\Security\MongoProvider
    arguments: [@mm_service_user]

The “mm_security_provider” is configured to create an object of class MediaMates\BlogBundle\Security\MongoProvider. The provider will need to access the User collection in my MongoDB. I have taken a Service Oriented Architecture (SOA) approach, where I gather all business intelligence in so called Service Classes. My provider needs the correct service to talk to the database. So I configured an argument for the constructor which refers to my User service. In case you don’t work with a SOA, you can just replace the argument with @doctrine.odm.mongodb.document_manager. This should inject the Doctrine MongoDB ODM into your provider instead of a User service class.

Implementing the UserProviderInterface

Now that we have made the necessary configurations, it’s time to implement the actual custom provider that will allow the Security layer to talk to a MongoDB collection. First I’ll show you the code, and then I’ll explain what happens.
File: /src/MediaMates/BlogBundle/Security/MongoProvider.php

namespace MediaMates\BlogBundle\Security;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

use MediaMates\BlogBundle\Service;

class MongoProvider implements UserProviderInterface
{
    protected $userService;
    
    public function __construct (Service\User $userService)
    {
        $this->userService = $userService;
    }
    
    public function loadUserByUsername($username)
    {
        $user = $this->userService->getUserByUsername($username);

        if (null === $user) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found', $username));
        }
        
        return $user;
    }

    public function refreshUser (UserInterface $user)
    {
        return $this->loadUserByUsername( $user->getUsername() );
    }

    public function supportsClass($class)
    {
        return $class === 'MediaMates\BlogBundle\Document\User';
    }
}

The most important part is to extend Doctrine\ODM\MongoDB\DocumentRepository and implement Symfony\Component\Security\Core\User\UserProviderInterface. The interface only requires you to implement 3 methods: “loadUserbyUsername”, “refreshUser” and “supportsClass”. I have also implemented the constructor. The argument of the constructor, in my case, is my User service, like I specified earlier in my services configuration. In case you changed the configuration to the Doctrine MongoDB ODM, then you will have to update the constructor argument to be of the correct type.

loadUserByUsername is called by the Security layer, when the given username & password need to be verified. You have to find a user by his/her username. If this fails, you throw a UsernameNotFoundException. If you do find a user, you just return the object.

refreshUser is called by the Security layer, whenever it deems it necessary to reload the given user. This already happens after login, because it is possible that the object retrieved for verifying the credentials, is incomplete. With refreshUser, you should return a complete User object.

supportsClass returns wether or not the given class name is supported by your provider.

Implementing the UserInterface

The hard part is over now. Only thing remaining, is creating a User model which implements the correct interface, so that the Security layer knows what to expect. Here’s my model, complete with MongoDB ODM annotations, properties, getters & setters, and required methods by the interface.
File: /src/MediaMates/BlogBundle/Document/User.php

namespace MediaMates\BlogBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @MongoDB\Document
 */
class User implements UserInterface
{
    /**
     *
     * @MongoDB\Id
     */
    protected $userID;
    
    /**
     * @MongoDB\String
     */
    protected $username;
    
    /**
     * @MongoDB\String
     */
    protected $password;
    
    /**
     * @MongoDB\String
     */
    protected $firstname;

    /**
     * @MongoDB\String
     */
    protected $lastname;
    
    /**
     * @var string A salt for the password
     */
    protected $salt = "Blog";
    
    /**
     * The roles a user has
     *
     * @return array
     */
    public function getRoles ()
    {
        return array(
            'ROLE_ADMIN'
        );
    }
    
    /**
     * Erases the credential information
     */
    public function eraseCredentials ()
    {
        $this->password = null;
    }
    
    /**
     * Verifies if given user equals the current user
     *
     * @param mixed $user
     * @return Boolean
     */
    public function equals (UserInterface $user)
    {
        return ($this->getUsername() === $user->getUsername());
    }
    
    /**
     * Returns the salt
     *
     * @return string
     */
    public function getSalt ()
    {
        return $this->salt;
    }

    /**
     * Get userID
     *
     * @return id $userID
     */
    public function getUserID()
    {
        return $this->userID;
    }

    /**
     * Set username
     *
     * @param string $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * Get username
     *
     * @return string $username
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Set password
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Get password
     *
     * @return string $password
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Set firstname
     *
     * @param string $firstname
     */
    public function setFirstname($firstname)
    {
        $this->firstname = $firstname;
    }

    /**
     * Get firstname
     *
     * @return string $firstname
     */
    public function getFirstname()
    {
        return $this->firstname;
    }

    /**
     * Set lastname
     *
     * @param string $lastname
     */
    public function setLastname($lastname)
    {
        $this->lastname = $lastname;
    }

    /**
     * Get lastname
     *
     * @return string $lastname
     */
    public function getLastname()
    {
        return $this->lastname;
    }
}

Methods required by the interface are: “getUsername”, “getSalt”, “getRoles”, “eraseCredentials”, “equals”. I hard coded the getRoles return value. The eraseCredentials method should remove sensitive information from your model. In our case, the password. The equals method lets you implement how two User models should be compared. I just made a comparison against the username, but of course this could (an should!) be more elaborate.

Conclusion

As it turns out, writing your own provider for authenticating against a MongoDB User collection wasn’t all that hard. You just have to figure out which classes & methods to implement, and it just works. Nevertheless, Symfony2 is quite young, and documentation still is scarce. The official documentation has some examples yes, but they usually don’t go beyond the basics. The cookbook has some specific use cases, which is nice. I hope this write-up will help you to implement your custom provider. If you have any suggestions or remarks, don’t hesitate to comment.