LASIDO: improve your sight reading

LASIDO is the first iOS application developed on my free time, it’s a simple music game that aims to help in improving sight reading. It’s available for both the iPhone and the iPad.

Since I’m not a graphic designer, I chose this ultra minimalistic design, the app focuses on its only feature with two modes:

  • training: unlimited time to understand and improve the reading
  • chrono: where user have to read the more notes he can in a limited 30 seconds game

Game Center is supported, so you can challenge yourself and your friends to get better at reading scores.

mzl.npbbgvbb

LASIDO – 1.99 – https://itunes.apple.com/fr/app/lasido/id613353673?ls=1&mt=8

Création de templates personnalisés pour Xcode 4

Les actions répétitives font parties des choses les plus agaçantes pour un développeur, et c’est pourquoi nous cherchons toujours à automatiser au maximum ces petites choses que l’on doit systématiquement faire et qui nous usent.

La création de classes et plus particulièrement de classes héritants de UIViewController dans Xcode a toujours été pour moi source de frustration, et ce pour plusieurs raisons:
– je ne supporte pas le constructeur par défaut initWithNibName:bundle: qui demande à tout va le nom de fichier du XIB alors qu’une fois pourrait suffire
– les templates de base n’indentent pas le code à ma façon
– les pragma mark ne sont pas présentent alors qu’elles sont très pratiques
– je dois toujours supprimer les commentaires d’Apple sur ce qui doit être fait ou non dans les méthodes

Voici donc la méthode pour gagner deux minutes à chaque création d’un view controller: les templates personnalisés.

Pour commencer

La meilleure façon de commencer est peu être de comprendre comment sont faits les templates déjà présents dans Xcode. Depuis Xcode 4.3, ils se trouvent sur votre Mac à cet endroit:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates

Vous pouvez les ouvrir, regarder comment ils sont faits, mais je vous déconseille de les modifier pour la simple et bonne raison qu’une erreur dans un de ces templates peut ne plus le faire fonctionner, voir faire crasher Xcode lorsque vous essayerez de les utiliser.

Nous allons plutôt créer nos propres templates dans le répertoire prévu à cet effet, il n’existe d’ailleurs probablement pas et c’est pourquoi nous allons le créer:

~/Library/Developer/Xcode/Templates

Une fois le dossier “Templates” créé, nous allons créer un sous-répertoire “File Templates” qui contiendra lui même un sous-dossier nommé comme bon vous semble pour vous identifier, par exemple “CC” soit:

~/Library/Developer/Xcode/Templates/Project Templates/CC/

Composition d’un template de fichier

Un template est à nouveau un répertoire dont l’extension est .xctemplate. Ce répertoire contient au moins deux fichiers:

– TemplateInfo.plist: il s’agit de la configuration du template

– TemplateIcon.icns: il s’agit de l’icone du template

À ces fichiers s’ajoutent ceux qui seront automatiquement créés lors de l’utilisation de notre template, par exemple:

– ___FILEBASENAME___.h: notre fichier interface

– ___FILEBASENAME___.m: notre fichier d’implémentation

Commençons donc par créer copier le fichier TemplateIcon.icns contenu dans n’importe quel template de base d’Xcode.

Créons ensuite les fichiers suivants:

Architecture du template de base

TemplateInfo.plist:

<!--?xml version="1.0" encoding="UTF-8"?-->

AllowedTypes

public.objective-c-source
public.objective-c-plus-plus-source

DefaultCompletionName
MyClass
Description
A custom UIViewController subclass, with implementation and header files.
Kind
Xcode.IDEKit.TextSubstitutionFileTemplateKind
MainTemplateFile
___FILEBASENAME___.m
Options

Description
The name of the class to create
Identifier
productName
Name
Class
NotPersisted
Required
Type
text

Platforms

com.apple.platform.iphoneos

SortOrder
1

___FILEBASENAME___.h:

//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//

#import

@interface ___FILEBASENAMEASIDENTIFIER___ : UIViewController

@end

___FILEBASENAME___.m:

//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//

#import "___FILEBASENAME___.h"

@interface ___FILEBASENAMEASIDENTIFIER___ ()

@end

@implementation ___FILEBASENAMEASIDENTIFIER___

#pragma mark - Constructor

- (id)init
{
    self = [super initWithNibName:@"___FILEBASENAMEASIDENTIFIER___" bundle:nil];
    if (self)
    {
        // Custom initialization
    }
    return self;
}

#pragma mark - View management

- (void)viewDidLoad
{
    [super viewDidLoad];
}

#pragma mark - Memory

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

@end
Fenêtre de création de fichiers

Cela suffit à créer nos fichiers propres et bien formatés, testez, vous verrez!

Le contenu du fichier TemplateInfo.plist

Ce fichier au format PLIST contient toute la configuration de notre template. Il est composé à partir des clés suivantes:
– AllowedTypes: Un tableau déclarant le ou les types possible
– DefaultCompletionName: Le nom qui sera affiché dans la fenêtre de création d’un nouveau fichier
– Description: La description du template qui sera affichée dans la fenêtre de création d’un nouveau fichier
– Kind: Apparement, on utilise toujours Xcode.IDEKit.TextSubstitutionFileTemplateKind, je n’ai pas plus d’informations là dessus pour le moment
– MainTemplateFile: le fichier principal qui sera créé par le template
– Platforms: la liste des plate-formes autorisées à utilisées le template (pour nous iOS uniquement)
– SortOrder: La position du template dans la fenêtre de création d’un nouveau fichier, utile lorsqu’on a plusieurs templates pour les ordonner
– Options: Contient notamment les informations sur les champs contenu dans la boite de dialogue pour créer un nouveau fichier

Le contenu des fichiers sources

Il existe là quelques macros que l’on peut utiliser afin qu’elles soient automatiquement remplacées à la création d’un nouveau fichier. Dans notre exemple, nous retrouvons les macros suivantes:
– ___FILENAME___: Le nom du fichier avec extension (.h ou .m)
– ___FILEBASENAMEASIDENTIFIER___: Le nom du fichier sans extension
– ___PROJECTNAME___: Le nom du projet
– ___FULLUSERNAME___: Le nom complet de l’utilisateur, dans mon cas “Cyril CHANDELIER”
– ___ORGANIZATIONNAME___: Le nom d’organisation réglé à la création du projet
– ___DATE___: La date du jour
– ___TIME___: L’heure courante
– ___YEAR___: L’année en 4 chiffres

Automatiser l’ajout d’un XIB

En testant tout ça, on se rend compte qu’il manque quelque chose: le XIB. Il serait intéressant d’en avoir un automatiquement créé et configuré. Pour cela, on va en créé un normalement, ajouter le nom de classe du File Owner, supprimer la status bar et mettre le layout en mode “Freeform”.

Maintenant, étudions le en l’ouvrant en mode code puis copions son code.

Au même niveau que les .h et .m, nous allons créer un fichier ___FILEBASENAME___.xib et y coller le code précedemment copié; il ne reste plus qu’à remplacer le nom de classe de test par des macros ___FILEBASENAMEASIDENTIFIER___ pour obtenir quelque chose comme ceci:

<!--?xml version="1.0" encoding="UTF-8"?-->

1536
11G63b
2844
1138.51
569.00
<object class="NSMutableDictionary" width="300" height="150"><string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string><string key="NS.object.0">1930</string></object>

IBProxyObject
IBUIView

com.apple.InterfaceBuilder.IBCocoaTouchPlugin

<object class="NSMutableDictionary" width="300" height="150"><string key="NS.key.0">PluginDependencyRecalculationVersion</string><integer value="1" key="NS.object.0"></integer></object>

<object id="372490531" class="IBProxyObject" width="300" height="150"><string key="IBProxiedObjectIdentifier">IBFilesOwner</string><string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string></object>
<object id="975951072" class="IBProxyObject" width="300" height="150"><string key="IBProxiedObjectIdentifier">IBFirstResponder</string><string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string></object>
<object id="191373211" class="IBUIView" width="300" height="150"><reference key="NSNextResponder"><int key="NSvFlags">274</int><string key="NSFrameSize">{320, 548}</string><reference key="NSSuperview"><reference key="NSWindow"><reference key="NSNextKeyView"><object class="NSColor" key="IBUIBackgroundColor"><int key="NSColorSpace">3</int><bytes key="NSWhite">MQA</bytes><object class="NSColorSpace" key="NSCustomColorSpace"><int key="NSID">2</int></object></object></reference></reference></reference></reference></object>
<object class="IBUISimulatedSizeMetrics" width="300" height="150"><string key="IBUISimulatedSizeMetricsClass">IBUISimulatedFreeformSizeMetricsSentinel</string><string key="IBUIDisplayName">Freeform</string></object>
IBCocoaTouchFramework

<object class="IBObjectContainer" width="300" height="150"><array class="NSMutableArray" key="connectionRecords"><object class="IBConnectionRecord"><object class="IBCocoaTouchOutletConnection" key="connection"><string key="label">view</string><reference key="source" ref="372490531"><reference key="destination" ref="191373211"></reference></reference></object></object></array></object>
3

<object class="IBMutableOrderedSet" width="300" height="150"><array key="orderedObjects"><object class="IBObjectRecord"><int key="objectID">0</int><array key="object" id="0"><reference key="children" ref="1000"><nil key="parent"></nil></reference></array></object></array></object>
<object class="IBObjectRecord" width="300" height="150"><int key="objectID">1</int><reference key="object" ref="191373211"><reference key="parent" ref="0"></reference></reference></object>
<object class="IBObjectRecord" width="300" height="150"><int key="objectID">-1</int><reference key="object" ref="372490531"><reference key="parent" ref="0"><string key="objectName">File's Owner</string></reference></reference></object>
<object class="IBObjectRecord" width="300" height="150"><int key="objectID">-2</int><reference key="object" ref="975951072"><reference key="parent" ref="0"></reference></reference></object>

___FILEBASENAMEASIDENTIFIER___
com.apple.InterfaceBuilder.IBCocoaTouchPlugin
UIResponder
com.apple.InterfaceBuilder.IBCocoaTouchPlugin
com.apple.InterfaceBuilder.IBCocoaTouchPlugin

3
<object class="IBClassDescriber" width="300" height="150"><array class="NSMutableArray" key="referencedPartialClassDescriptions"><object class="IBPartialClassDescription"><string key="className">___FILEBASENAMEASIDENTIFIER___</string><string key="superclassName">UIViewController</string><object class="IBClassDescriptionSource" key="sourceIdentifier"><string key="majorKey">IBProjectSource</string><string key="minorKey">./Classes/___FILEBASENAMEASIDENTIFIER___.h</string></object></object></array></object>

0
IBCocoaTouchFramework
YES
3
YES
1930
Architecture finale du template

Voici donc l’arborescence finale de notre template:

Aller plus loin

Cette technique qui nous permet de gagner deux minutes et un peu de joie de vivre en codant n’est qu’un début. Sur le même principe, il est possible de créer des templates pour automatiser à l’extrème certains processus du code iOS. La création d’un projet complet et configuré est un bon exemple qui peut faire gagner énormément de temps au lancement d’un nouveau projet, l’automatisation de la création de view controllers contenant une table view et faisant l’import par exemple de cellule custom pré-configurées en est un autre. Bref, de nombreuses possibilités s’offrent à vous pour gagner du temps et ainsi confirmer le fait que les développeurs sont des êtres extrêmement feignants!

Bootstrap et Google Maps

J’ai rencontré quelques problèmes lors de l’intégration de cartes Google Maps (mais ce problème peut arriver dans d’autres cas) dans une application Web utilisant Bootstrap. Le problème: les images utilisées dans la carte (marqueur, barre de scroll, etc.) étaient complement déformées sans raison apparente puisque le même code aurait très bien fonctionné ailleurs.

Après quelques recherches avec l’inspecteur d’éléments de Chrome, j’ai finalement identifié la source: un “max-width: 100%” appliqué à toutes les balises img par Bootstrap. En regardant un peu plus dans le le fichier bootstrap.css, voici ce qu’on peut trouver:

img {
width: auto;
height: auto;
max-width: 100%;
vertical-align: middle;
border: 0;
-ms-interpolation-mode: bicubic;
}

#map_canvas img,
.google-maps img {
max-width: none;
}

Autrement dit, le cas est géré par le framework CSS uniquement si vous avez appelé le conteneur de la carte avec

<div id="map_canvas"></div>

ou

<div class="google-maps"></div>

La solution, outre changer le nom de votre conteneur, est d’ajouter cette ligne à votre CSS: .votreClasse { max-width: none; }

Une fois cela fait, tout fonctionne à nouveau correctement! Dans l’espoir que cette astuce puisse aider quelqu’un un jour.

jQuery Address Picker & Symfony 2

Essayant de découvrir Symfony 2, je cherchais un moyen de créer un nouveau type de champs de formulaire pour gérer les champs de type “Address” tout en offrant la possibilité d’utiliser une carte et l’API Google Maps pour localiser les adresses.

Je suis tombé sur un tutoriel très intéressant qui résume ça comme il faut, très bien expliqué.

Ca se passe là bas: http://devyourdream.net/2012/09/24/jquery-address-picker-symfony2-google-map-dans-vos-formulaires/

Problème avec MAMP et mysql.sock

En essayant d’utiliser la fonction doctrine:build –all de Symfony, un problème est survenu et m’a pris la tête aujourd’hui car PDO n’arrivait pas à trouver le socket sur lequel écrire pour communiquer avec la base de données MySQL installée en local grâce à MAMP.

L’erreur:

[...] No such file or directory (trying to connect via unix:///var/mysql/mysql.sock) in [...]

Tout simplement, le “fichier” mysql.sock n’existe pas à cet endroit. Il se trouve ailleurs, dans le répertoire d’installation de MAMP, un lien symbolique suffira à résoudre le problème…

La solution:

sudo mkdir /var/mysql
sudo ln -s /Applications/MAMP/tmp/mysql/mysql.sock /var/mysql/mysql.sock

Tout d’abord, il faut créer le répertoire /var/mysql (il n’existait pas sur mon installation), ensuite on créer le lien symbolique vers ce dossier et depuis les fichiers temporaires de MAMP. Le tour est joué!

Fix posté dans l’espoir de pouvoir aider quelqu’un, un jour, à ne pas perdre son temps.