How to mock classes of external framework with delegates in iOS? - ios

I am working in an iOS application called ConnectApp and I am using a framework called Connector. Now, Connector framework completes actual connection task with BLE devices and let my caller app (i.e. ConnectApp) know the connection request results through ConnectionDelegate. Let's see example code,
ConnectApp - host app
class ConnectionService: ConnectionDelegate {
func connect(){
var connector = Connector()
connector.setDelegate(self)
connector.connect()
}
func onConnected(result: ConnectionResult) {
//connection result
}
}
Connector Framework
public class ConnectionResult {
// many complicated custom variables
}
public protocol ConnectionDelegate {
func onConnected(result: ConnectionResult)
}
public class Connector {
var delegate: ConnectionDelegate?
func setDelegate(delegate: ConnectionDelegate) {
self.delegate = delegate
}
func connect() {
//…..
// result = prepared from framework
delegate?.onConnected(result)
}
}
Problem
Sometimes developers have no BLE device and we need to mock the Connector layer of framework. In case of simple classes (i.e. with simpler methods) we could have used inheritance and mock the Connector with a MockConnector which might override the lower tasks and return status from MockConnector class. But when I need to deal with a ConnectionDelegate which returns complicated object. How can I resolve this issue?
Note that framework does not provide interfaces of the classes rather we need to find way around for concrete objects like, Connector, ConnectionDelegate etc.
Update 1:
Trying to apply Skwiggs's answer so I created protocol like,
protocol ConnectorProtocol: Connector {
associatedType MockResult: ConnectionResult
}
And then injecting real/mock using strategy pattern like,
class ConnectionService: ConnectionDelegate {
var connector: ConnectorProtocol? // Getting compiler error
init(conn: ConnectorProtocol){
connector = conn
}
func connect(){
connector.setDelegate(self)
connector.connect()
}
func onConnected(result: ConnectionResult) {
//connection result
}
}
Now I am getting compiler error,
Protocol 'ConnectorProtocol' can only be used as a generic constraint because it has Self or associated type requirements
What am I doing wrong?

In Swift, the cleanest way to create a Seam (a separation that allows us to substitute different implementations) is to define a protocol. This requires changing the production code to talk to the protocol, instead of a hard-coded dependency like Connector().
First, create the protocol. Swift lets us attach new protocols to existing types.
protocol ConnectorProtocol {}
extension Connector: ConnectorProtocol {}
This defines a protocol, initially empty. And it says that Connector conforms to this protocol.
What belongs in the protocol? You can discover this by changing the type of var connector from the implicit Connector to an explicit ConnectorProtocol:
var connector: ConnectorProtocol = Connector()
Xcode will complain about unknown methods. Satisfy it by copying the signature of each method it needs into the protocol. Judging from your code sample, it may be:
protocol ConnectorProtocol {
func setDelegate(delegate: ConnectionDelegate)
func connect()
}
Because Connector already implements these methods, the protocol extension is satisfied.
Next, we need a way for the production code to use Connector, but for test code to substitute a different implementation of the protocol. Since ConnectionService creates a new instance when connect() is called, we can use a closure as a simple Factory Method. The production code can supply a default closure (creating a Connector) like with a closure property:
private let makeConnector: () -> ConnectorProtocol
Set its value by passing an argument to the initializer. The initializer can specify a default value, so that it makes a real Connector unless told otherwise:
init(makeConnector: (() -> ConnectorProtocol) = { Connector() }) {
self.makeConnector = makeConnector
super.init()
}
In connect(), call makeConnector() instead of Connector(). Since we don't have unit tests for this change, do a manual test to confirm we didn't break anything.
Now our Seam is in place, so we can begin writing tests. There are two types of tests to write:
Are we calling Connector correctly?
What happens when the delegate method is called?
Let's make a Mock Object to check the first part. It's important that we call setDelegate(delegate:) before calling connect(), so let's have the mock record all calls in an array. The array gives us a way to check the call order. Instead of having the test code examine the array of calls (acting as a Test Spy which just records stuff), your test will be cleaner if we make this a full-fledged Mock Object — meaning it will do its own verification.
final class MockConnector: ConnectorProtocol {
private enum Methods {
case setDelegate(ConnectionDelegate)
case connect
}
private var calls: [Methods] = []
func setDelegate(delegate: ConnectionDelegate) {
calls.append(.setDelegate(delegate))
}
func connect() {
calls.append(.connect)
}
func verifySetDelegateThenConnect(
expectedDelegate: ConnectionDelegate,
file: StaticString = #file,
line: UInt = #line
) {
if calls.count != 2 {
fail(file: file, line: line)
return
}
guard case let .setDelegate(delegate) = calls[0] else {
fail(file: file, line: line)
return
}
guard case .connect = calls[1] else {
fail(file: file, line: line)
return
}
if expectedDelegate !== delegate {
XCTFail(
"Expected setDelegate(delegate:) with \(expectedDelegate), but was \(delegate)",
file: file,
line: line
)
}
}
private func fail(file: StaticString, line: UInt) {
XCTFail("Expected setDelegate(delegate:) followed by connect(), but was \(calls)", file: file, line: line)
}
}
(That business with passing around file and line? This makes it so that any test failure will report the line that calls verifySetDelegateThenConnect(expectedDelegate:), instead of the line that calls XCTFail(_).)
Here's how you'd use this in ConnectionServiceTests:
func test_connect_shouldMakeConnectorSettingSelfAsDelegateThenConnecting() {
let mockConnector = MockConnector()
let service = ConnectionService(makeConnector: { mockConnector })
service.connect()
mockConnector.verifySetDelegateThenConnect(expectedDelegate: service)
}
That takes care of the first type of test. For the second type, there's no need to test that Connector calls the delegate. You know it does, and it's outside your control. Instead, write a test to call the delegate method directly. (You'll still want it to make a MockConnector to prevent any calls to the real Connector).
func test_onConnected_withCertainResult_shouldDoSomething() {
let service = ConnectionService(makeConnector: { MockConnector() })
let result = ConnectionResult(…) // Whatever you need
service.onConnected(result: result)
// Whatever you want to verify
}

You could try
protocol MockConnector: Connector {
associatedType MockResult: ConnectionResult
}
Then, for each connector you need to mock, define a concrete class that conforms to this mock connector
class SomeMockConnector: MockConnector {
struct MockResult: ConnectionResult {
// Any mocked variables for this connection result here
}
// implement any further requirements from the Connector class
var delegate: ConnectionDelegate?
func connect() {
// initialise your mock result with any specific data
let mockResult = MockResult()
delegate?.onConnected(mockResult)
}
}

Related

Vapor 4: creating a protocol that contains a Fluent ParentProperty results in compiler errors

I have a whole bunch of Fluent (Vapor 4) models, all which have a parent field like this:
final class Location: Model, Content {
// .. bunch of other properties
#Parent(key: FieldKeys.campaign)
var campaign: Campaign
}
Now, I want to make a protocol that can be applied to all these models, like this:
protocol HasCampaignId: Model {
var _campaign: ParentProperty<Self, Campaign> { get }
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self
}
extension HasCampaignId {
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self {
if self._campaign.id != campaignID {
throw Abort(.forbidden)
}
return self
}
}
Just a convenient little function I can apply to any instance of a model that HasCampaignId, that's the idea. Sadly this doesn't compile, I get the error Property '_campaign' must be declared internal because it matches a requirement in internal protocol 'HasCampaignId'. I can make the protocol public, but then I just get another error: Property '_campaign' must be as accessible as its enclosing type because it matches a requirement in protocol 'HasCampaignId'.
I could change the protocol like this:
protocol HasCampaignId: Model {
var campaign: Campaign { get }
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self
}
extension HasCampaignId {
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self {
if self.campaign.id != campaignID {
throw Abort(.forbidden)
}
return self
}
}
But that requires me to load the campaign relation, which is usually not what I want - otherwise it crashes with a fatal error: Fatal error: Parent relation not eager loaded, use $ prefix to access: Parent<Loot, Campaign>(key: campaign_id).
The campaign property within my protocol can't have a property wrapper applied either.
So how can I have a protocol that requires a ParentProperty field? How can I solve the compiler errors?
As of Swift 5.4 and 5.5 - you can't. Property wrappers in protocols have been pitched but they're not implemented and it's impossible for enforce. There was a lot of effort to try and work around these restrictions in Fluent but it's not currently possible to do what you're trying.

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.

Binding Protocols with Associated Objects - Protocol Oriented Programming with Swift

i'm an iOS dev with a couple of years of experience with swift, but rarely i've used PAT's...
This time, I was trying to move some code from an app that i've developed to a shared library that I use in a couple of projects. The case is about a Factory that uses various Builders (that are decorators of my business resources) via an Abstract Builder protocol, to obtain Items (in the real case, ViewControllers).
The Builder relays upon some variables that the Factory passes to him, but those are at the application level, so, to extract this logic and put it into my library, i need to use a generic reference, and because I want to work in a Protocol Oriented Programming manner, it is an AssociatedType.
// The item that i want to receive from my factory
protocol Item {
var content: String { get }
}
// This is the Builder interface that the Factory consumes
protocol Builder {
// The Abstract Parameters that the Application should define
associatedtype Parameters
func build(_ parameters: Parameters) -> Item?
}
// The BusinessResource of my library
protocol BusinessResource { }
// The Factory that consumes the Builders
protocol Factory {
associatedtype FactoryBuilder: Builder
var parameters: FactoryBuilder.Parameters { get }
func make(from businessResource: BusinessResource) -> Item?
}
// The generic implementation of my Factory
extension Factory {
func make(from businessResource: BusinessResource) -> Item? {
guard let builder = businessResource as? FactoryBuilder else {
return nil
}
return builder.build(self.parameters)
}
}
At this point everything looks good.
I have two protocols and those are binded together, sharing a common type who is generic (the Builder Parameters).
So, on the application layer, now i could introduce my concrete Parameters (i'll call them ConcreteParameters XD)
// The concrete parameters of the Application Factory
struct ConcreteParameters {
let string: String
}
// The Builder interface restricting Parameters to ConcreteParameters
protocol BindedBuilder: Builder where Parameters == ConcreteParameters {
}
// The Factory interface restricting Parameters to ConcreteParameters
protocol BindedFactory: AbstractFactory where FactoryParameters: ConcreteParameters {
}
So far, so good. Everything looks in place and I'm start thinking that this could work, so now i try to implement a concrete Factory on the application to try if this really works.
// The concrete output of my Builder
struct ConcreteItem: Item {
var content: String
}
// The concrete BusinessResource that i get from my library
struct ConcreteObject: BusinessResource {
let string: String
}
// The decoration extension that makes ConcreteObject compliant with Builder
extension ConcreteObject: Builder {
typealias Parameters = ConcreteParameters
func build(_ parameters: ConcreteParameters) -> Item? {
return ConcreteItem(content: parameters.string + self.string)
}
}
// The real Factory inside my app
class ConcreteFactory: BindedFactory {
typealias FactoryBuilder = BindedBuilder
var parameters: ConcreteParameters {
return ConcreteParameters(string: "Hello ")
}
}
let item = ConcreteFactory().make(from: ConcreteObject(string: "world!"))
print(item ?? "NOT WORKING")
At this point something breaks... I get this error:
[EDIT: Error came from a previous version of the snippet, AbstractFactori is current Factory]
It is a Bug??
I really don't know how to solve this...
I think in this case you need to use a concrete type to alias FactoryBuilder instead of BindedBuilder, as protocols do not conform to themselves.
This code effectively compiles, would something like that match your requirements?
class ConcreteFactory: BindedFactory {
typealias FactoryBuilder = ConcreteObject
var parameters: ConcreteParameters {
return ConcreteParameters(string: "Hello ")
}
}
Otherwise you can also try type erasing BindedBuilder and create AnyBindedBuilder, as suggested in the same link.

Where to put reusable functions in IOS Swift?

New to IOS programming but just wondering where is the best place to put functions that I would use throughout my code. For example, I want to write a few functions to perform a POST request to a web service and return a dictionary. Maybe another function to do some calculations. Is it best to create another .swift file and put all my functions there. And what would be a good name to give the file if so?
public func postRequest() -> [String:String] {
// do a post request and return post data
return ["someData" : "someData"]
}
The best way is to create a helper class with static functions, like this:
class Helper{
static func postRequest() -> [String:String] {
// do a post request and return post data
return ["someData" : "someData"]
}
}
Now every time you need to use postRequest you can just use like so: Helper.postRequest()
I usually create a separate class if I have functions that will be used by multiple classes, especially for the ones involving network operations.
If you just have separate functions that will be used, you can simply create static functions inside that class so it is easily accessible by other classes in a static way:
class DataController {
static func getData() -> [String:String] {
// do some operations
return ["someData" : "someData"]
}
}
let data = DataController.getData() // example
However, what often has been the case for me (especially if it involves more complicated operations) was that these network operations needed to establish an initial connection beforehand or required some initial setups, and they also performed asynchronous operations that needed to be controlled. If this is the case and you will often be calling such methods, you might want to create a singleton object that you could use throughout different classes and functions. This way, you could do the initial setup or establish an initial connection just once, and then do the rest as needed with the other functions, instead of doing them every time the function gets called.
Creating a singleton object is pretty simple in Swift:
class DataController {
static let sharedInstance = DataController() // singleton object
init() {
// do initial setup or establish an initial connection
}
func getData() -> [String:String] {
// do some operations
return ["someData" : "someData"]
}
}
let data = DataController.sharedInstance.getData() // example
For the name of the class, I usually name it something like DataController or DataHelper, but anything that makes sense as a "helper" class would work.
Hope this helps :)
For reusable functions it depends what I decide to use. For this specific case I use a separate file, because posting to a backend will become more complicated when the application evolves. In my app I use a backend class, with all kinds of helper classes:
struct BackendError {
var message : String
}
struct SuccessCall {
var json : JSON
var containsError : Bool {
if let error = json["error"].string {
return true
}
else {
return false
}
}
}
typealias FailureBlock = (BackendError) -> Void
typealias SuccessBlock = (SuccessCall) -> Void
typealias AlamoFireRequest = (path: String, method: Alamofire.Method, data: [String:String]) -> Request
typealias GetFunction = (path: String , data: [String : String], failureBlock: FailureBlock, successBlock: SuccessBlock) -> Void
class Backend {
func getRequestToBackend (token: String )(path: String , data: [String : String], failureBlock: FailureBlock, successBlock:
}
For other cases I often use extensions on Swift classes. Like for getting a random element from an Array.
extension Array {
func sampleItem() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
This very old question but I would like to chirp some more points.
There are a few option, basically you can write your utility functions in Swift -
A class with static function. For example
class CommonUtility {
static func someTask() {
}
}
// uses
CommonUtility.someTask()
Also, you can have class method's as well instead of static method but those functions can be overridden by subclasses unlike static functions.
class CommonUtility {
class func someTask() {
}
}
// uses
CommonUtility.someTask()
Secondly, you can have Global functions as well, that are not part of any class and can be access anywhere from your app just by name.
func someTask() {
}
Though, selecting one over other is very subjective and I thing this is ok to make a class with static function in this particular case, where you need to achieve networking functionality but if you have some functions which perform only one task than Global function is a way to go because Global functions are more modular and separate out single tasks for a single function.
In case of static functions, if we access one of the static member, entire class gets loaded in memory. But in case of global function, only that particular function will be loaded in mem
You can create a separate swift class, might name it WebServicesManager.swift, and write all methods related to web requests in it.
You can use class methods, or singleton pattern to access the methods.

How to reference a class that follows a protocol in return type of function?

I have a protocol called Social Service, declared as follows:
protocol SocialService: class {
class func testFunc()
}
A class that follows the protocol may look like this:
class Twitter: SocialService {
class func testFunc() {
}
}
I want to have a method which returns a class that follows this protocol, so calling it would look like this:
let socialService = socialServiceForServiceType(serviceType: String)
I'm not sure what I need to put as the return value type of this function. For example, this:
func socialServiceForServiceType(serviceType: String) -> SocialService.Type
doesn't give an error right here, but trying to call it as above, gives an error:
Accessing members of protocol type value 'SocialService.Type' is
unimplemented
EDIT: I don't want an instance of that type, I want a class of that type. So I want a Twitter class, so I can call the class methods from the SocialService protocol on it.
Like the error says, this feature is unimplemented. However...
I don't want an instance of that type, I want a class of that type. So I want a Twitter class, so I can call the class methods from the SocialService protocol on it.
I'm not sure what you think you're getting from avoiding instances like this. Bear in mind classes don’t need to have member variables, and without them are essentially just collection of function pointers – which is what you seem to be looking for.
If you implement a Twitter class that has no properties and that conforms to a protocol, then calling methods on that protocol will dynamically dispatch to the implementations of that instance:
protocol SocialService: class {
func testFunc()
}
class Twitter: SocialService {
func testFunc() {
println("Testing Twitter!")
}
}
func socialServiceForServiceType(serviceType: String) -> SocialService {
return Twitter()
}
let service = socialServiceForServiceType("blah")
// prints "Testing Twitter!"
service.testFunc()
If your concern is that you want to put member variables in the Twitter class, but don’t want the overhead of that for some features, then this probably suggests you want to decompose this functionality into two different classes. Alternatively, if you want a singleton instance (to handle the connectivity for example) then there are other patterns to handle this.
Use simply
func socialServiceForServiceType(serviceType: String) -> SocialService
A protocol can be the return type of a function.
Totally agree with Airspeed Velocity, but I'd like to expand on one of his points:
I'm not sure what you think you're getting from avoiding instances like this. Bear in mind classes don’t need to have member variables, and without them are essentially just collection of function pointers – which is what you seem to be looking for.
I assume you're trying to do something like this:
func socialServiceForServiceType(serviceType: String) -> SocialService.Type
...
let cls = socialServiceForServiceType("twitter")
let conn = cls.connect(user)
Or something like that. You don't need classes to achieve that. You can just return functions.
typealias Connect = User -> Connection
func connectorForServiceType(serviceType: String) -> Connect {
switch serviceType {
case "twitter": return Twitter.Connect
...
}
}
let connect = connectorForServiceType("twitter")
let conn = connect(user)
If you have a whole bundle of functions that you want to package together, just use a struct.
struct ServiceHandlers {
let connect : User -> Connection
let ping : () -> Bool
let name: () -> String
}
func standardPinger(host: String) -> () -> Bool {
return { host in
// perform an ICMP ping and return Bool
}
}
func handlersForServiceType(serviceType: String) -> ServiceHandlers {
switch serviceType {
case "twitter":
return ServiceHandlers(connect: Twitter.connect,
ping: standardPinger("www.twitter.com"),
name: { "Twitter" })
...
}
}
let service = handlersForServiceType("twitter")
let conn = service.connect(user)
In some ways this is duplicative with class methods, but (a) the features you need for class methods aren't implemented, and (b) this is much more flexible. You can return any collection of functions you want; they don't have to all be class methods. It's easier to have default behaviors (which are hard in Swift when you use inheritance). It's easier to extend because you don't necessarily have to extend all the classes (see my use of standardPinger, which is some function I've made up that returns another function; it doesn't have to be a class method).
Breaking free of class/inheritance thinking and just passing around functions can be a major benefit in Swift. Sometimes a struct is better than a protocol.
Use a Factory pattern to achieve the same.
class SocialFactory : NSObject
{
class func socialServiceForServiceType(serviceType: String) -> SocialService?
{
switch serviceType
{
case "Twitter":
return Twitter();
case "Facebook":
return Facebook()
default:
return nil;
}
}
}

Resources