On reprend le tutoriel API REST avec FOSRestBundle Symfony 2 pour mise en place de l'API auquel on va faire quelques modifications.
L'environnement de développement pour l'application est Xcode 6.3.2.
L'objectif est de mettre en place d'une Class RestApiManager qui va exploiter la librairie SwiftyJSON. L'action réalisée par l'app est de charger l'utilisateur dont l'id est 1 dans une liste.
1 - Création du projet Xcode
Créer un nouveau projet, choisir le template iOS Application > Single View Application.
Configurer comme suit :
Product Name : FreshApiClient
Language : Swift
Devices : iPhone
2 - Ajout de la class SwiftyJSON.swift
Télécharger la librairie SwiftyJSON sur GitHub à l'adresse suivante :
https://github.com/lingoer/SwiftyJSON
3 - Création de la class RestApiManager
La classe RestApiManager va appeler l'API REST de notre back-end Symfony via la méthode makeHTTPGetRequest(). La méthode getRandomUser() ne sert pas ici à grand chose étant donné que dans le cadre de cet exemple on force la réponse sur l'utilisateur 1.
A la racine du projet créer un fichier Swift "RestApiManager.swift".
Ajouter le code suivant :
import Foundation
typealias ServiceResponse = (JSON, NSError?) -> Void
class RestApiManager: NSObject {
static let sharedInstance = RestApiManager()
let baseURL = "http://api-test.local/users/1"
func getRandomUser(onCompletion: (JSON) -> Void) {
let route = baseURL
makeHTTPGetRequest(route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
func makeHTTPGetRequest(path: String, onCompletion: ServiceResponse) {
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let json:JSON = JSON(data: data)
onCompletion(json, error)
})
task.resume()
}
}
4 - Edition du ViewController
Notre ViewController est chargé lui de la logique et de l'affichage avec la méthode addDummyData() qui va parser le flux JSON et ajouter l'utilisateur à la vue. Les méthodes tableView() définissent la vue.
Editer le fichier ViewController.swift qui est situé à la racine du projet.
Ajouter le code suivant :
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableView:UITableView?
var items = NSMutableArray()
override func viewWillAppear(animated: Bool) {
let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100)
self.tableView = UITableView(frame: frame)
self.tableView?.dataSource = self
self.tableView?.delegate = self
self.view.addSubview(self.tableView!)
let btn = UIButton(frame: CGRect(x: 0, y: 25, width: self.view.frame.width, height: 50))
btn.backgroundColor = UIColor.orangeColor()
btn.setTitle("Charger utilisateur 1", forState: UIControlState.Normal)
btn.addTarget(self, action: "addDummyData", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func addDummyData() {
RestApiManager.sharedInstance.getRandomUser { json -> Void in
let results = json["results"]
for (index: String, subJson: JSON) in results {
let user: AnyObject = subJson["user"].object
self.items.addObject(user)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
tableView?.reloadData()
})
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}
let user:JSON = JSON(self.items[indexPath.row])
let picURL = user["picture"].string
let url = NSURL(string: picURL!)
let data = NSData(contentsOfURL: url!)
cell?.textLabel?.text = user["username"].string
cell?.imageView?.image = UIImage(data: data!)
return cell!
}
}
5 - Modification du Backend Symfony 2
On va retourner la réponse dans un format différent pour coller au parsing de notre application iOS et on va rajouter une propriété picture à notre user
5.1 - Ajout de la propriété picture à l'entité User
Dans le fichier src/Fresh/ApiTestBundle/Entity/User.php ajouter les lignes suivantes :
/**
* @var string
* @ORM\Column(name="picture", type="string", length=255)
*/
private $picture;
/**
* @return string
*/
public function getPicture()
{
return $this->picture;
}
/**
* @param string $picture
*/
public function setPicture($picture)
{
$this->picture = $picture;
}
Mettre à jour la base de données :
php app/console doctrine:schema:update --force
Ajouter dans les fixtures pour chaque utilisateur, une entrée pour le nouveau champs picture :
nano src/Fresh/ApiTestBundle/DataFixtures/ORM/LoadUserData.php
<?php
namespace Fresh\ApiTestBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Fresh\ApiTestBundle\Entity\User;
class LoadUserData implements FixtureInterface
{
/**
* Load data fixtures with the passed EntityManager
*
*
*/
public function load(ObjectManager $manager)
{
$toto = new User();
$toto->setUsername('toto');
$toto->setPassword('toto');
$toto->setEmail('toto@toto.org');
$toto->setPicture('http://api-test.local/uploads/toto.png');
$titi = new User();
$titi->setUsername('titi');
$titi->setPassword('titi');
$titi->setEmail('titi@titi.org');
$titi->setPicture('http://api-test.local/uploads/titi.png');
$manager->persist($toto);
$manager->persist($titi);
$manager->flush();
}
}
Ajout de fichiers titi.png et toto.png de 45px par 45px, dans le dossier /web/uploads/.
On flush le cache de l'application :
rm -rf app/cache/*
5.2 - Modification de la méthode getUser du UsersController
On met à jour la structure de la réponse JSON pour coller avec le format parsé par notre app iOS.
nano src/Fresh/ApiTestBundle/Controller/UsersController.php
/**
* @param User $user
* @return array
* @View()
* @ParamConverter("user", class="FreshApiTestBundle:User")
*/
public function getUserAction(User $user)
{
$resultsArr = array('results' => array(
array('user' => $user)
));
return $resultsArr;
}
5.3 - Modification du serializer
On ajoute le champ afin qu'il apparaisse dans le flux JSON.
nano src/Fresh/ApiTestBundle/Resources/config/serializer/Entity.User.yml
Fresh\ApiTestBundle\Entity\User:
exclusion_policy: ALL
properties:
id:
expose: true
username:
expose: true
email:
expose: true
picture:
expose: true
6 - Test de l'application iOS
On va pouvoir builder l'app dans Xcode et la tester dans iOS Simulator.
Cliquer sur "Charger utilisateur 1" et on verra l'image et le username s'ajouter dans la liste.
Ressources :