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) {
}
}
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 using the HandyJSOn framework to serialize and deserialize objects in Swift3. Now I've the problem that I want to exclude some properties from this process. I tried to follow the steps given on the GithHub page but I can't get to work:
class MyClass : HandyJSON {
private var excludeThisProperty : String
public func mapping(mapper: HelpingMapper) {
mapper >>> self.excludeThisProperty
}
}
The compiler fials with the error:
binary operator >>> cannot be applied to operands of type HelpingMapper and String
+++ Example +++
class MyClass : HandyJSON {
private let myPropertyDefault : String? = "example"
private var myProperty : String
public required init() {
myProperty = myPropertyDefault!
}
public func reset() {
myProperty = myPropertyDefault!
}
public func mapping(mapper: HelpingMapper) {
mapper >>> self.myPropertyDefault
}
}
Please change your string to optional :
private var excludeThisProperty : String?
Example code :
let jsonString = "{\"excludeThisProperty\":\"sdfsdf\"}"
if let myclass = MyClass.deserialize(from: jsonString) {
print(myclass)
}
class MyClass : HandyJSON {
private var myPropertyDefault : String? = "example" // changed from let to var
private var myProperty : String
public required init() {
myProperty = myPropertyDefault!
}
public func reset() {
myProperty = myPropertyDefault!
}
public func mapping(mapper: HelpingMapper) {
mapper >>> self.myPropertyDefault
}
}
I've followed the solution at Make a Swift dictionary where the key is "Type"? to create dictionaries that can use a class type as keys.
What I want to do is: I have one dictionary that should store class types with their class type (aka metatype) as keys, too:
class MyScenario {
static var metatype:Metatype<MyScenario> {
return Metatype(self)
}
}
var scenarioClasses:[Metatype<MyScenario>: MyScenario.Type] = [:]
Then I have methods to register and execute scenarios:
public func registerScenario(scenarioID:MyScenario.Type) {
if (scenarioClasses[scenarioID.metatype] == nil) {
scenarioClasses[scenarioID.metatype] = scenarioID
}
}
public func executeScenario(scenarioID:MyScenario.Type) {
if let scenarioClass = scenarioClasses[scenarioID.metatype] {
let scenario = scenarioClass()
}
}
... Problem is in the last line:
Constructing an object of class type 'MyScenario' with a metatype
value must use a 'required' initializer.
It looks like the compiler is confused at that point since I cannot use 'required' at that assignment. Does anyone have an idea how I would have to instantiate the scenarioClass in executeScenario()?
This must do the job.
import Foundation
struct Metatype<T> : Hashable
{
static func ==(lhs: Metatype, rhs: Metatype) -> Bool
{
return lhs.base == rhs.base
}
let base: T.Type
init(_ base: T.Type)
{
self.base = base
}
var hashValue: Int
{
return ObjectIdentifier(base).hashValue
}
}
public class MyScenario
{
var p: String
public required init()
{
self.p = "any"
}
static var metatype:Metatype<MyScenario>
{
return Metatype(self)
}
}
var scenarioClasses:[Metatype<MyScenario>: MyScenario.Type] = [:]
public func registerScenario(scenarioID:MyScenario.Type)
{
if (scenarioClasses[scenarioID.metatype] == nil)
{
scenarioClasses[scenarioID.metatype] = scenarioID
}
}
public func executeScenario(scenarioID:MyScenario.Type)
{
if let scenarioClass = scenarioClasses[scenarioID.metatype]
{
let scenario = scenarioClass.init()
print("\(scenario.p)")
}
}
// Register a new scenario
registerScenario(scenarioID: MyScenario.self)
// Execute
executeScenario(scenarioID: MyScenario.self)
// Should print "any"
I'm trying to use protocol in my class here is the code
public protocol Channel {
typealias CallBackType
func onResponse(responseDic: NSDictionary)
func onRequest(responseDic: NSDictionary)
func onBroadcast(responseDic: NSDictionary)
func request(var messageDic: [String:String]) -> Promise<Any>
func broadcast(var messageDic:[String:AnyObject])
func listen(messageDic:[String: AnyObject], callback:CallBackType)
}
Then I have other class where I create the class that use my protocol
public class SocketManager {
var systemChannel:SystemChannel?
public var socket = SocketIOClient(socketURL: hostUrl, options: ["log": false,
"reconnects": true,
"reconnectAttempts": 5,
"reconnectWait": 5,
"connectParams": ["token":tokenSDK]])
public func start_socket() -> Promise<Bool> {
systemChannel = SystemChannel(socket: self.socket)
}
public func getChannel(typeChannel:SOCKET_CHANNELS) -> Channel{
switch typeChannel{
case .SYSTEM:
return systemChannel!
default:
return systemChannel!
}
}
}
Here is the SystemChannel class
public class SystemChannel: Channel {
public typealias CallBackType = [String: AnyObject] -> Void
}
But I get compile error on the getChannel method Protocol 'Channel' can only be used as a generic constraint because it has Self or associated type requirements
I'm trying to implement an image downloader class. It's a singleton implementation. The idea is to ask the instance to download an image and register as an observer for that image download. This is what I came up with so far:
public protocol ImageDownloaderDelegate {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if (!contains(observerList, delegate)) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}
I get the following error:
'ImageDownloaderDelegate' is not convertible to 'S.Generator.Element -> L'
Any help is much appreciated
You're not passing the correct arguments to the contains function, which expects a collection and a predicate (closure).
You need to do
public protocol ImageDownloaderDelegate : class {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if !contains(observerList!, { observer in observer === delegate }) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}