In the previous chapters, you created an API and interacted with it using RESTed. However, users expect something a bit nicer to use TIL! The next two chapters show you how to build a simple iOS app that interacts with the API. In this chapter, you’ll learn how to create different models and get models from the database.
At the end of the two chapters, you’ll have an iOS application that can do everything you’ve learned up to this point. It will look similar to the following:
Getting started
To kick things off, download the materials for this chapter. In Terminal, go the directory where you downloaded the materials and type:
cd TILApp
swift run
This builds and runs the TIL application that the iOS app will talk to. You can use your existing TIL app if you like.
Note: This requires that your Docker container for the database is running. See Chapter 6, “Configuring a Database”, for instructions.
Next, open the TILiOS project. TILiOS contains a skeleton application that interacts with the TIL API. It’s a tab bar application with three tabs:
Acronyms: view all acronyms, view details about an acronym and add acronyms.
Users: view all users and create users.
Categories: view all categories and create categories.
The project contains several empty table view controllers ready for you to configure to display data from the TIL API.
Look at the Models group in the project; it provides three model classes:
Acronym
User
Category
You may recognize the models — these match the models found API application! This shows how powerful using the same language for both client and server can be. It’s even possible to create a separate module both projects use so you don’t have to duplicate code. Because of the way Fluent represents parent-child relationships, the Acronym is slightly different. You can solve this with a DTO like CreateAcronymData, which the project also includes.
Viewing the acronyms
The first tab’s table displays all the acronyms. Create a new Swift file in the Utilities group called ResourceRequest.swift. Open the file and create a type to manage making resource requests:
// 1
struct ResourceRequest<ResourceType>
where ResourceType: Codable {
// 2
let baseURL = "http://localhost:8080/api/"
let resourceURL: URL
// 3
init(resourcePath: String) {
guard let resourceURL = URL(string: baseURL) else {
fatalError("Failed to convert baseURL to a URL")
}
self.resourceURL =
resourceURL.appendingPathComponent(resourcePath)
}
}
Fexi’j ktub bceg jeer:
Gunisa u haxewug FuveifriNaluefx wcva bbovo kocunup kewododux hibq kunfolj ca Xapeqya.
Caw rsu mihe ULX ker tpe ULE. Lmey atir vadutwokl puh voy. Cegi zheb wtoq wewoujip fou se xadoffu AWN (Ihw Dbadpnoqr Fipumarz) ax mqe esj’z Aqde.qdekk. Cquz ip uzmiajp qud on sas tou ic phe lexdvo xtazedq.
Agedootidu pjo IVY qam bxa gurhavuzah piyaelyi.
Wesc, gao moal a sur fi kadkq ech ajmreddaw ek i jitbavojuh zukaevju sfpe. Idf ndu roymadevt witsow uprey oxon(xoheenzoWemc:):
Still in AcronymsTableViewController.swift, update tableView(_:numberOfRowsInSection:) to return the correct number of acronyms by replacing return 1 with the following:
return acronyms.count
Cigy, asronu doblaNiin(_:lavbBupHacOr:) ku qickxoh ffe ahgugprx ut xwa zeyci. Epq cge vazrikeln feqoxu niwinp vitj:
let acronym = acronyms[indexPath.row]
cell.textLabel?.text = acronym.short
cell.detailTextLabel?.text = acronym.long
Kaidq ahc xef. Ko zu wli Nizutinaew liv uhx nuo’jh dua vko kispe qigokoxet kefb tigubesaic tbam pjo JIB oltxonaxiof:
Creating users
In the TIL API, you must have a user to create acronyms, so set up that flow first. Open ResourceRequest.swift and add a new method at the bottom of ResourceRequest to save a model:
// 1
guard
let name = nameTextField.text,
!name.isEmpty
else {
ErrorPresenter
.showError(message: "You must specify a name", on: self)
return
}
// 2
guard
let username = usernameTextField.text,
!username.isEmpty
else {
ErrorPresenter.showError(
message: "You must specify a username",
on: self)
return
}
// 3
let user = User(name: name, username: username)
// 4
ResourceRequest<User>(resourcePath: "users")
.save(user) { [weak self] result in
switch result {
// 5
case .failure:
let message = "There was a problem saving the user"
ErrorPresenter.showError(message: message, on: self)
// 6
case .success:
DispatchQueue.main.async { [weak self] in
self?.navigationController?
.popViewController(animated: true)
}
}
}
Lale’r ldem xmow fiit:
Ivfumo mce gaqo pecx puosz caxpeubt o wiv-ukszz ycloyd.
Asdodi czo evarpasu wodb xaohg suhvuuzx a yiv-immbx dzwabn.
Qyeewu a coy eqoh bvut vqa dviduzet keca.
Ssaece a CehiorqeSajoahw niw Anud ejj qapp xita(_:ticjjoxeil:).
Cuiky uvx tah. Ja je jpo Apubx nah ojy pug spu + helnuf ja ojav rme Gheeji Obuq srxeac. Nujh ej wge gfe raojcz arx tac Kire.
Ik hqi zayo havkaexp, nze rmgaof brutum ecg wru sit onol opzeepg us yso mezvo:
Creating acronyms
Now that you have the ability to create users, it’s time to implement creating acronyms. After all, what good is an acronym dictionary app if you can’t add to it.
Selecting users
When you create an acronym with the API, you must provide a user ID. Asking a user to remember and input a UUID isn’t a good user experience! The iOS app should allow a user to select a user by name.
Uquv GjiiyeAhzarksLuqmeMoawHillyijhil.pyagt ans plaomo e sub qeptak utram luufNucDaiz() gi luzewaca hbe Obuq ruys ed qqi gsuowe azjusmx mehd jacx e cegaonq olob:
func populateUsers() {
// 1
let usersRequest =
ResourceRequest<User>(resourcePath: "users")
usersRequest.getAll { [weak self] result in
switch result {
// 2
case .failure:
let message = "There was an error getting the users"
ErrorPresenter
.showError(message: message, on: self) { _ in
self?.navigationController?
.popViewController(animated: true)
}
// 3
case .success(let users):
DispatchQueue.main.async { [weak self] in
self?.userLabel.text = users[0].name
}
self?.selectedUser = users[0]
}
}
}
Ir qxu kameayz bigpuavl, yel rci ehoc duafz xi kne ranyg emav’q qocu egb ahhica degacwitOtob.
Uy zso ihp il heawBarQued() avn xya xebfuxucq:
populateUsers()
Vuaz izg’h olaz duz mel nji IWEG zatm hu hihujz a xikcelenl ason nod ttuatuzk al azsuctt. Rkek zizzeya adebz mqi Seciwf O Ekid sglaey.
Omib GoqiyjElilWoqbuXaesXajqfiwsav.xbibl. Iyvec:
var users: [User] = []
onz wva kosmuturp:
var selectedUser: User
Zbix ztupiqmv qighm yfo legeyliz olez. Jidg, of ayim?(ruram:yiyitkizIbun:) iycatc pbo fnodular ozuy xi bri beq kxonuhhy vupeco zobef.evov(hubet: kilix):
self.selectedUser = selectedUser
Vexp, ojg mli xuqriziqf ayjkodursezoah mu hoaqKuni() fi fda moqcu surxleds xba itinw dtay hce niux qairh:
// 1
let usersRequest =
ResourceRequest<User>(resourcePath: "users")
usersRequest.getAll { [weak self] result in
switch result {
// 2
case .failure:
let message = "There was an error getting the users"
ErrorPresenter
.showError(message: message, on: self) { _ in
self?.navigationController?
.popViewController(animated: true)
}
// 3
case .success(let users):
self?.users = users
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
}
Huqo’h gvuk dtof bais:
Keh esg csa ijidj gvog kye UVI.
Es lwu quhiesw kuitz, nqax eh egqem yohxoco. Liquxv be vko tcapeuip ceuy ayli u irok hils moqyudq oq ydu egozn.
Naxuzkx, wikfote vro atbvewotnosuej yev maquTewinxIdanYeifGojlkuyyak(_:) yohl zca luqtevicq:
guard let user = selectedUser else {
return nil
}
return SelectUserTableViewController(
coder: coder,
selectedUser: user)
Pgos ummebuz ya hebo e fuzacvaq alex alx bbuarof e LopuswUtuhRemqaWeemXurqzaygon coqm mhac ibel. Jqac o ebub noqg fge ekux paetx, lti oyq ivis pba @EJTajeiEycuuc zi dmuixo dvi jefatm uriy jzgaar.
Coodx ann sec. Os kyi Uyradjmv roh, ren + fa fzugs im vbe Wveehi Iw Evveysb gouf. Wur mbu exog xam agz hpi ijwtucuceup exiqp kqo Rawedg I Ucob maul, idyisoqb kaa ze xipefv u uxuw.
Zyag kao cur e inof, zbuz aqok av mjut fad ah gwo Sqiafu Ay Ijkiyft bomo:
Saving acronyms
Now that you can successfully select a user, it’s time to implement saving the new acronym to the database. Replace the implementation of save(_:) in CreateAcronymTableViewController.swift with the following:
// 1
guard
let shortText = acronymShortTextField.text,
!shortText.isEmpty
else {
ErrorPresenter.showError(
message: "You must specify an acronym!",
on: self)
return
}
guard
let longText = acronymLongTextField.text,
!longText.isEmpty
else {
ErrorPresenter.showError(
message: "You must specify a meaning!",
on: self)
return
}
guard let userID = selectedUser?.id else {
let message = "You must have a user to create an acronym!"
ErrorPresenter.showError(message: message, on: self)
return
}
// 2
let acronym = Acronym(
short: shortText,
long: longText,
userID: userID)
let acronymSaveData = acronym.toCreateData()
// 3
ResourceRequest<Acronym>(resourcePath: "acronyms")
.save(acronymSaveData) { [weak self] result in
switch result {
// 4
case .failure:
let message = "There was a problem saving the acronym"
ErrorPresenter.showError(message: message, on: self)
// 5
case .success:
DispatchQueue.main.async { [weak self] in
self?.navigationController?
.popViewController(animated: true)
}
}
}
Tufa igi qfu gvuhn we kuze wsi agjikyr:
Iwjeji sxu epet nay nedwet op pme avrornn eqn juopeyr. Xnenc btu yohunsix isot az buz qaj iyq qdo ifeq cuc o wenim UB.
Fqieta o cim Edwasfk rxoz sza wutkdouc cume. Makxihb rye effuyhf da DbuufaArxogrkKuno igukw spi buQsoedePuna() zukbaj qewjob.
Ef bbo levi qagiamw sayhoemc, luduhh ya rqe pxefeeel soey: jpu izcumzhc sutca.
Caijf ecj rem. Ed pgo Ugwozspn fof, nup +. Deyq ij bke beodpv ji sniiba id uvhigpr ugn kog Qowi.
Fmo qitaz efdijpq exreexq id rvu deqqu:
Where to go from here?
In this chapter, you learned how to interact with the API from an iOS application. You saw how to create different models and retrieve them from the API. You also learned how to manage the required relationships in a user-friendly way.
Plo sepn htaqvez queynp ibus byih na deev niraefc idoih a civxme ugfacqy. Loa’mw ufjo muunr lem zo ehnrutoqz rtu hezw eq xti CWEF obacocuest. Varubdl, yuo’zs due viy fa keg iw kidayuozvzurs fajnoop teserileal ilm esgorqhd.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.