Proper use of Strategy Pattern for simple networking layer in Swift 3 - ios

Does the following code demonstrate proper use of Strategy design pattern for a simple networking layer in swift 3?
Some code smells I'm unsure about:
violates Single responsibiility principle. Each strategy class such as Find, has a method for a different type of implementation. This is because I could want to find an image, or a user, or a chatroom. which are stored at different nodes in Firebase. all these different find methods are clumped together in Find class.
At the call sight of a request, if I need to make multiple async request, I nest the next request call inside the closure of the call back. Is this Ok?
The request object allows access to every type of insert, and find method. so in my signup VC I could i have the option to download a chatroom. Is even having access to that kind of implementation bad?
I have posted the code below, and left out all the actual implementation for brevity.
Any tips or guidance is much appreciated!
// USE CASE: Would go in viewDidLoad of ViewController
func testMyRequest () {
let myRequest = Request(insert: Insert(), find: Find())
myRequest.find?.user(with: "id", handler: { (user) in
myRequest.find?.nearbyUsers(user: user, handler: { (users) in
// update collectionView datasource
})
})
}
// Is this protocol necessary?
protocol RequestProtocol {
// - Family of algorithms, related actions.
var insert: Insert? { get set }
var find: Find? { get set }
}
// ---------------------------
class Request: RequestProtocol {
var insert: Insert?
var find: Find?
init(insert: Insert?, find: Find?) {
self.insert = insert
self.find = find
}
}
// Use a singleton maybe for the classes below? Why wouldn't I?
class Insert {
init() { }
func user(_ user: User) {
// insert user to firebase implementation
}
func message(_ message: Message) -> Void {
// insert message to firebase impelmentation
}
func image(data: Data, user: User) {
// insert image to firebase impelmentation
}
}
class Find {
init() { }
func user(with id: String, handler: #escaping (_ user: User) -> Void ) {
// find user implementation
}
func allChatrooms(handler: #escaping ([Chatroom]) -> Void) {
// find all chatrooms implementation
}
func nearbyUsers(user: User, handler: #escaping ([User]) -> Void ) {
// find nearby Users relative to current User location implementation
}
// Private helper method
private func findChatPartners (currentUser: User, chatrooms: [Chatroom] ) -> Set<String> {
}
}

Related

Understanding how to initialize a Vapor 4 repository

I am trying to migrate some code using a Repository pattern from Vapor 3 to Vapor 4. I have gone through the documentation of this specific pattern from the Vapor 4 documentation, and I think I understand it for the most part.
The one thing I am not getting, however, is the way that the repository factory gets set within the Application extension. The example from the documentation shows this:
extension Application {
private struct UserRepositoryKey: StorageKey {
typealias Value = UserRepositoryFactory
}
var users: UserRepositoryFactory {
get {
self.storage[UserRepositoryKey.self] ?? .init()
}
set {
self.storage[UserRepositoryKey.self] = newValue
}
}
}
If I am reading the getter method correctly (and I might not be - I'm far from a Swift expert), a new instance of the UserRepositoryFactory structure will be created and returned when app.users is referenced. At that time, however, it does not appear that the contents of self.storage[UserRepositoryKey.self] is changed in any way. So if I happened to access app.users two times in a row, I would get 2 different instances returned to me and self.storage[UserRepositoryKey.self] would remain set to nil.
Following through the rest of the sample code in the document, it appears to define the make function that will be used by the factory when configuring the app as so:
app.users.use { req in
DatabaseUserRepository(database: req.db)
}
Here it seems like app.users.use would get a new factory instance and call its use function to set the appropriate make method for that instance.
Later, when I go to handle a request, I use the request.users method that was defined by this Request extension:
extension Request {
var users: UserRepository {
self.application.users.make!(self)
}
}
Here it seems like self.application.users.make would be invoked on a different repository factory instance that is referenced by self.application.users. It would therefore not apply the factory's make method that was set earlier when configuring the application.
So what am I missing here?
It looks like the docs are slightly out of date for that. You can have a look at how views or client is done, but somewhere you need to call initialize() to set the repository. Here's what my working repository looks like:
import Vapor
extension Application {
struct Repositories {
struct Provider {
let run: (Application) -> ()
public init(_ run: #escaping (Application) -> ()) {
self.run = run
}
}
final class Storage {
var makeRepository: ((Application) -> APIRepository)?
init() { }
}
struct Key: StorageKey {
typealias Value = Storage
}
let application: Application
var repository: APIRepository {
guard let makeRepository = self.storage.makeRepository else {
fatalError("No repository configured. Configure with app.repositories.use(...)")
}
return makeRepository(self.application)
}
func use(_ provider: Provider) {
provider.run(self.application)
}
func use(_ makeRepository: #escaping (Application) -> APIRepository) {
self.storage.makeRepository = makeRepository
}
func initialize() {
self.application.storage[Key.self] = .init()
}
private var storage: Storage {
if self.application.storage[Key.self] == nil {
self.initialize()
}
return self.application.storage[Key.self]!
}
}
var repositories: Repositories {
.init(application: self)
}
}
That autoinitializes itself the first time it's used. Note that APIRepository is the protocol used for my repostiory. FluentRepository is the Fluent implementation of that protocol. Then like you I have an extension on Request to use it in request handlers:
extension Request {
var repository: APIRepository {
self.application.repositories.repository.for(self)
}
}
Finally, you need to configure it to use the right repository. So in my configure.swift I have:
app.repositories.use { application in
FluentRepository(database: application.db)
}
and in tests I can switch it for the in-memory repository that doesn't touch the DB:
application.repositories.use { _ in
return inMemoryRepository
}
I have managed to get the example from the docs working as-is.
Tracing through the execution with the debugger, there is the predictable call to get, as you say, and this returns the instance from .init() as the failover from not having a previously stored value. Included in the example you refer to is:
struct UserRepositoryFactory {
var make: ((Request) -> UserRepository)?
mutating func use(_ make: #escaping ((Request) -> UserRepository)) {
self.make = make
}
}
This use function is executed next, which is mutating and updates the variable make. I believe it is this change to make that then triggers a call to set. It certainly happens immediately after use and before execution moves on in configure.swift. So, by the time the server formally starts and you actually use the Repository in a route, there is a stored instance that is reused as required.

Dependency Injection for Static Functions for Unit Test in Swift

I know this looks like a common question but after reading 10-15 tutorial and looking how can I write test for my service class. I can't solve moving static functions to protocol or etc.. for dependency injection
I have a network layer like below image. All my function classes (like fetch users, news, media etc..) calls "Service Caller" class and after that If response is error; calls "Service Error" class to handle error and If not error, decode the JSON.
My problem is that I'm calling service class as a static function like "ServiceCaller.performRequest" and If It gets error I'm also calling error class as static like "ServiceError.handle". Also It calls URLCache class to get path of request url. I'm not sure how can I make them dependency inject and mock in test class. As I find in tutorials, I should write it like;
protocol MyProtocol{
func myfunction() -> Void
}
class A{
let testProtocol = MyProtocol!
init(pro: MyProtocol){
testProtocol = pro
}
}
and in setup function in test class it probably;
myMockProtocol = ...
myTestclass = A.init(pro: myMockProtocol)
but I can't find how can I get ride of static calls like ServiceCaller.performRequest or ServiceError.handle..; (Simplified version in the bottom part of question)
class AppInitService{
static func initAppRequest(_ completion: #escaping (_ appInitRecevingModel: Result<AppInitRecevingModel>) -> Void) {
let sendingModel = AppInitSendingModel(cmsVersion: AppDefaults.instance.getCMSVersion())
let route = ServiceRouter(method: .post, path: URLCache.instance.getServiceURL(key: URLKeys.initApp), parameters: (sendingModel.getJSONData()), timeoutSec: 1)
ServiceCaller.performRequest(route: route) { (result) in
if let error = result.error{
if let statusCode = result.response?.statusCode{
completion(.error(ServiceError.handle(error: error, statusCode: statusCode)))
}else{
completion(.error(ServiceError.handle(error: error, statusCode: error._code)))
}
}else{
if let data = result.data{
do{
var responseJson = JSON(data)
responseJson["idleTimeoutInMinutes"] = 10
let input = try AppInitRecevingModel(data: responseJson.rawData())
completion(.success(input))
}catch let error{
completion(.error(ServiceError.handle(error: error, statusCode: -1002)))
}
}
}}
}
}
My Test class:
class MyProjectAppInitTests: XCTestCase {
var appInitTest: AppInitService!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
appInitTest = AppInitService.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
appInitTest = nil
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
let testParamater = ["string":"test"]
let route = ServiceRouter(method: .post, path: "/testPath", parameters: testParamater.getJSONData(), timeoutSec: 10)
appInitTest. //cant call anything in here
}
Tutorials I looked for Unit Test;
https://www.raywenderlich.com/150073/ios-unit-testing-and-ui-testing-tutorial
https://www.swiftbysundell.com/posts/time-traveling-in-swift-unit-tests
https://marcosantadev.com/test-doubles-swift
http://merowing.info/2017/04/using-protocol-compositon-for-dependency-injection/
EDIT: One solution maybe writing init class for whole network layer and service classes then get rid of static functions? But I'm not sure If It will be a good approach.
EDIT 2: Simplified Code;
class A{
static func b(completion:...){
let paramater = ObjectModel(somevariable: SomeClass.Singleton.getVariable()) //Data that I sent on network request
let router = ServiceRouter(somevariable: SomeClassAgain.Singleton.getsomething()) //Router class which gets parameters, http method etc..
NetworkClass.performNetworkRequest(sender: object2){ (result) in
//Result - What I want to test (Write UnitTest about)
}
}
}
Use mocking.
class ServiceCallerMock: ServiceCaller {
override class func performRequest(route: ServiceRouter) -> (Any?) -> Void? {
//your implementation goes here
}
}
You could mock ServiceCaller and override the performRequest method, then change the function to:
static func initAppRequest(_ completion: #escaping (_ appInitRecevingModel: Result<AppInitRecevingModel>) -> Void, serviceCaller: ServiceCaller.Type = ServiceCaller.self) {
...
serviceCaller.performRequest(route: route) { (result) in
...
}
Then you could call the initAppRequest function using your mock implementation of ServiceCaller.

How to use swift-4 Promises then, done, catch and other blocks

I would like to learn about promises in swift-4. How to use multiple then statements and done, catch blocks.
Here I am trying to get the value from the promise. But I'm getting errors. Could someone help me to understand promises?
Here is my code.
import UIKit
import PromiseKit
struct User {
var firstname : String?
var lastname : String?
}
struct APIError {
var message : String?
}
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let userPromise : Promise = self.getUserDetails()
userPromise.then { user -> Void in
//print(user.f)
}
}
func getUserDetails()->Promise<User> {
return Promise<User> { resolve in
let user = User(firstname: "Scot", lastname: "dem")
if ((user.firstname?.count) != nil) {
resolve.fulfill(user)
} else {
let error = APIError(message: "User not valid")
resolve.reject(error as! Error)
}
}
}
}
Once I get the user details I want to make a full name, uppercase promises which are dependent on userPromise.
I would like to use multiple then, done, finally blocks. Just want to understand usage.
Why I'm getting an error here when we use userPromise.then { user -> Void in
what should I give inside the block
In PromiseKit 6, then can no longer return Void. This is mainly due to the tuplegate issue in Swift 4.
Quote from PromieKit 6 Release News
With PromiseKit our then did multiple things, and we relied on Swift
to infer the correct then from context. However with multiple line
thens it would fail to do this, and instead of telling you that the
situation was ambiguous it would invent some other error. Often the
dreaded cannot convert T to AnyPromise. We have a troubleshooting
guide to combat this but I believe in tools that just work, and when
you spend 4 years waiting for Swift to fix the issue and Swift doesn’t
fix the issue, what do you do? We chose to find a solution at the
higher level.
So we split then into then, done and map.
then is fed the previous promise value and requires you return a promise.
done is fed the previous promise value and returns a Void promise (which is 80% of chain usage)
map is fed the previous promise value and requires you return a non-promise, ie. a value.
Hence .then { (user) -> Void in is no longer valid and that's why you're getting an error.
It's now designed to return a Thenable like so:
userPromise.then { user -> Promise<User> in
//return yet another promise
}
The .then that used to return Void is now it's own .done.
i.e:
userPromise.done { (user) in
print(user)
}
So when you mix 'em up:
We get:
userPromise
.then { (user) -> Promise<User> in
//return another Promise
return self.uppercasePromise(on: user)
}
.done { (user) in
/*
Depending on your sequence, no more promises are left
and you should have a matured user object by now
*/
print(user)
}
.catch { (error) in
print(error)
}
.finally {
print("finally")
}
func uppercasePromise(on user: User) -> Promise<User> {
/*
Didn't understand your statement but do whatever you meant when you said:
"Once I get the user details I want to make a full name, "
uppercase promises which are dependent on userPromise."
*/
return Promise { seal in
seal.fulfill(user)
}
}

Swift: Returning a recursive function (currying)

I am wanting to return a function which will in turn call back itself.
Is it possible to do through returning a closure calling itself?
My problem is that I'm unsure of the correct syntax to use here, as well as I'm not sure if it is even possible due to having a cyclic reference to itself (and swift being heavy on compiler type checking)
I am currying my functions so that the models and presenters do not need to know about the dataGateway further decoupling my code
Some background information about the problem, the API expects a page number to be passed into itself, I do not want to store this state. I want the function to pass something back so that the model can just call the next function when it needs to.
I know the curried function definition looks like this:
function (completion: ([Example.Product], UInt) -> Void) -> Example.Task?
look for __function_defined_here__ in my code samples
Original - example code
func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: [Product] -> Void) -> Task? {
return dataGateway.productMap(category, page: page) { products in
completion(products.map { $0.build })
}
}
Idea 1 - return as tuple
func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: [Product] -> Void) -> (Task?, __function_defined_here__) {
return (dataGateway.productMap(category, page: page) { products in
completion(products.map { $0.build })
}, fetch(dataGateway, category: category)(page: page + 1))
}
Idea 2 - pass back in the completion
func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: ([Product], __function_defined_here__) -> Void) -> Task? {
return dataGateway.productMap(category, page: page) { products in
completion(products.map { $0.build }, fetch(dataGateway, category: category)(page: page + 1))
}
}
I ended up solving it with something like the following, what it does is create a class reference to store the next function in. I pass a reference to this object in the completion of the asynchronous operation.
extension Product {
class Next {
typealias FunctionType = (([Product], Next) -> Void) -> Task?
let fetch: FunctionType
init(_ fetch: FunctionType) {
self.fetch = fetch
}
}
func fetch(dataGateway: DataGateway, inCategory category: String)(page: UInt)(completion: ([Product], Next) -> Void) -> Task? {
return dataGateway.products(inCategory: category, page: page)() { products in
completion(products.map { $0.build }, Next(fetch(dataGateway, inCategory: category)(page: page + 1)))
}
}
}
let initial = Product.fetch(dataGateway, inCategory: "1")(page: 0)
pass the function in to a data model
data() { [weak self] products, next in
self?.data = products
self?.setNeedsUpdate()
self?.next = next
}
scrolling down to bottom of table view triggers the above again, using the next function instead of data

Swift callbacks syntax issue

I'm new to Swift, and I'm trying to declare a function that receives a callback.
func getAll(callback: (students: [Student]!) -> Void) {
// http request to get a list of students and parse it
callback(students: students)
}
And when calling the function, I'm doing:
obj.getAll() {
(students: [Student]!) in
// Callback code
}
But it won't build, it says: Cannot invoke getAll with an argument list of type '(([Student]!) -> _)'
I was following this thread as a guide, what did I miss?
struct Student {
}
func getAll(callback: (students: [Student]!) -> Void) {
// http request to get a list of students and parse it
let students = [Student]()
callback(students: students)
}
getAll { (students) -> Void in
println(students)
}
You do not send an argument students, but receive a parameter, that is why you implement it like this:
obj.getAll { (students) -> Void in
// Callback code
}
If you are not sure about the closures, always use autocomplete and you won't have to worry about the syntax. Hope this helps.
Remove type declaration in your call
obj.getAll() {
students in
// Callback code
}

Resources