CocoaPods trunk is moving to be read-only. Read more on the blog, there are 19 months to go.
TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Nov 2017 |
SwiftSwift Version | 3.0 |
SPMSupports SPM | ✓ |
Maintained by Luciano Polit.
RepositoryKit is a framework that eases the way of organizing your code.
It is based on the Repository Pattern which main purpose is to separate the logic that retrieves the data of the store modules (such as networking or local storage) from your code. It is usually located in a controller, forcing it to do more things than what it is supposed to (and making it bigger). However, now, they have the right and cleaner place to be.
It consists on three components:
Moreover, it uses Promises, which is a good way to manage asynchronous code.
To run the example project there are two ways:
pod try RepositoryKit
on the Terminal.To install RepositoryKit using CocoaPods, simply add the following line to your Podfile
:
pod "RepositoryKit", "~> 4.1"
To install RepositoryKit using Carthage, simply add the following line to your Cartfile
:
github "LucianoPolit/RepositoryKit" ~> 4.1
The case: it is needed to interact with a REST API to manage a conversation, just the simple case, 'Message' as the unique entity. With that messages, we need to make four different operations and the Repository will be the responsable of executing them. The operations are: Create, Read, Update, Delete, which are known as CRUD.
First of all, to interact with our API, our repository needs to be able to identify the entity because it needs to execute the different operations over the same entity. So, we have to conform 'Identifiable', as simple as adding an 'id' property.
struct Message: Identifiable {
// Entity identification.
var id: String
// Properties.
var text: String
}
Then, just because the repository needs to communicate to our API in a common data representation, the entity has to conform two new protocols: 'DictionaryInitializable' & 'DictionaryRepresentable'.
extension Message: DictionaryInitializable {
// Initialize with a dictionary.
init?(dictionary: [String: Any]) {
// Here we will have the dictionary that should initialize the entity.
// We have to be careful that every information we need is inside the dictionary.
// If not, return nil, and we will have an error in the promise of the repository operation.
guard let id = dictionary["_id"] as? String, let text = dictionary["text"] as? String else { return nil }
// In case that we have the data needed, set it.
self.id = id
self.text = text
}
}
extension Message: DictionaryRepresentable {
// Dictionary representation.
var dictionary: [String: Any] {
return [
"_id": id,
"text": text
]
}
}
And thats all! We have the entity ready to interact with our repository and our API!
Now that we have our entity ready, we need to make something that should be able to interact with our API with the information that the entity is able to bring.
First of all, our repository needs to know about our entity. Conforming 'Repository' is easy.
class MessageRepository: Repository {
// It is the entity that the repository operates.
typealias Entity = Message
}
Then, we need to conform to 'NetworkingRepository'. So, the repository needs:
class MessageRepository: NetworkingRepository {
// It is the entity that the repository operates.
typealias Entity = Message
// It will make the requests.
var store: Networking
// The path that represents the repository.
var path: String = "messages"
// Initialize it with a networking store.
init(store: Networking) {
self.store = store
}
}
Now, we are able to communicate between Entity - Repository - API. But, what operations can we make? Anyone yet…
So, as the case requires, we need to be able to make the CRUD operations:
If we just make the repository conforms to 'CRUDNetworkingRepository', it will be able to make this operations. Is that cool? Thanks to Swift, it is possible because of default protocol implementations!
class MessageRepository: CRUDNetworkingRepository { ... }
In the case that the CRUD concept is not enough for what you need, or another repository that the KIT includes, you can extend the repository and define your own methods, or also make your own type of repository.
Now, we are ready to try it!
First of all, we are using Promises. If you have no knowledge about them, I invite you to figure out a little about PromiseKit.
Before starting to make the CRUD operations, we need to initialize the networking session and the repository.
let networkingSession = NetworkingSession(url: "http://localhost:3000")
let messageRepository = MessageRepository(store: networkingSession)
Now we are ready! Lets create a message.
// It will create a message on your API.
messageRepository.create(["text": "Here goes a message!!!"])
.then { message in
print(message)
// Observe that you should have at least the 'text' and 'id' properties initialized.
// In my case, it printed 'Message(id: "581a8e2da80614c82661b98d", text: "Here goes a message!!!")'.
// Here you can update the UI for example.
}
.catch { error in
// In case that an error occurs, do whatever you have to do here.
errorHandler(error)
}
Let me show you one more case. Then try whatever you want!
messageRepository.search("581a8e2da80614c82661b98d")
.then(execute: messageRepository.delete)
.then {
print("The message has been deleted")
}
.catch { error in
// In case that an error occurs, do whatever you have to do here.
errorHandler(error)
}
In this case, it will try to search the entity on the API with the specified id. Then, in case of success, it will try to delete it, and, if everything is OK, it will print that the entity was deleted. Just remember to not forget to handle if an error occurs.
Is it easy? And cleaner? That's cool! And now, its your turn to try it!
The usage case was only the beginning and the simplest one. There are more repositories types, therefore, the entities might have more requirements. To see a more complex case, download the example, and see how it works with multiple repositories (networking and local storage together, for a list of users in this case).
Here goes a list of the current repositories available (for every single repository, check what protocols are required to be implemented, for both cases, the entity and the repository):
Luciano Polit, [email protected]
RepositoryKit is available under the MIT license. See the LICENSE file for more info.