Hiding Implementation Details Using internal Properties

October 8, 2018

Swift comes with five access-level modifiers: open, public, internal, fileprivate and private. The internal modifier leads to entities being available for use only within their defining module. It’s a default modifier but it starts getting interesting only once we split our codebase into modules.

In this article, we’ll see how to provide an ability to inject a framework’s data structure into the framework’s classes, while at the same time keeping its internals hidden.

Problem Statement

Most apps these days need some kind of local persistence of data. There are many choices: Core Data, Realm, SQLitewith or without wrappers, etc. No matter what we choose, once our project exceeds 10K, 50K, or 100K lines of code we’ll inevitably start thinking about splitting it up into modules.

One of the modules we can consider extracting from the main target would contain the database access code. Let’s call it PersistenceKit, following Apple’s naming convention. We can implement the module either as a dynamic framework or a static library.

Let’s assume that PersistenceKit will contain many repositories, such as: ArticleRepository, UserRepository, etc that we’ll use to fetch and store data. A repository can be implemented as follows:

publicstructArticle{publicletid:ArticleIDpubliclettitle:Stringpublicletcontent:String}publicclassArticleRepository{publicfuncarticle(forid:ArticleID)->Article?{// finds a row in the database and maps it to a struct//// missing implementation}// other methods...}

To be able to perform an actual database access in implementations of repositories’ methods, we need some kind of reference to the database, be it:

Now, since users of our framework won’t be able to initialize Connection, we have to provide them with an instance. We can do that in an entry point to PersistenceKit:

publicstructAppDatabase{publicfuncsetup(withpath:URL)throws->Connection{// performs the setup and returns a connection instance}}

What’s left, is injecting Connection to our ArticleRepository, by changing its implementation to:

publicclassArticleRepository{publicfuncarticle(forid:ArticleID,connection:Connection)->Article?{// we can access `pool` property here because it’s accessible in this modulereturnconnection.pool.read{(db)->Article?inreturnArticle.fetchOne(db,key:id)}}}

Even if users of our framework wanted to access Connection.pool here directly, they couldn’t because it’s not accessible outside of PersistenceKit. We can be sure that database access code stays in PersistenceKit leading to a cleaner overall architecture.

Summary

Public types with internal properties are a powerful tool. We showed how to allow users to own and pass an object we need – as the framework’s authors – without exposing any of the internals. Are you aware of any other cool uses of public types with internal properties?