I want to access function in protocol, but XCode complaint
Instance member 'createColumns' cannot be used on type 'T'; did you
mean to use a value of this type instead?
What I have done:
Create protocol:
protocol StorageModelDelegate {
func createColumns(for tableBuilder: TableBuilder)
}
Create class generic that receive StorageModelDelegate:
class SQLiteStorage<T: StorageModelDelegate> {
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder) // -> this is the error comes up.
}
}
}
Create class that implement SQLiteStorage:
final class InfoStorageModel {
private let sqlite: SQLiteStorage = SQLiteStorage<Info>()
}
so, how to fix the error in SQLiteStorage class?
The error indicates that you need an instance of T, not the type itself.
So you need something like:
class SQLiteStorage<T: StorageModelDelegate> {
var delegate:T
init (delegate:T) {
self.delegate = delegate
}
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
self.delegate.createColumns(for: builder) // -> this is the error comes up.
}
}
}
You want to call static method instead of instance method.
In order to fix, you should add instance parameter:
First of all, use weak var delegate in order to prevent retain cycles.
protocol StorageModelDelegate: class {
func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
delegate?.createColumns(for: builder)
}
}
}
Or use static protocol methods:
protocol StorageModelDelegate {
static func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder)
}
}
}
Related
I'm trying to pass data between viewControllers, but something seems wrong.
The first viewController I want to set the "Bool" to the protocol function to be able to recover in the other screen. What am I doing wrong, I always used protocols but at this time I got in trouble.
That's how I'm doing that:
//
// ComboBoxNode.swift
//
import Foundation
import SWXMLHash
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool)
}
class ComboBoxNode: FormControlNode, IFormControlDataSource {
var listType: String?
var dataSource: String?
var dataSourceValue: String?
var dataSourceText: String?
var hasCustomOption:Bool?
var customOptionText: String?
var ctrlDataSourceType: String?
var parameters = [ParameterNode]()
var staticList: FormControlStaticListNode?
var delegate:ComboBoxNodeDelegate?
override init(indexer: XMLIndexer) {
super.init(indexer: indexer)
guard let element = indexer.element else {
preconditionFailure("Error")
}
let isCustomOption = element.bool(by: .hasCustomOption) ?? hasCustomOption
if isCustomOption == true {
self.delegate?.getCustomOption(data: hasCustomOption!)
}
self.readFormControlDataSource(indexer: indexer)
}
override func accept<T, E: IViewVisitor>(visitor: E) -> T where E.T == T {
return visitor.visit(node: self)
}
}
That's how I'm trying to recover on next screen:
// FormPickerViewDelegate.swift
import Foundation
import ViewLib
import RxSwift
class FormPickerViewDelegate: NSObject {
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
private var controlViewModel: FormControlViewModel
private var customText:Bool?
private var PickerNodeDelegate:ComboBoxNodeDelegate?
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
}
func getItemByValue(_ value: Any) -> (AnyHashable, String)? {
if value is AnyHashable {
let found = items.value.filter {$0.value == value as! AnyHashable}
if found.count >= 1 {
return found[0]
}
}
return nil
}
}
extension FormPickerViewDelegate:ComboBoxNodeDelegate {
func getCustomOption(data: Bool) {
customText = data
}
}
Instead of setting PickerNodeDelegate = self in didSet {} closure
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
Assign it in your init() function instead
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
PickerNodeDelegate = self
}
Note, your should declare your delegate to be weak also, since it's a delegate, your protocol should conform to be a class type in order to be weakified.
protocol ComboBoxNodeDelegate: class
...
weak var delegate: ComboBoxNodeDelegate?
Here is an example, hope it helps!
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool) -> String
}
class ViewOne:ComboBoxNodeDelegate {
var foo:Bool = false
var bar:String = "it works!"
/** Return: String */
func getCustomOption(data:Bool) -> String { //conform here to protocol
// do whatever you wanna do here ...example
self.foo = data // you can set
return bar // even return what you want
}
//initialize
func initalizeViewTwo() {
let v2 = ViewTwo()
v2.delegate = self //since `self` conforms to the ComboBoxNodeDelegate protcol you are allowed to set
}
}
class ViewTwo {
var delegate:ComboBoxNodeDelegate?
func getCustomOption_forV1() {
let view2_foo = delegate.getCustomOption(data:true)
print(view2_foo) // should print "it works!"
}
}
All parameters passed around in Swift are constants -- so you cannot change them.
If you want to change them in a function, you must declare your protocol to pass by reference with inout:
protocol ComboBoxNodeDelegate {
func getCustomOption(data: inout Bool)
}
Note: you cannot pass a constant (let) to this function. It must be a variable -- which I see you are doing!
I have the following Swift code. My goal is to be able to pass in the type or class of either FirstItem or SecondItem into the Manager to be able to instantiate later on (by calling the createItem function).
protocol CustomItem {
static func instantiate() -> CustomItem
}
class FirstItem: CustomItem {
static func instantiate() -> CustomItem {
return FirstItem()
}
}
class SecondItem: CustomItem {
static func instantiate() -> CustomItem {
return SecondItem()
}
}
class Manager {
var itemClass: CustomItem
func createItem() {
let itemInstance = itemClass.instantiate()
}
init(itemClass: CustomItem) {
self.itemClass = itemClass
}
}
let manager = Manager(itemClass: FirstItem)
How can I fix the code above to allow for this so that the manager accepts the class itself instead of an instance of the class.
By changing the parameter type that you are accepting in the init function to CustomItem.Type and changing the value you are passing in to FirstItem.self this works. For example changing the code to something like the following should work.
protocol CustomItem {
static func instantiate() -> CustomItem
}
class FirstItem: CustomItem {
static func instantiate() -> CustomItem {
return FirstItem()
}
}
class SecondItem: CustomItem {
static func instantiate() -> CustomItem {
return SecondItem()
}
}
class Manager {
var itemClass: CustomItem.Type
func createItem() {
let itemInstance = itemClass.instantiate()
}
init(itemClass: CustomItem.Type) {
self.itemClass = itemClass
}
}
let manager = Manager(itemClass: FirstItem.self)
So, i am creating a Singleton class as below, and i need few instance variables in this class, such that any team member can access the instance variable and get the values. To do that, i will need to initialize these instance variables to a certain value at the beginning itself.
But i get a compilation error, saying "missing argument for parameter 'doesValueExists' in call".
What exactly i m doing wrong here ?
class ABC_Util {
private var doesValueExists: Bool
private var arrValues: NSMutableArray?
class var sharedInstance: ABC_Util {
struct ABC_UtilSingleton {
static let instance = ABC_Util()
}
return ABC_UtilSingleton.instance
}
init(doesValueExists: Bool, arrValues: NSMutableArray?) {
self.doesValueExists = self.checkValueExists()
self.arrValues = self.getArrayOfValues()
}
//method
internal func checkValueExists() -> Bool {
}
//method
internal func getArrayOfValues() -> NSMutableArray? {
}
}
Your initializer for ABC_Util is declared as:
init(doesValueExists:Bool, arrValues:NSMutableArray?) {
Therefore you cannot say
static let instance = ABC_Util()
The expression ABC_Util() would correspond to an initializer with no parameters, and you do not have such an initializer. You must say:
static let instance = ABC_Util(doesValueExists:someBool, arrValues:someArray)
(with appropriate values, of course).
You have to use your initializer in order to initialize your variables.
class ABC_Util {
private var doesValueExists:Bool
private var arrValues:NSMutableArray?
class var sharedInstance: ABC_Util
{
struct ABC_UtilSingleton
{
static let instance = ABC_Util(doesValueExists: true, arrValues: nil)
}
return ABC_UtilSingleton.instance
}
init(doesValueExists:Bool, arrValues:NSMutableArray?) {
self.doesValueExists = doesValueExists
self.arrValues = arrValues
}
//method
internal func checkValueExists()-> Bool
{
return true
}
//method
internal func getArrayOfValues()-> NSMutableArray?
{
return nil
}
}
And I recommend you to change your singleton declaration to the suggested syntax
static let sharedInstance: ABC_Util = ABC_Util(doesValueExists: true, arrValues: nil)
You could use as below.
class ABC_Util {
private var doesValueExists:Bool = false
private var arrValues:NSMutableArray?
class var sharedInstance: ABC_Util {
struct ABC_UtilSingleton {
static let instance = ABC_Util(doesValueExists: false, arrValues: ["a", "b", "c"])
}
return ABC_UtilSingleton.instance
}
init(doesValueExists:Bool, arrValues:NSMutableArray?) {
self.doesValueExists = self.checkValueExists()
self.arrValues = self.getArrayOfValues()
}
//method
internal func checkValueExists()-> Bool{
return self.doesValueExists
}
//method
internal func getArrayOfValues()-> NSMutableArray?{
return arrValues
}
}
i got the solution, when i tried this, worked fine!
class ABC_Util {
var doesValueExists:Bool = false
var arrValues:NSMutableArray? = nil
class var sharedInstance: ABC_Util
{
struct ABC_UtilSingleton
{
static let instance = ABC_Util()
}
return ABC_UtilSingleton.instance
}
init() {
self.doesValueExists = self.checkValueExists()
self.arrValues = self.getArrayOfValues()
}
//method
internal func checkValueExists()-> Bool
{
//return true/false
}
//method
internal func getArrayOfValues()-> NSMutableArray?
{
//return array/nil
}
}
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())
I want to check if I already have a delegate in my removeDelegate method before removing.
How do I do that?
Here's what I've got so far:
protocol LocationManagerDelegate {
func locationManagerDidUpdateLocation(
oldLocation: CLLocationCoordinate2D,
currentLocation: CLLocationCoordinate2D
)
}
class LocationManager: NSObject {
private var _delegates = [LocationManagerDelegate]()
func removeDelegate(delegate:LocationManagerDelegate) {
if contains(_delegates, delegate) {
// Remove delegate
}
}
}
However, this gives me the following error on the 'if contains' line:
cannot invoke 'contains' with an argument list of type '(#lvalue Array< LocationManagerDelegate >!, LocationManagerDelegate)'
Update for Swift 4.2:
Assuming that the delegates are actually instances of a class, you could require that in the protocol by "inheriting" from "class":
protocol LocationManagerDelegate: class {
// ...
}
and then use the firstIndex(where:) method, using the "identity operator
===:
class LocationManager: NSObject {
private var _delegates = [LocationManagerDelegate]()
func removeDelegate(delegate:LocationManagerDelegate) {
if let index = _delegates.firstIndex(where: { $0 === delegate }) {
_delegates.remove(at: index)
}
}
}
Old answer (Swift 1):
There are two slightly different contains() functions:
func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: (S.Generator.Element) -> L) -> Bool
You are using the first one, which requires that the sequence elements conform to
the Equatable protocol, i.e. they can be compared with ==.
Assuming that the delegates are actually instances of a class, you could require
that in the protocol by "inheriting" from "class":
protocol LocationManagerDelegate : class {
// ...
}
and then use the second, predicate-based version of contains() with the
identity operator ===:
func removeDelegate(delegate:LocationManagerDelegate) {
if contains(_delegates, { $0 === delegate }) {
// Remove delegate
}
}
To remove the object from the array you'll have to get its index, so you might use
the findIdenticalObject() function from https://stackoverflow.com/a/25543084/1187415:
func findIdenticalObject<T : AnyObject>(array: [T], value: T) -> Int? {
for (index, elem) in enumerate(array) {
if elem === value {
return index
}
}
return nil
}
and then find and remove from the array with
func removeDelegate(delegate:LocationManagerDelegate) {
if let index = findIdenticalObject(_delegates, delegate) {
_delegates.removeAtIndex(index)
}
}
The arguments to contains must implement the Equatable protocol since it is defined as:
public func contains<T:Equatable>(left:[T], right:T) -> Bool
Since there's no way to indicate that LocationManagerDelegate implements Equatable, I don't think you can use it. The obvious attempt would be:
protocol LocationManagerDelegate : Equatable {
...
}
But that will fail when you try to declare the array because Equatable uses Self.
The best option I can come up with is:
func removeDelegate(delegate:LocationManagerDelegate) {
_delegates = filter(_delegates) { return $0 !== delegate }
}
protocol LocationManagerDelegate {
// ...
var index_delegate:Int?{get set}
}
class LocationManager {
private var delegates:[LocationManagerDelegate] = []
func add(delegate: LocationManagerDelegate?){
if let d = delegate {
self.delegates.append(d)
let index = self.delegates.count - 1
self.delegates[index].index_delegate = index
}
}
func remove(delegate: LocationManagerDelegate) {
delegates = delegates.filter({ return $0.index_delegate != delegate.index_delegate })
}
}