Kitura Tutorial: Getting Started With Server-Side Swift
Do you wish your iOS skills worked on the backend? This Kitura tutorial will teach you to create RESTful APIs written entirely in Swift. By David Okun.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Kitura Tutorial: Getting Started With Server-Side Swift
30 mins
- Getting Started
 - Installing CouchDB
 - Kitura and RESTful API Routing
 - Creating the Kitura Tutorial Project
 - Troubleshooting Errors
 - Using Kitura With Xcode
 - Setting Up Your Kitura Server
 - Creating Your Model
 - Connecting to CouchDB
 - Persisting Your Acronyms
 - Creating Your Codable Routes
 - Testing Your API
 - Where to Go From Here?
 
Persisting Your Acronyms
Create a Swift File named AcronymPersistence.swift, and add it to the KituraTIL target.
Replace the contents of AcronymPersistence.swift with the following:
import Foundation
import CouchDB
import LoggerAPI
extension Acronym {
  // 1
  class Persistence {
    // 2
    static func getAll(from database: Database, callback: 
      @escaping (_ acronyms: [Acronym]?, _ error: Error?) -> Void) {
    
    }
    
    // 3
    static func save(_ acronym: Acronym, to database: Database, callback: 
      @escaping (_ acronym: Acronym?, _ error: Error?) -> Void) {
    
    }
    
    // 4
    static func delete(_ acronymID: String, from database: Database, callback: 
      @escaping (_ error: Error?) -> Void) {
    
    
    }
  }
}
OK, take a look at what you just stubbed out:
- By adding a class to an extension of 
Acronym, you are essentially creating a namespace for your class. Because you are making use of static methods, and these technically need to be globally scoped, namespacing your persistence methods allows you to make it more difficult to call them accidentally. - Think about what you will want to do for your acronyms — you want an easy way to run your most basic operations, but you don’t want to write a whole bunch of logic into your router. This 
getAll(from:callback:)method should logically return an array ofAcronyms! - This 
save(_:to:callback:)method should easily save an object, but you’ll do more. It is common practice for aPOSTrequest to return the entire created object with a 201 response code — thus, you’ll make sure you have the entire newly created object returned to you. - Lastly, when you delete an object, the HTTP route will only include a reference to the ID of the object. So you should create a method that only needs the object’s ID to delete it, even though, internally, you will also use its 
_revvalue to delete it. 
Now that you understand the usefulness of this class, and why you would abstract much of your database operation away from your routes, add the following code to your getAll(from:callback:) method:
//1
database.retrieveAll(includeDocuments: true) { documents, error in
  guard let documents = documents else {
    Log.error("Error retrieving all documents: \(String(describing: error))")
    return callback(nil, error)
  }
  //2
  let acronyms = documents.decodeDocuments(ofType: Acronym.self)
  callback(acronyms, nil)
}
SwiftyJSON. Not bad, right?Take a look at the two main components that you’ve added to this function:
- You will notice that your 
databaseobject has a lot of functionality attached to it. While you could write this in yourCodableroutes, it’s easier to bury this in yourPersistencehelper class. This will do most of the heavy lifting for you! - You are taking the documents that CouchDB has returned to you and encoding them into their intended native types. This is where 
Codablealso shines in a big way — gone are the days of having to parse parameters from a request! 
Next, beef up your save(_:to:callback:) method by adding the following code to it:
// 1
database.create(acronym) { document, error in
  guard let document = document else {
    Log.error("Error creating new document: \(String(describing: error))")
    return callback(nil, error)
  }
  // 2
  database.retrieve(document.id, callback: callback)
}
Breaking this down:
- Just like before, you use your 
databaseobject to perform your CRUD operation — but notice that you’re still getting a returned type ofDocumentResponse. This contains an_idand a_revfor your newly created object, but not much else! - You take the 
_idfrom yourcreateoperation, and you use yourdatabaseto retrieve that specific object. You can implicitly pass thecallbackdown to this operation because the twocallbackarguments have basically the same signature — this saves you some code! 
Alright, time to wrap this one up! Add the following code to your delete(_:from:callback:) method:
// 1
database.retrieve(acronymID) { (acronym: Acronym?, error: CouchDBError?) in
  guard let acronym = acronym, let acronymRev = acronym._rev else {
    Log.error("Error retrieving document: \(String(describing:error))")
    return callback(error)
  }
  // 2
  database.delete(acronymID, rev: acronymRev, callback: callback)
}
And, point by point:
- Just like in your 
save(_:to:callback:)method, you are retrieving an object and using a native type in the callback. You need to get the object because yourCodableroute will only provide the_id, whereas theDatabasemethod requires both an_idand a_rev. - Once you have your native object, you ask 
databaseto delete it, and just like you did insave(_:to:callback:), you can implicitly pass the callback to thisDatabaseoperation because they have the same signature! 
OK, now that you have an easy utility for working with CouchDB and your Acronym object, it’s time to dive into setting up your Codable routes!
Creating Your Codable Routes
Create a new file named AcronymRoutes.swift, and add it to the KituraTIL target. Replace the contents of AcronymRoutes.swift with the following:
import CouchDB
import Kitura
import KituraContracts
import LoggerAPI
// 1
private var database: Database?
func initializeAcronymRoutes(app: App) {
  // 2
  database = app.database
  // 3
  app.router.get("/acronyms", handler: getAcronyms)
  app.router.post("/acronyms", handler: addAcronym)
  app.router.delete("/acronyms", handler: deleteAcronym)
}
// 4
private func getAcronyms(completion: @escaping ([Acronym]?, 
  RequestError?) -> Void) {
  
}
// 5
private func addAcronym(acronym: Acronym, completion: @escaping (Acronym?, 
  RequestError?) -> Void) {
}
// 6
private func deleteAcronym(id: String, completion: @escaping 
  (RequestError?) -> Void) {
}
This is a lot of change! Walking through what you’ve just added:
- Since all of your routes will make use of your 
databaseobject, it’s handy to keep a reference to it here. - You don’t want to constantly have to refer back to your 
Appclass, so this is where you store a reference to yourdatabaseobject. - You are “registering” three routes here, choosing the paths that they will be registered for, and the handlers that will run when a request is made to any of them.
 - Assuming you are launching your server on 
localhost:8080, your router will run this function with the data parsed from the request, if someone makes aGETrequest tolocalhost:8080/acronyms. - This function runs if someone makes a 
POSTrequest tolocalhost:8080/acronyms. - This function runs if someone makes a 
DELETErequest tolocalhost:8080/acronyms/id, replacing theidpart of this path with an actual ID string. 
OK, it’s now time to make your server actually getAcronyms(completion:) route handler:
guard let database = database else {
  return completion(nil, .internalServerError)
}
Acronym.Persistence.getAll(from: database) { acronyms, error in
  return completion(acronyms, error as? RequestError)
}
Pretend you’re a chef and kiss your fingers — this is 
Next, add the following code to your addAcronym(acronym:completion:) route handler:
guard let database = database else {
  return completion(nil, .internalServerError)
}
Acronym.Persistence.save(acronym, to: database) { newAcronym, error in
  return completion(newAcronym, error as? RequestError)
}
Again — just enum case as well! Here, you send a HTTP 500 error if you can’t get a handle on your database. Command-click on .internalServerError if you want to see what the other errors are!
The last route you need to update is your deleteAcronym(id:completion:) route, but you can probably already figure out what’s going to go in there:
guard let database = database else {
  return completion(.internalServerError)
}
Acronym.Persistence.delete(id, from: database) { error in
  return completion(error as? RequestError)
}
I personally love how you can make the external parameter label in function signatures optional, as you did with Persistence.delete(_:from:callback:). This generally lets you focus on making your functions English-readable as well as code-readable, and this is a great example of that feature in Swift.
To complete your app, open Application.swift and complete finalizeRoutes(with:) by replacing // 5 with the following:
self.database = database
initializeAcronymRoutes(app: self)
Log.info("Acronym routes created")