# iOS app COYLA J Carlos DWM Sources doc: https://hackmd.io/@sensei/r1Ep-cw8r Github mobile: https://github.com/blade-sensei/ios-app-estiam ## API projects On met en place une API qui permet de récupérer des users - projects - détails d'un projets et la modification d'un projet précis. ### Connection base de données et run du server ```javascript= // config server const port = process.env.PORT || config.server.port; app.listen(port, () => { logger.info(`server api is running on : ${port} port ...`); }); // config database const mongoURI = process.env.DB_URI || config.db.uri; mongoose .connect(mongoURI) .then(() => { return logger.info(`database connection on ${mongoose.connection.port} port with success!`); }) .catch((error) => { logger.info('fail to connect to database'); logger.error(error.message); }); ``` ### Routes ```javascript= const router = express.Router(); router.use('/users', userController); router.use('/auth', authController); router.use('/projects', projectController); module.exports = router; ``` ### Route projects Voici un example d'une route qui récupère les projets. Avant de requeter la base de données on vérifie que l'utilisateur envoie un token ```javascript= router.get('', token.verifyToken, async (req, res) => { try { const projects = await projectService.findAll(); res.send(projects); } catch (e) { logger.info(e.message); return res.status(500).send('not found'); } }); ``` ### Test Voici le résultat d'un test avec l'interface swagger `API: localhost/api/projects` ![](https://i.imgur.com/jKyluFy.png) ## App iOS Swift ### Creation des écrans - Sur Xcode on va créer une application simple - Ensuite on crée quelques écrans pour la liste de projets et détails - On oublie pas de rajouter également un Navigation Controller pour naviger entre chaque écran - Sachant que la view pour lister les projets et une view du type TableView - On rajoute une view "Home" et on déclare que la navigation commence par cette view "Home" ![](https://i.imgur.com/aDOVZyZ.png) ### Récupération des données sur une API Pour cette étape on a besoin de modifier la méthode "viewDidLoad" de notre TableViewController associé à la vue "Projects". Cette méthode effecute le code AVANT de charger la vue. ```swift override func viewDidLoad() { super.viewDidLoad() // Uncomment the following line to preserve selection between presentations // self.clearsSelectionOnViewWillAppear = false // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem tableView.tableFooterView = UIView(frame: CGRect.zero) let projectsURL = URL(string: "http://localhost:3000/api/projects")! let task = URLSession.shared.dataTask(with: projectsURL) { (data, response, error) in guard let data = data else { return } do { let projects = try JSONSerialization.jsonObject(with: data, options: []) print(projects) } catch { print(error) } }.resume() } ``` :::info On print dans le console juste pour tester la réponse que l'API nous renvoie ::: ![](https://i.imgur.com/tzqpBS2.png) Pour le moment c'est normal. On n'envoie pas de token à l'API ## Retrieve token/login On va faire une requete vers la route auth/login pour récuperer le token. On récupère le texte des champs username et password pour les mettre dans notre body de requete. Ensuite on a besoin de créer un model/struct Auth qui va mapper ou Parser la réponse json en object swift Dès qu'on récupère le token il faut qu'on le stock quelque part pour qu'on l'envoie dans les prochaines requetes ```swift @IBAction func login(_ sender: Any) { let username = textFieldUsername.text let password = textFieldPassword.text let parametersRequestLogin = ["username": username, "password": password] let projectsURL = URL(string: "http://localhost:3000/api/auth/login")! var request = URLRequest(url: projectsURL) request.httpMethod = "POST" request.addValue("application/json", forHTTPHeaderField: "Content-Type") guard let httpBody = try? JSONSerialization.data(withJSONObject: parametersRequestLogin, options: []) else { return } request.httpBody = httpBody let session = URLSession.shared.dataTask(with: request) { (data, response, error) in guard let data = data else { return } do { let auth:Auth? let decode = JSONDecoder() let authResponse = try decode.decode(Auth.self, from: data) print(authResponse.token) UserDefaults.standard.set(authResponse.token, forKey: "userToken" ) } catch { print(error) } }.resume() } ``` ## Liste et details Lorsqu'on charge la view pour afficher la liste de projets on va faire une requette vrs l'API On oublie pas de récupérer notre token dans UserDefaults ```swift override func viewDidLoad() { super.viewDidLoad() // Uncomment the following line to preserve selection between presentations // self.clearsSelectionOnViewWillAppear = false // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem tableView.tableFooterView = UIView(frame: CGRect.zero) let projectsURL = URL(string: "http://localhost:3000/api/projects")! var request = URLRequest(url: projectsURL) request.httpMethod = "GET" request.addValue("application/json", forHTTPHeaderField: "Content-Type") let tokenUser = UserDefaults.standard.string(forKey: "userToken") ?? "" request.addValue(tokenUser, forHTTPHeaderField: "x-access-token") let session = URLSession.shared.dataTask(with: request) { (data, response, error) in guard let data = data else { return } let decode = JSONDecoder() self.projects = try! decode.decode([Project].self, from: data) DispatchQueue.main.async { self.tableView.reloadData() } }.resume() } ``` On va passer le projet à la vue details, pour cela il faut changer la méthode prepare qui prépare les données vers la prochaine navigation. ```swift override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "Project" { guard let selectedContactRow = tableView.indexPathForSelectedRow?.row else { return } guard let detaislViewController = segue.destination as? DetailsViewController else { return } let project = projects[selectedContactRow] detaislViewController.project = project } } ``` Ensuite dans notre vue details on va utiliser l'object project qui a été passé la vue Table, et on va changer le titre de notre vue ainsi que mettre une description/status du projet sur cette page. ```swift var project: Project? { didSet { self.title = self.project?.title } } override func viewDidLoad() { super.viewDidLoad() self.descriptionLabel.text = self.project?.description // Do any additional setup after loading the view. } ``` ## Résultat Vue projects ![](https://i.imgur.com/EoftFxR.png) Vue détails ![](https://i.imgur.com/zeZQ3Bz.png) ## Test Pour que vous puissiez tester ### API Server - Créer un base de données mongo appelé "dev" en local - Lancez le serveur de db - Taper la commande suivante pour installer les dépendances ```bash npm install ``` - Lancer le serveur API avec la commande: ``` npm run start:dev npm run start ``` - Créer les jeux de données, taper les urls - http://localhost:3000/seed/users - http://localhost:3000/seed/projects ![](https://i.imgur.com/2NuUX6V.png) - Vous pouvez tester l'API en utilisant le swagger - taper l'url: http://localhost:3000/explorer/#/ - Utiliser la route auth/login, click sur le bouton "try it out" - username = demo et password = demo - Récupérer (copiez) les token de la réponse - Click sur Authorize et rentrez copier le token - Vous pouvez maintenant accéder aux autres routes ### Mobile iOS Ouvrir le projet sur XCODE Appuyer sur CMD + R Le projet va se lancer, vous arriverez sur une page de login. Rentrez: username: demo et password: demo.