File Handling Tutorial for Server-Side Swift Part 1
In this two-part file handling tutorial, we’ll take a close look at Server-side Swift file handling and distribution by building a MadLibs clone. By Brian Schick.
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
File Handling Tutorial for Server-Side Swift Part 1
30 mins
- Getting Started
- Using Templates
- Template Syntax
- Getting a List of Templates
- Reading Template Files
- Persisting the Selected Template
- Submitting the Completed Form
- Handling the Completed Form
- Creating Your Own Template
- Adding Routes for Your Template
- Putting It All Together
- Adding Concurrency
- Using SwiftNIO
- Reading a File Asynchronously
- Getting a List of Templates Asynchronously
- Writing a File Asynchronously
- Using Grand Central Dispatch
- Where to Go From Here
In this two-part file-handling tutorial, you’ll take a close look at how Server-Side Swift handles and distributes files. Part One focuses on the internal aspects of file handling: How to persist data to file, and how to access data stored within a server’s file system.
Part Two follows up by exploring best practices for serving up data between a Server-Side Swift app and its consumers.
If you’ve worked with Server-Side Swift, you’ve likely used tools like Vapor’s Fluent or Kitura’s SwiftKuery to integrate databases and other structured data into your app.
But what if you need to interact directly with files and their contents? Do familiar iOS file management tools work as expected on a server and at web scale? You’ll find out in this tutorial.
Your sample project for this file-handling tutorial is a Vapor app called RetroLibs. It’s a nostalgic homage to the classic MadLibs text-based game. You’ll find its starting page brimming with the vintage colors, fonts and styling of Web 1.0.
For this retro game, you’ll use the equally-classic method of reading and writing your story templates directly to and from the file system. You’ll enable basic gameplay by reading and parsing stock templates from disk.
Finally, since RetroLibs is best with your own stories, you’ll accept custom story templates and store them on the file system.
While doing this, you’ll learn both synchronous and asynchronous file read and write techniques.
So go ahead and dive in!
Getting Started
Use the Download Materials button at the top or bottom of this tutorial to download the starter project. Then open Terminal, navigate to the begin folder, and run this command:
open Package.swift
This command opens the project in Xcode. It will take some for Xcode to download the dependencies but when it’s finished, Xcode will populate the schemes and show the dependencies in the project navigator.
As with any Server-Side Swift project, your sample project uses MVC – but with a special twist! Take a look at the three files in your Models directory. Notice anything unusual?
Your project has no database support! Everything is driven by stored file content. For this reason, your Model
objects aren’t classes as usual, but simple Codable
structs.
Take your app for a quick spin. In Xcode, ensure you’ve selected the Run schema with My Mac as the target device.
Set Xcode’s working directory: Edit the Run▸My Mac scheme, and check the Run▸Options▸Working Directory checkbox. Click the folder icon to locate your begin folder.
Build and run, then open your browser to localhost:8080. You might see a prompt to allow access to the directory: Be sure to grant access so the app can read and write files.
swift run
in Terminal instead! You can still edit code in the Xcode editor, but you’ll have to vapor build
then swift run
in Terminal to run the server. You probably won’t see the access prompt, but everything will still work OK.
You’ll see the RetroLibs opening page. Other than proudly displaying its retro colors, it doesn’t do anything yet. It’s time to get to work!
Using Templates
To get underway, you’ll first assemble a list of available RetroLibs templates. You’ll find sample template files in the RetroLibsTemplates subdirectory.
Template Syntax
Take a look at these sample templates. Each template is a simple text file. Templates let users add any number of fill-in-the-blank-with-a-word-of-this-description elements by surrounding inline descriptors with curly braces. For example:
I like {type of food}, because it is {adjective}!
As a bonus(?!) – and in the spirit of the web’s earlier Wild West days — templates can include ad hoc HTML tags. After all, what’s a vintage Web 1.0 page without a bit of freeform inline styling? :]
The <b>{adjective} robot</b> blinked its <blink>{adjective} {color} eyes</blink>.
The pre-supplied sample templates are <blink>-free, we promise!
Getting a List of Templates
To keep things simple, each template’s filename is the title you’ll present to users. So you’ll just need to get a list of this directory’s contents.
To get the directory’s contents, you’ll use FileManager
, which you likely know from iOS and other Apple-native platforms. Even though FileManager
isn’t optimized for server-side use, it’s common to use it to quickly grab the contents of a directory.
Back in Xcode, open FileController.swift, and locate its getTemplateNames()
stub method. Replace its contents with the following:
do {
return try fileManager.contentsOfDirectory(atPath: workingDir)
} catch {
return nil
}
Here, you leverage two pre-provided static vars. fileManager
provides an instance of FileManager.default
, while workingDir
uses Vapor’s DirectorConfig
type to detect the local environment’s working directory, and build a reference to the RetroLibsTemplates directory, which contains your RetroLibs templates.
From there, you simply ask fileManager
to return an array of filenames, or nil if there’s an error.
Build and run, then open your browser to localhost:8080 again.
You’ll now see a list of the sample templates. Nice! But if you click one, you’ll get an error because you haven’t wired up the read-file functionality yet. You’ll also see an unwanted .DS_Store tag-along in the list of templates. You’ll address these issues next.
Reading Template Files
To teach your sample app to read template files, you’ll first add that functionality to FileController.swift. Open it in Xcode, find the static method readFileSync(_:)
and replace its contents with:
fileManager.contents(atPath: workingDir + filename)
This uses fileManager
again. Note that you wouldn’t want to do this in production. You’ll return to this a bit later to make the read operations production-worthy.
Next, open Models/TemplateCollection.swift, the file tasked with assembling raw template files into an array of structured Template
objects. Find its init()
method.
Currently, it creates an array of empty Template
objects with only a name, but you’ll need it to read each template body from the file system.
To do this, replace the existing for name in templateNames { ... }
loop with the following:
for name in templateNames {
if let data = FileController.readFileSync(name),
let content = String(data: data, encoding: .utf8) {
let (templateParts, tags) = content.toRetroLibsClips()
let template = Template(name: name, templateParts: templateParts, tags: tags)
templates.append( template )
}
}
Here, you use the just-updated readFileSync(_:)
to read each template file from disk. You then parse each template’s raw file contents into paired arrays of templateParts
, the static parts of a template, and tags
, the parts you’ll fill in to create a unique story.