Swift : astuces

Voici quelques astuces pour développer avec Swift et les API d'Apple :

Cet article n'est pas un tutoriel, la liste d'astuces est présentée hors contexte. C'est simplement un pense-bête accessible, en français, pour ces fonctions.

Article mis-à-jour le 2015-06-11

Etendre une classe NS

On peut facilement rajouter des fonctions à des classes Apple préexistantes.

Récemment j'ai découvert que la syntaxe pour accéder directement au dossier de cache ou de document de l'application, avec NSFileManager, était une plaie.

Normalement, pour accéder par exemple au dossier de cache, il faut faire :

var thePaths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)

var thePath: AnyObject = thePaths[0]

Bon, ça va pas aller de taper ça trop souvent…

On va donc écrire une petite extension à NSFileManager pour se créer des raccourcis :

extension NSFileManager {
    class func documentsDir() -> String {
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as [String]
        return paths[0]
    }

    class func cachesDir() -> String {
        let paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true) as [String]
        return paths[0]
    }
}

On a ajouté les fonctions documentsDir et cachesDir à la classe NSFileManager, donc mainenant on peut simplement appeller comme ceci :

let path = NSFileManager.documentsDir()

Ouf !

Je me suis fait la même chose avec une petite série de couleurs dernièrement :

extension NSColor {
    class func solarizedBase3() -> NSColor {
        return NSColor(calibratedRed: 0.0, green: 0.17, blue: 0.21, alpha: 1)
    }
    class func solarizedBase2() -> NSColor {
        return NSColor(calibratedRed: 0.0, green: 0.21, blue: 0.25, alpha: 1)
    }
    class func solarizedText3() -> NSColor {
        return NSColor(calibratedRed: 1.0, green: 0.96, blue: 0.89, alpha: 1)
    }
    class func solarizedRed() -> NSColor {
        return NSColor(calibratedRed: 0.88, green: 0.17, blue: 0.2, alpha: 1)
    }
    class func solarizedGreen() -> NSColor {
        return NSColor(calibratedRed: 0.53, green: 0.6, blue: 0.12, alpha: 1)
    }
    class func solarizedBlue() -> NSColor {
        return NSColor(calibratedRed: 0.0, green: 0.55, blue: 0.81, alpha: 1)
    }
}

Il y avait d'autres moyens, mais celui-ci me parlait directement. Ensuite il suffit par exemple de faire :

let myColoredBackground = NSColor.solarizedBlue()

Enregistrer des valeurs utilisateur

Avec NSUserDefaults, on peut quasi-automatiquement, c'est-à-dire sans se soucier de chemins ou de noms de fichiers, sauvegarder des informations pour l'utilisateur que l'application pourra retrouver à son redémarrage.

Typiquement : des scores pour un jeu, des noms d'utilisateur, des préférences du genre “auto login yes/no”, une position de marque-page dans un document, etc.

Ce n'est pas fait pour sauvegarder des fichiers entiers par dizaines, mais c'est pratique pour des petites valeurs qu'on veut retrouver à chaque lancement de l'app.

On peut par exemple instancier des objets “score” et “username” dans sa classe :

class AppDelegate: NSObject, NSApplicationDelegate {
  var score: Int = 100
  var username = "Yolo42"
  var defaults: NSUserDefaults
  override init() {
    self.defaults = NSUserDefaults.standardUserDefaults()
  }
}

Puis enregistrer :

defaults.setObject(self.score, forKey: "score")
defaults.setObject(self.username, forKey: "username")

Et récupérer :

if let savedScore = defaults.objectForKey("score") as? Int {
  self.score = savedScore
}
if let savedUsername = defaults.objectForKey("username") as? String {
  self.username = savedUsername
}

Les objets sauvegardables doivent être compatibles avec NSCoding, on parle donc de Int, String, NSString, NSDictionary, NSArray, etc.

Lancer un timer en tâche de fond

Avec NSTimer on peut se lancer un timer en background, qui appellera une fonction à chaque tic.

Je reprends notre classe d'exemple et instancie un NSTimer vide dans l'initializer :

class AppDelegate: NSObject, NSApplicationDelegate {
  var score: Int = 100
  var username = "Yolo42"
  var timer: NSTimer?
  var defaults: NSUserDefaults
  override init() {
    self.defaults = NSUserDefaults.standardUserDefaults()
    self.timer = nil
  }
}

Dans une fonction, on crée le timer complet.

func letsGo() {
  self.timer = NSTimer.scheduledTimerWithTimeInterval(30.0, target: self, selector: "doTheThingForTheTimer:", userInfo: nil, repeats: true)
}

Ce timer va déclencher, toutes les 30 secondes, la fonction “doTheThingForTheTimer”, en se passant lui-même (self) dans la notification.

func doTheThingForTheTimer(timer: NSTimer) {
  self.timer = timer
  self.score -= 1
}

On peut l'arrêter à tout moment :

func stopIt() {
  if self.timer != nil {
    self.timer!.invalidate()
    self.timer = nil
  } else {
    self.timer = nil
  }
}

Ne pas oublier de le mettre à nil avant de le modifier, même une fois invalidé, sinon il garde en mémoire ses anciens réglages.

Auteur: Eric Dejonckheere