Introducing AppFolder

Folder of your iOS app — friendly and strongly-typed

Oleg Dreyman
AnySuggestion

--

Each one of your iOS apps has an associated container with it, where you, as a developer, can store basically anything you need. This is often referred to as a “sandbox”. Inside this sandbox, you can place files that serve different purposes — it could be user-visible documents, or database files, or caches, or some kind of metadata — anything. Basically, every persistence technique on iOS uses your app’s container to store info there. Thus, understanding how your app’s folder looks, how it’s structured and what files your put there is crucial for every iOS developer. But, here lies the problem:

For a concept so vital to every iOS app, Apple, unfortunately, haven’t put much thought into its API

So, if you need to get, say, a “Documents” folder URL for your app, you need to write this code:

let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsURL = urls[0]

Which may not look that bad… if you know exactly what that code is doing.

But for someone who’s not accustomed to this API, it may as well look scary. Because when you’re trying to use autocompletion to discover your options, you get this:

Giant list of FileManager.SearchPathDirectory options, most of which are useless on iOS

In this situation, anyone will wonder “what the heck is going on here?” .adminApplicationDirectory? .applicationScriptsDirectory? .moviesDirectory, .trashDirectory?! There’s a lot of stuff, and most of it makes absolutely no sense for an iOS app.

And then there’s Search Path Domain Mask:

FileManager.SearchPathDomainMask options

…umm, yeah, sure, I know exactly which one to choose.

This API can be justified for macOS, but on iOS it’s plain misleading:

  • cachesDirectory is placed amongst musicDirectory and desktopDirectory (which are obviously not only unique per device, but also nonsensical for iOS).
  • Documents/” directory for some reason is represented as .documentDirectory (singular), which can make you question whether you’re choosing the right folder.
  • It returns an array of URLs (can there be more than one URL? Can there be none? Who knows.)
  • And finally, using this API you have little to no idea about how your folders are actually structured on disk. It’s not obvious which folders from this giant list even exist and which do not.

We can do better

Well, imagine if we could trade the code above

let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsURL = urls[0]

For something like this

let documentsURL = AppFolder.Documents.url

Also harnessing the power of Xcode autocompletion:

Wouldn’t it be nice?

Well, that’s exactly what AppFolder is made for:

AppFolder is a really small library that visualizes your app’s folder structure in code. You can easily access system-defined directories like this:

let documents = AppFolder.Documents
let tmp = AppFolder.tmp
let applicationSupport = AppFolder.Library.Application_Support
let caches = AppFolder.Library.Caches
let cachesURL = caches.url

Everything is laid out exactly the way it is on disk. This way, disk-related operations feels much more intuitive:

let cachesURL = AppFolder.Library.Caches.url
let userCacheURL = cachesURL.appendingPathComponent("user-cache.json")
let userCacheData = try Data(contentsOf: userCacheURL)

AppFolder was designed to let developers describe their full folder structure inside their app’s container, including the directories they create themselves:

extension Documents {

final class Photos : Directory { }

var Photos: Photos {
return subdirectory()
}

}

And just like that, the newly created folder can now be treated like any other

let photos = AppFolder.Documents.Photos.url

The name for this directory will be generated automatically, so the subpath will look like “Documents/Photos/”

The dark power of inheritance

AppFolder was built using the Swift feature that is often overlooked nowadays — inheritance. As you might noticed, every directory is a strongly-typed sublcass of Directory. Swift’s nested types and extensions allow us to structure our code in the most natural way. Here’s, for example, how Library is implemented:

public final class Library : Directory {

public final class Caches : Directory { }
public var Caches: Caches {
return subdirectory()
}

public final class Application_Support : Directory { }
public var Application_Support: Application_Support {
return subdirectory()
}

}

And Documents implementation is, literally, a single line of code

public final class Documents : Directory { }

I tried many different approaches, including protocols and phantom types — but none of them allowed this level of customization and convenience. For this task, subclassing was just the right choice.

Which draws an important conclusion: no matter what you feel about certain techniques or patterns, you shouldn’t be afraid to use them when they fit your needs. Subclassing is just another tool that Swift gives us — and embracing it or renouncing it is a matter of our choice.

AppFolder is a piece of code that changed the way I think about an iOS app. I love having the whole container structure available to me from any place in my code. I hope it will be useful for you as well — let me know if it is 😉

If you have any questions about AppFolder — ask them in “Responses” section below, or ping me on Twitter. Or you can also open an issue (or a pull request!) on GitHub:

Hi! I’m Oleg, the author of Women’s Football 2017 and an independent iOS/watchOS developer with a huge passion for Swift. While I’m in the process of delivering my next app, you can check out my last project called “The Cleaning App” — a small utility that helps you track your cleaning routines. Thanks for your support!

--

--

Oleg Dreyman
AnySuggestion

iOS development know-it-all. Talk to me about Swift, coffee, photography & motorsports.