I want to know the equivalent part in ReactiveSwift for this PromiseKit code snippet
import PromiseKit
// import ReactiveSwift
class MockA {
....
public var addressReturnValue: Promise<String>?
public func createAddress() -> Promise<String> {
if let promise = addressReturnValue {
return promise
}
return Promise<String>.pending().promise
}
....
}
I think it would be something like this but not sure about the pending method
// import PromiseKit
import ReactiveSwift
class MockA {
....
public var addressReturnValue: SignalProducer<String, Error>?
public func createAddress() -> SignalProducer<String, Error> {
if let producer = addressReturnValue {
return producer
}
// return SignalProducer<String, Error>.someMethod() ??????
}
....
}
Related
I have a class with static function like this.
UserAuthentication.swift
final class UserAuthentication {
/// Check is logged in
/// - Returns: boolean true is login, false not login
#objc public static func isLogin() -> Bool {
return true
}
}
I want to write a unit test for checkTermAndCondition function which call it in my HomeInteractor class
HomeInteractor.swift
class HomeInteractor: HomeInteractorBusinessLogic {
var presenter: HomePresenterInterface!
var worker: HomeWorker = HomeWorker(with: HomeService())
func checkTermAndCondition() {
if UserAuthentication.isLogin() {
///do true case
} else {
///do fasle case
}
}
}
Have anyone ever done it ?
Appreciate any help from you.
You cannot mock a static method on a final class.
You should either change the method to class and make the class non-final or even better, inject UserAuthentication as a protocol to HomeInteractor and in your unit tests, inject a mock type rather than your real production type.
protocol UserAuthenticator {
/// Check is logged in
/// - Returns: boolean true is login, false not login
static func isLogin() -> Bool
}
final class UserAuthentication: UserAuthenticator {
#objc public static func isLogin() -> Bool {
return true
}
}
final class UserAuthenticationMock: UserAuthenticator {
static var shouldLogin: Bool = false
static func isLogin() -> Bool {
shouldLogin
}
}
class HomeInteractor: HomeInteractorBusinessLogic {
var presenter: HomePresenterInterface!
var worker: HomeWorker = HomeWorker(with: HomeService())
let userAuthenticator: UserAuthenticator.Type
init(userAuthenticator: UserAuthenticator.Type) {
self.userAuthenticator = userAuthenticator
}
func checkTermAndCondition() {
if userAuthenticator.isLogin() {
} else {
}
}
}
Inject UserAuthentication.self for your prod code, while UserAuthenticationMock.self for the tests.
let prodHomeInteractor = HomeInteractor(userAuthenticator: UserAuthentication.self)
let testHomeInteractor = HomeInteractor(userAuthenticator: UserAuthenticationMock.self)
I'm trying to create an app to get some news from an API and i'm using Moya, RxSwift and MVVM.
This is my ViewModel:
import Foundation
import RxSwift
import RxCocoa
public enum NewsListError {
case internetError(String)
case serverMessage(String)
}
enum ViewModelState {
case success
case failure
}
protocol NewsListViewModelInput {
func viewDidLoad()
func didLoadNextPage()
}
protocol MoviesListViewModelOutput {
var newsList: PublishSubject<NewsList> { get }
var error: PublishSubject<String> { get }
var loading: PublishSubject<Bool> { get }
var isEmpty: PublishSubject<Bool> { get }
}
protocol NewsListViewModel: NewsListViewModelInput, MoviesListViewModelOutput {}
class DefaultNewsListViewModel: NewsListViewModel{
func viewDidLoad() {
}
func didLoadNextPage() {
}
private(set) var currentPage: Int = 0
private var totalPageCount: Int = 1
var hasMorePages: Bool {
return currentPage < totalPageCount
}
var nextPage: Int {
guard hasMorePages else { return currentPage }
return currentPage + 1
}
private var newsLoadTask: Cancellable? { willSet { newsLoadTask?.cancel() } }
private let disposable = DisposeBag()
// MARK: - OUTPUT
let newsList: PublishSubject<NewsList> = PublishSubject()
let error: PublishSubject<String> = PublishSubject()
let loading: PublishSubject<Bool> = PublishSubject()
let isEmpty: PublishSubject<Bool> = PublishSubject()
func getNewsList() -> Void{
print("sono dentro il viewModel!")
NewsDataService.shared.getNewsList()
.subscribe { event in
switch event {
case .next(let progressResponse):
if progressResponse.response != nil {
do{
let json = try progressResponse.response?.map(NewsList.self)
print(json!)
self.newsList.onNext(json!)
}
catch _ {
print("error try")
}
} else {
print("Progress: \(progressResponse.progress)")
}
case .error( _): break
// handle the error
default:
break
}
}
}
}
This is my ViewController, where xCode give me the following error when i try to bind to tableNews:
Expression type 'Reactive<_>' is ambiguous without more context
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
#IBOutlet weak var tableNews: UITableView!
let viewModel = DefaultNewsListViewModel()
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
}
private func setupBindings() {
viewModel.newsList.bind(to: tableNews.rx.items(cellIdentifier: "Cell")) {
(index, repository: NewsList, cell) in
cell.textLabel?.text = repository.name
cell.detailTextLabel?.text = repository.url
}
.disposed(by: disposeBag)
}
}
This is the service that get data from API:
import Moya
import RxSwift
struct NewsDataService {
static let shared = NewsDataService()
private let disposable = DisposeBag()
private init() {}
fileprivate let newsListProvider = MoyaProvider<NewsService>()
func getNewsList() -> Observable<ProgressResponse> {
self.newsListProvider.rx.requestWithProgress(.readNewsList)
}
}
I'm new at rxSwift, I followed some documentation but i'd like to know if i'm approaching in the right way. Another point i'd like to know is how correctly bind my tableView to viewModel.
Thanks for the support.
As #FabioFelici mentioned in the comments, UITableView.rx.items(cellIdentifier:) is expecting to be bound to an Observable that contains an array of objects but your NewsListViewModel.newsList is an Observable<NewsList>.
This means you either have to extract the array out of NewsList (assuming it has one) through a map. As in newsList.map { $0.items }.bind(to:...
Also, your MoviesListViewModelOutput should not be full of Subjects, rather it should contain Observables. And I wouldn't bother with the protocols, struts are fine.
Also, your view model is still very imperative, not really in an Rx style. A well constructed Rx view model doesn't contain functions that are repeatedly called. It just has a constructor (or is itself just a single function.) You create it, bind to it and then you are done.
I am trying to work with swift generics, but I am stuck...
It might be impossible to do it this way, but I was hoping someone would have a good suggestion.
So I have this protocol and a type that I want to have internal:
internal protocol ATCoreInstrumentProtocol { // SOME STUFF }
internal typealias AT_1G_WaveInstrumentType = //Somethings that conforms to ATCoreInstrumentProtocol
Then I have this InstrumentType that I want to be public.
The problem here is that ASSOCIATED_INSTRUMENT aka ATCoreInstrumentProtocol
needs to be internal and therefore I cannot use it in this way.
There is not an option to make the ATCoreInstrumentProtocol public.
public protocol InstrumentType {
static var instrumentType: SupportedInstrumentTypes { get }
associatedtype ASSOCIATED_INSTRUMENT: ATCoreInstrumentProtocol
}
public final class InstrumentTypes {
private init() {}
public final class AT_1G_Wave : InstrumentType {
public class var instrumentType: SupportedInstrumentTypes { get { return .wave_1G } }
public typealias ASSOCIATED_INSTRUMENT = AT_1G_WaveInstrumentType
}
}
This is how I want to use it.
internal class ATCoreInteractor<IT: InstrumentType> {
internal var instrumentObservable : Observable<IT.ASSOCIATED_INSTRUMENT> {
return self.instrumentSubject.asObservable()
}
private var instrumentSubject = ReplaySubject<IT.ASSOCIATED_INSTRUMENT>.create(bufferSize: 1)
internal init(withSerial serial: String){
self.scanDisposable = manager
.scanFor([IT.instrumentType])
.get(withSerial: serial)
.take(1)
.do(onNext: { (inst) in
self.instrumentSubject.onNext(inst)
})
.subscribe()
}
And then I have the ATSyncInteractor which is public
public final class ATSyncInteractor<IT : InstrumentType> : ATCoreInteractor<IT> {
public override init(withSerial serial: String) {
super.init(withSerial: serial)
}
}
public extension ATSyncInteractor where IT : InstrumentTypes.AT_1G_Wave {
public func sync(opPriority: BLEOperationPriority = .normal, from: Date, to: Date, callback: #escaping (ATCoreReadyData<AT_1G_Wave_CurrentValues>?, [WaveTimeSeriesCoordinator], Error?) -> Void) {
// DO SOMETHING
}
}
let interactor = ATSyncInteractor<InstrumentTypes.AT_1G_Wave>(withSerial: "12345")
This ended up beeing my solution..
The InstrumentType only contains the SupportedInstrumentTypes.
The ATCoreInteractor Is no longer generic, but the observer is of type ATCoreInstrumentProtocol.
The ATSyncInteractor extensions casts the instrument to its associated type.
I am open to improvements and suggestions.
public protocol InstrumentType : class {
static var instrumentType: SupportedInstrumentTypes { get }
}
public final class InstrumentTypes {
private init() {}
public final class AT_1G_Wave : InstrumentType {
private init() {}
public class var instrumentType: SupportedInstrumentTypes { get { return .wave_1G } }
}
}
public class ATCoreInteractor {
internal var instrumentSubject = ReplaySubject<ATCoreInstrumentProtocol>.create(bufferSize: 1)
internal init(for type: SupportedInstrumentTypes, withSerial serial: String){
self.scanDisposable = manager
.scanFor([type])
.get(withSerial: serial)
.take(1)
.do(onNext: { (inst) in
self.instrumentSubject.onNext(inst)
})
.subscribe()
}
}
public final class ATSyncInteractor<IT : InstrumentType> : ATCoreInteractor {
public init(withSerial serial: String) {
super.init(for: IT.instrumentType, withSerial: serial)
}
}
public extension ATSyncInteractor where IT : InstrumentTypes.AT_1G_Wave {
private func instrument() -> Observable<AT_1G_WaveInstrumentType> {
return self.instrumentSubject.map { $0 as! AT_1G_WaveInstrumentType }
}
public func sync(opPriority: BLEOperationPriority = .normal, from: Date, to: Date, callback: #escaping (ATCoreReadyData<AT_1G_Wave_CurrentValues>?, [WaveTimeSeriesCoordinator], Error?) -> Void) {
}
}
What I want to achieve is to wait for all service calls to complete. I know that it can be done with GCD, but I'm looking for more Object Oriented Approach. Here is what I've so far:
First services should notify delegate for their completion, so we will need a protocol for that:
protocol ParallelServiceDelegate: class {
func serviceDidCompleted()
}
Services are Alamofire requests and we are getting their response as:
enum ServiceResult {
case Success(NSDictionary)
case Failure(NSError)
}
My design is to add Facade (wrapper) over this methods. This is the abstract:
import ObjectMapper
protocol ParallelService: class {
associatedtype ItemType: Mappable
var item: ItemType? { get }
var error: NSError? { get }
var isCompleted: Bool { get }
weak var delegate: ParallelServiceDelegate? { get set }
// TODO: Pass params
func start()
func handleRequestCompletion(result: ServiceResult)
}
private var psiAssociationKey: UInt8 = 0
private var pseAssociationKey: UInt8 = 0
private var psdAssociationKey: UInt8 = 0
extension ParallelService {
var item: ItemType? {
return _item
}
var error: NSError? {
return _error
}
var isCompleted: Bool {
return item != nil || error != nil
}
weak var delegate: ParallelServiceDelegate? {
get {
let object = objc_getAssociatedObject(self, &psdAssociationKey)
let wrapper = object as? WeakWrapper<ItemType?>
return wrapper?.value as? ParallelServiceDelegate
}
set(newValue) {
objc_setAssociatedObject(self,
&psdAssociationKey,
WeakWrapper(value: newValue),
.OBJC_ASSOCIATION_RETAIN)
}
}
func handleRequestCompletion(result: ServiceResult) {
switch result {
case .Success(let json):
_item = map(json, object: ItemType.self)
case .Failure(let error):
_error = error
}
}
// Degfault is nothing
func start() {}
// MARK: - Private
private var _item: ItemType? {
get {
let object = objc_getAssociatedObject(self, &psiAssociationKey)
let wrapper = object as? WeakWrapper<ItemType?>
return wrapper?.value as? ItemType
}
set(newValue) {
objc_setAssociatedObject(self,
&psiAssociationKey,
WeakWrapper(value: newValue),
.OBJC_ASSOCIATION_RETAIN)
}
}
private var _error: NSError? {
get {
let object = objc_getAssociatedObject(self, &pseAssociationKey)
return object as? NSError
}
set(newValue) {
objc_setAssociatedObject(self,
&pseAssociationKey,
newValue,
.OBJC_ASSOCIATION_RETAIN)
}
}
}
And Specific Service Facade implementation:
class EmployeesParallelService: ParallelService {
typealias ItemType = Employee
func start() {
Service.emploeesList(callback: handleRequestCompletion)
}
}
class InformationParallelService: ParallelService {
typealias ItemType = Information
func start() {
Service.information(callback: handleRequestCompletion)
}
}
Service caller - Knows nothing about services it just starts all and waits for them to complete:
class ParallelServiceCaller {
private var services: [ParallelService] = []
// MARK: - Lifecycle
func addParallelService(service: ParallelService) {
service.delegate = self
self.services.append(service)
}
// MARK: - Public
func start() {
for service in services {
service.start()
}
}
}
extension ParallelServiceCaller: ParallelServiceDelegate {
func serviceDidCompleted() {
for service in services {
// !!! wait
if !service.isCompleted {
return
}
}
// TODO: Notify delegate
}
}
Latter I want to use it like this:
let caller = ParallelServiceCaller()
caller.addParallelService(EmployeesParallelService())
caller.addParallelService(InformationParallelService())
caller.start()
However I got problem in the implementation of the ParallelServiceCaller class. I'm getting the following error:
Protocol 'ParallelService' can only be used as a generic constraint
because it has Self or associated type requirements
Any idea how to avoid this error?
Update 07/07/16:
I'm still not able to understand how to use PATs. However I took slightly different approach and now I'm using visitor pattern. Here is my playground code, it may be helpful for someone:
//: Playground - noun: a place where people can play
import UIKit
// Mocks
enum ServiceResult {
case Success(NSDictionary)
case Failure(NSError)
}
protocol Mappable { }
typealias CompletionHandlerType = (result: ServiceResult) -> Void
class Service {
class func emploeesList(start: Int? = nil,
max: Int? = nil,
timestamp: Int? = nil,
callback: CompletionHandlerType) {
callback(result: .Success(NSDictionary()))
}
class func information(timestamp: Int? = nil,
callback: CompletionHandlerType) {
callback(result: .Failure(NSError(domain: "", code: 1, userInfo: nil)))
}
}
class EmployeesList: Mappable {}
class Information: Mappable {}
// Actual Implementation
// Visitor
protocol ParallelServiceCallerProtocol {
func call(service: EmployeesListParallelService)
func call(service: InformationParallelService)
}
// Element
protocol ParallelServiceProtocol {
func start(visitor: ParallelServiceCallerProtocol)
}
// Concrete Elements
class EmployeesListParallelService: ParallelServiceProtocol {
func start(visitor: ParallelServiceCallerProtocol) { visitor.call(self) }
}
class InformationParallelService: ParallelServiceProtocol {
func start(visitor: ParallelServiceCallerProtocol) { visitor.call(self) }
}
// Concrete Visitor Delegate - defines callback for async tasks
protocol ParallelServiceCallerDelegateProtocol: class {
func didCompleteParellelServiceWithResult(service: ParallelServiceProtocol, result: ServiceResult)
}
// Concrete Visitor - make API calls
class ParallelServiceCaller <T: ParallelServiceCallerDelegateProtocol>: ParallelServiceCallerProtocol {
private unowned let delegate: T
init(delegate: T) {
self.delegate = delegate
}
func call(service: EmployeesListParallelService) {
Service.emploeesList { [unowned self] (result) in
self.delegate.didCompleteParellelServiceWithResult(service, result: result)
}
}
func call(service: InformationParallelService) {
Service.information { (result) in
self.delegate.didCompleteParellelServiceWithResult(service, result: result)
}
}
}
// Service Result In Context
enum SynchronizationServiceResult {
case Employees(ServiceResult)
case Information(ServiceResult)
}
// Concrete Visitor - Wraps API Result And Gives Context
class ParallelServiceParser: ParallelServiceCallerProtocol {
var result: SynchronizationServiceResult?
private let serviceResult: ServiceResult
init(serviceResult: ServiceResult) {
self.serviceResult = serviceResult
}
func call(service: EmployeesListParallelService) {
result = .Employees(serviceResult)
}
func call(service: InformationParallelService) {
result = .Information(serviceResult)
}
}
// Delegate that notifies for completion of all calls
protocol ParallelServiceManagerDelegateProtocol: class {
func didCompleteAllServicesWithResults(results: [SynchronizationServiceResult])
}
// Manager - starts all calls and adds context to returned results - knows nothing about calls
class ParallelServiceManager<T where T: ParallelServiceManagerDelegateProtocol> {
private let services: [ParallelServiceProtocol]
private unowned let delegate: T
// Keep Caller Visitors in Memory or they will be dealocated
private var callers: [ParallelServiceCaller<ParallelServiceManager>] = []
private var completed: [SynchronizationServiceResult] = [] {
didSet {
if completed.count == services.count {
self.delegate.didCompleteAllServicesWithResults(completed)
self.callers.removeAll()
}
}
}
init(services: [ParallelServiceProtocol], delegate: T) {
self.services = services
self.delegate = delegate
}
func start() {
visitAllServices { (service) in
let caller =
ParallelServiceCaller<ParallelServiceManager>(delegate: self)
service.start(caller)
self.callers.append(caller)
}
}
private func visitAllServices(perform: ParallelServiceProtocol -> () ) {
for service in self.services {
perform(service)
}
}
}
extension ParallelServiceManager: ParallelServiceCallerDelegateProtocol {
func didCompleteParellelServiceWithResult(service: ParallelServiceProtocol,
result: ServiceResult) {
// No need to persist parser visitor
let caller = ParallelServiceParser(serviceResult: result)
service.start(caller)
completed.append(caller.result!)
}
}
// Example Usage
class SynchronizationService {
private lazy var services: [ParallelServiceProtocol] = {
return [EmployeesListParallelService(), InformationParallelService()]
}()
func start() {
let manager = ParallelServiceManager<SynchronizationService>(services: services, delegate: self)
manager.start()
}
}
extension SynchronizationService: ParallelServiceManagerDelegateProtocol {
func didCompleteAllServicesWithResults(results: [SynchronizationServiceResult]) {
for result in results {
switch result {
case .Employees(let result):
// TODO:
print("\(result)") // Should Return Success
case .Information(let result):
// TODO:
print("\(result)") // Should Return Failure
}
}
}
}
let sync = SynchronizationService()
sync.start()
I am subclassing a Typhoon assembly such that stubbed implementations are returned, for unit testing purposes.
My assembly looks something like this:
class RealAssembly : TyphoonAssembly {
public dynamic func instanceToStubOut() -> AnyObject {
return TyphoonDefinition.withClass(SomeRealWorldClass.self)
}
public dynamic func instanceToTest() -> AnyObject {
return TyphoonDefinition.withClass(ClassToTest.self, configuration: { (definition : TyphoonDefinition!) -> Void in
definition.useInitializer("initWithObjectToStub:", parameters: { (initializer : TyphoonMethod!) -> Void in
initializer.injectParameterWith(self.instancetoStubOut())
})
})
}
}
My test class is solely for testing the instance of type ClassToTest, and I want to test it with the initializer-injected dependency on the object of type SomeRealWorldClass to be stubbed out. So I subclass RealAssembly so that instanceToStubOut() is overridden to return my stub object.
class MyTestClass : XCTestCase {
var assembly : TestAssembly!
class TestAssembly : RealAssembly {
override dynamic func instanceToStubOut() -> AnyObject {
return TyphoonDefinition.withClass(TestClass.self)
}
}
#objc
class TestClass : NSObject, ClassToStubOut {
func methodToStubOut() { /* do nothing */ }
}
override func setUp() {
super.setUp()
self.assembly = TestAssembly().activate()
}
override func tearDown() {
self.assembly = nil
super.tearDown()
}
func testStuff() {
let testingInstance = self.assembly.instanceToTest()
XCTAssertTrue(testingInstance.doStuff(), "doStuff returns true")
}
}
I expected this to work, but it doesn't. Typhoon seems to inject an uninitialized object instead of ever calling TestAssembly.instanceToStubOut()
Am I doing something wrong? Should I take a different approach?
EDIT: Here is some code you can paste into a Swift Playground that demonstrates the problem. The last line shows c.otherInstance returning nil :
import Typhoon
#objc
class BaseClass : NSObject {
var otherInstance : OtherProtocol!
func doIt() -> String {
return self.otherInstance.doStuff()
}
}
#objc
protocol OtherProtocol {
func doStuff() -> String
}
#objc
class OtherImpl : NSObject, OtherProtocol {
func doStuff() -> String {
return "OtherClass"
}
}
#objc
class StubClass : NSObject, OtherProtocol {
func doStuff() -> String {
return "Stubbed out"
}
}
class BaseAssembly : TyphoonAssembly {
dynamic func baseObject() -> AnyObject {
return TyphoonDefinition.withClass(BaseClass.self,
configuration: { (def : TyphoonDefinition!) -> Void in
def.injectProperty("otherInstance", with: self.otherObject())
})
}
dynamic func otherObject() -> AnyObject {
return TyphoonDefinition.withClass(OtherImpl.self)
}
}
var assembly = BaseAssembly()
assembly.activate()
var b = assembly.baseObject() as! BaseClass
b.doIt()
#objc
class TestAssembly : BaseAssembly {
override func otherObject() -> AnyObject {
return TyphoonDefinition.withClass(StubClass.self)
}
}
var testAssembly = TestAssembly()
testAssembly.activate()
var c = testAssembly.baseObject() as! BaseClass
c.otherInstance // this shouldn't be nil
Edit:
While patching is an option, as outlined in #Herman's answer below, what was attempted in the question is a supported feature, however there was a regression bug preventing it from working correctly.
The regression bug has been fixed in Typhoon 3.2.2 and so now both patching and overriding an assembly are again options for configuring Typhoon for a particular use-case.
Patching
There is a patching feature for this purpose in Typhoon. Look here.
For example:
class StubClass : NSObject, OtherProtocol {
#objc func doStuff() -> String {
return "Stubbed out"
}
}
let assembly = BaseAssembly()
assembly.activate()
let b = assembly.baseObject() as! BaseClass
print(b.doIt())
let testAssembly = BaseAssembly().activate()
let patcher = TyphoonPatcher()
patcher.patchDefinitionWithSelector("otherObject") { () -> AnyObject! in
return StubClass()
}
testAssembly.attachPostProcessor(patcher)
let c = testAssembly.baseObject() as! BaseClass
print(c.doIt())