Recently I’ve released iOS social network app template called Socium. Here I’d like to share with you my knowledge learned from developing it.
List of features
First of all, we need to write down a list of features we want to see in the app. For example Socium, just as a typical social network app, has the following features:
- User profiles
- User posts and comments
- Post likes
- Browsing and searching other users
- Private realtime chats
- Push notifications
Having this list in mind we can select a backend for the project.
Social network application is a complex system, consisting of client side and server side. Socium’s server side is built on top of Parse Server. There are many advantages of Parse Server: it’s open sourced, has a large community and has a great iOS SDK (as well as Android SDK, by the way). Parse Server requires a VPS or a cloud application platform like Heroku, which could be hard to setup and maintain for those, who are not familiar with backend stuff. However, I believe having control over the backend is a good thing. Even if it requires some additional skills. After all, I learn from lessons.
Now when we know the requirements it’s time to write down all necessary tables and relationships between them. Here’s the schema I’ve designed for Socium:
Let’s run through few most interesting entities (or “classes” in Parse Server terminology).
User/UserProfile. To protect the user’s email I‘ve created another class for public user profile called UserProfile and add one-to-one relationship with User class. UserProfile is visible to anyone. User is visible only to the owner and the admin.
UserProfile has followings, followers and postLikes fields, which are one-to-many relationships to UserProfile and Post entity respectively. All of them
Image. Instead of storing images directly in PFFile field I’ve created a separate class for that. Therefore images can be stored in array and many-to-many relationships. This gives a better flexibility if we’ll want to add, for example, like/unlike functionality to a photo.
Post, PostComment, Conversation and Message entities are pretty self-explanatory.
The client app architecture
After we’ve defined the database schema it’s time to think about our iOS app. The app must:
- Display the content fast and efficiently.
- Interact with the user.
- Send requests to the server and receive responses from the server.
- Be testable and maintainable.
A standard Apple’s MVC design pattern won’t be a best fit for such quite big project. VIPER looks much better. However, VIPER has pretty high barriers to entry. Since Socium is the iOS app template created for wide range of developers (and even non-developers), it could be uneasy to understand the project for someone.
Finally, I’ve decided to mix MVC approach with service-oriented architecture: all the business logic is stored in Services, which are separated from ViewControllers. Thus, a view controller never interacts with the backend directly. Instead, it asks an appropriate service to do something it needs. Services interact with the backend, work with models and handle CRUD (create, read, update, delete) operations. Each model (Post, UserProfile, Message) has an appropriate service (PostService, UserProfileService, MessageService).
It’s worth noting that models are not subclasses of PFObject, as in future we may want to hook up another backend (not Parse Server). That’s why services aslo need to parse objects to our models and vice versa. To keep a single responsibility principle I’ve separated the parsing logic into Parsers.
Here’s how the architecture looks like:
Note the app stores the retrieved objects in a cache. Each service has it’s own cache. Initially, I was thinking to store the objects in a plain array right in a service. However, it’s useful if a cache has such methods like appendEntityIfNeeded, appendEntitiesIfNeeded, removeEntity etc. All of them could (and should) be separated into another class, which acts as a cache. I didn’t set a requirement to implement the data persistence, so no Core Data used. The cache flushes as soon as the app shuts down.
It is also very useful to separate the UI part into a modules. Each module would have it’s own storyboard and a set of view controllers. Services can’t always be attributed to any particular module, so I prefer to place them into a special Shared folder. Other common classes, helpers, utilities also goes into Shared. Here’s more detailed schema:
That’s it! As you can see, we’ve got simple yet solid, expandable and testable architecture for the social network app.