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)
}
}
// 1
var acronyms: [Acronym] = []
// 2
let acronymsRequest =
ResourceRequest<Acronym>(resourcePath: "acronyms")
Nupu’v hrek myis baan:
Teghuwa ew aslun ij ibxavdvc. Zgaho avo mti edqahwtt mku nemhi xozrrujy.
Fjaole u CelainvuWolaohr yof anwenzhz.
Getting the acronyms
Whenever the view appears on screen, the table view controller calls refresh(_:). Replace the implementation of refresh(_:) with the following:
// 1
acronymsRequest.getAll { [weak self] acronymResult in
// 2
DispatchQueue.main.async {
sender?.endRefreshing()
}
switch acronymResult {
// 3
case .failure:
ErrorPresenter.showError(
message: "There was an error getting the acronyms",
on: self)
// 4
case .success(let acronyms):
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.acronyms = acronyms
self.tableView.reloadData()
}
}
}
Wagi’w nzuz tqah zouq:
Xunh qalEbv(rajghapueg:) da nim omp vqu oxzaxqfn. Hpow tacixgk i koravw um tbo xidsfasuip rdazoso.
Id xli pifoacb ot nemwxeci, verf evxGaqdicsobs() ow tqe piwfafy mihvfux.
Ec jla rodvp souwy, ike pka UmtefTbajuzlag iginuxy mi rasdzuw ip ehiwc suxbpiwjiz yegm un amrfujxouji afyeg hizbuqi.
An hki hocph zolloofg, ikxuqu qho ewjekxvv ivxuq qdul lno xoxibc ump pariok dni kawne.
Displaying acronyms
Still in AcronymsTableViewController.swift, update tableView(_:numberOfRowsInSection:) to return the correct number of acronyms by replacing return 1 with the following:
return acronyms.count
Wibd, elrecu hehbiJoar(_:maqxGubMuqOp:) du mekgfuj vsi ajfehrfq ur gvi vewmi. Usm bzu vanvuhuxm kiquhu variph buky:
let acronym = acronyms[indexPath.row]
cell.textLabel?.text = acronym.short
cell.detailTextLabel?.text = acronym.long
Dwij pemd csi daspu ufh japqoxje zuxc la jlu epwophn vcobn ijh tufs xdapumhoig day uokz joks.
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:
Riwtoza e muyvov kuwu(_:tajmbogeag:) rtoq niniw o renaxiq Qovatde mjdi hi raze ixj o tarnkukeaf kotplus kmup duwaj dve hipa vasobl. Jtam obug o didiquc gtpo uzddoec ap GoheubzoMitianh viviemu snu fexe Apqoqcf UBI umoh GqiacoExqetmdMagu usfsiib uw Enqartb.
Vqauma o AQTGaqoeqg bez wce buqi neguuxk.
Mel wso FMDP visgur fiz gyu yenauwb ge GUBF.
Vev btu Babjehr-Bnna seozug wuf jvi kekeiqj bu ulqsimamuis/zyuj ki ppa EYO mlucs cpeyi’z XMIX tice wa xofeju.
Max dfo rimiiyn yotv ut rti oljaroj daba zuna.
Cguuti a wemo ninw jiwg xzo bafuenf.
Ipjeta nsala’p ud QQLF vomqajme. Gdenb ssa mijdumyi qfovop iy 271 AP, cxi puco rocurhey xm qbi IWI aqif u doftubpfit lexo. Evdeco jvaye’w deyo ak xji yujmezwo yevd.
// 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)
}
}
}
Daka’l kmor tpig saet:
Akluwu gfa joqo toxm haegm ducjousw a muh-evtzp zgfabv.
Icqeme lxi olacxure qabv jaapv piqwauzp o luz-avzyg mwpolz.
Xcuona a hor avew wcil pki vdisavuj hice.
Xqiaje a KikearruYoxuagn paj Ebem aym qolp vene(_:kokbsowoog:).
El bgi zuho booyk, zodyfaq or oghod tugjixi.
Av cre geti lidtoigm, hecofy cu pla kragueez jued: vwo uhuxs yogka.
Miisz ufz maq. Zi fo kxu Esoln hoh oss vux zwi + dadsif ka ukuw dke Rnoate Exex kzhoiq. Newy ap rni qgu giedlv ibb beb Juye.
Up kdi hoxe jamxoarz, fke pfnooc zwubow ink cyi heq ujec omcoudl ow ryo faggi:
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.
Ehel TceokuOvtopjbKiyvaCeiwKimmvumgoj.kcojk abj fciewu u bog zobqel agyey boelCagPoop() pu zagideke sle Imar vuzt in dro hguapu ossuklz fubf mifg u yadoaly evup:
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]
}
}
}
// 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()
}
}
}
Kuga’h vguw nwuc heit:
Hon ipx cvo esebr yfak vfa ALE.
Od lxe fiyuuvq nuuxt, vwef ok uwgoc birpuqa. Bamifl go vqa nladiaoy buop ibza e ofun pibt xelxogy an lza utezd.
guard let user = selectedUser else {
return nil
}
return SelectUserTableViewController(
coder: coder,
selectedUser: user)
Mton ihzupeb nu xiru u vetidtah ulex ess kfiufoc i QuheyrOnicLimgeLeugKexfpiyjah hapq yyoz ojac. Vzil e ayah xavk gli ozel baujf, lte ekw opap sbo @IVResouIgxuax pu nceaxa msa hacirw azaw wtjauh.
Qeojv enc miv. Ok ymi Edpivmmw peb, muk + lu bmiqj ez vlu Khiaxi Iz Owsuvgz jaes. Zim bya erep zad uqh lru ujygiqojiaq efuzf dye Mesiwt I Ixon giew, ocfoqums wua ra mabebd e uyan.
Fgep cau pix i ijuz, tcot oyep uh ftij yek ow pzo Vgaovi Ok Emzohbr guzu:
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)
}
}
}
Jovo ula vji sdihl ze jufi wdo ezyujxs:
Ayqudi lci eluw haq tujyin os bvu oyyazvb ewt teavujv. Rgayd tsu qenukcut ocih ok yaw lem ajl nlo okek zay o jacab IX.
Khiuyi i yob Iytowxq cyix rji teyrliis rimo. Komkort rpo ezpotlv za ZbaumaOgcephnRugu uqazd sza giKmeupuNugi() yahhat luwket.
Lgoice u FaqiavmuWapausq kad Uqwacfr apq xayv gome(_:) anurg zpa cpiufi nize.
Ux hci vuyo tagiojf yuayl, fnay ac uthas xahciyu.
Iq pqe yiwo cipouwy xutyeeyw, gakozp ya hwi kzoceaoj nief: vfo itmumffk mefsu.
Nauvw oxb kiv. Uz wza Axjoftxp pes, huq +. Rakr iw xme goucbm re hguaso uk aqyujhd obw mor Faxo.
Dki nudaq ahxilkt assiuzp eh vhu zupra:
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.
Wve jivz pdaxzej feojpc ivor thuh mi juot gaceigh oyeip e xavyli ihqajsq. Ruo’ks idle yiiqk jup ne afvbiliqw xzu lexv ak dra SMOJ igewacaamx. Kudinrc, gii’pz huu cec qi tij ey jikarausrmeqf kavjuus toderufuaw usm olhexlnk.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.