Accessing Class var on AnyClass variable in Swift 4 - swift

In Swift 3.2 this (let id = row.tableViewCellClass?.reuseIdentifier) worked:
class DrillDownTableViewCell {
class var reuseIdentifier: String
{
return String(describing: self)
}
}
class RowViewModel: NSObject
{
var tableViewCellClass: AnyClass?
}
class Foo {
var row : RowViewModel?
func setup() {
row = RowViewModel()
row?.Class = DrillDownTableViewCell.self
}
func doThings() {
let id = row?.tableViewCellClass?.reuseIdentifier
}
}
After my Swift 4 update, it's showing "Instance member 'reuseIdentifier' cannot be used on type 'AnyObject'.
How would I access a class variable on a class who's metaType information is stored in an AnyClass variable?

(I assume you mean to have a ? after row in doThings(). I assume that's a typo and not part of the question. There are several other ? missing here and some other typos that I won't dive into.)
If you expect tableViewCellClass to have a reuseIdentifier class property, then it isn't of type AnyClass. There are many classes that don't have that property. You want classes that conform to a protocol:
protocol Identifiying {
static var reuseIdentifier: String { get }
}
So your model requires an Identifying class:
class RowViewModel: NSObject {
var tableViewCellClass: Identifiying.Type?
}
Then you can use this as you're expecting.

Related

Swift - Referring to a class's type within the class

When using static, type properties and methods from within the instance methods of the type, I'm often repeating the name of the type.
e.g.
class Foo
{
// Type properties, methods
static let kBrandColor = UIColor.red
static let kMeaning = 42
static func makeThing() -> Thing { ... }
// Instance method
func printStuff()
{
print("Brand Color is: \(Foo.kBrandColor)")
print("The meaning of life is: \(Foo.kMeaning)")
let thing = Foo.makeThing()
print("Thing is \(thing)"
}
...
}
These repeated references to "Foo" can (and often do) lead to bugs when copy-pasting, refactoring. It's very easy to forget to change a "Foo", and the code will still compile.
So, I've been using a pattern like this:
class Foo
{
fileprivate typealias _ThisClass = Foo
// Type properties, methods
static let kBrandColor = UIColor.red
static let kMeaning = 42
static func makeThing() -> Thing { ... }
// Instance method
func printStuff()
{
print("Brand Color is: \(_ThisClass.kBrandColor)")
print("The meaning of life is: \(_ThisClass.kMeaning)")
let thing = _ThisClass.makeThing()
print("Thing is \(thing)"
}
...
}
This approach has the advantage of some copy-and-paste safety, but at the expense of a bit of boilerplate.
Is there a better, cleaner solution to this issue? (I've attempted to search SO, but getting the search terms right for this kind of problem has been tricky.)
A protocol would work well here. You can define the properties the protocol requires, and then apply that to any class you want to use these in.
protocol Brandable {
var kBrandColor: UIColor { get }
var kMeaning: Int { get }
}
class Foo: Brandable {
let kBrandColor: UIColor = .red
let kMeaning: Int = 42
}
If you want to reuse the printStuff function, you can also put that in the protocol, and put a base implementation in an extension:
protocol Brandable {
var kBrandColor: UIColor { get }
var kMeaning: Int { get }
func printStuff()
}
extension Brandable {
func printStuff() {
print("Brand Color is: \(kBrandColor)")
print("The meaning of life is: \(kMeaning)")
}
}
class Foo: Brandable {
let kBrandColor: UIColor = .red
let kMeaning: Int = 42
}
class Bar: Brandable {
let kBrandColor: UIColor = .blue
let kMeaning: Int = 100
}
Foo().printStuff()
Bar().printStuff()
The same can be done with the makeStuff() function. Shared functionality goes in the protocol and its extension. If you need to change the behavior in some class, you only need to add your own printStuff or makeStuff function to override the protocol's default implementation.
Create a protocol like Identifiable with an identifier property. Then make any class that you want identifiable conform to it. It's the protocol oriented approach.
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String {
return String(describing: self)
}
}
class X: Identifiable {}
You also don't need to refer to the class name. Just call type(of: instance).identifier.

Method cannot be marked #objc because the type of the parameter 2 cannot be represented in Objective-C [duplicate]

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}

Is there a way to initialize a class in swift by using a variable which contains the name of the class?

Suppose I have a simple factory which returns various subclasses of a custom ModelObject class like:
class func testModelObject(className: String) -> ModelObject
{
let obj = // instance of the subclass of ModelObject specified by className
return obj
}
Is there a way to do this? Will Swift freak out when I try to call any methods of that object? Should I have something else for my return type?
For best type safety, you should let testModalObject to accept a meta-type like:
class ModelObject {
required init() {}
}
class Subclass: ModelObject {
required init() { super.init() }
}
func testModalObject(_ type: ModelObject.Type) -> ModelObject {
return type.init()
}
testModalObject(Subclass.self)
If you really need a string input, you will need to rely on the Objective-C runtime and do some casting (see how to create instance of a class from a string in swift 3):
#objc
class ModelObject: NSObject { // <---
required override init() {}
}
#objc(MOSubclass) // <-- tell ObjC the name is "MOSubclass" instead of "????.Subclass".
class Subclass: ModelObject {
required init() { super.init() }
}
func testModalObject(_ typeName: String) -> ModelObject? {
let cls = NSClassFromString("MO\(typeName)") as? ModelObject.Type
return cls?.init()
}
testModalObject("Subclass")!

Swift delegate for a generic class

I have a class that needs to call out to a delegate when one of its properties changes. Here are the simplified class and protocol for the delegate:
protocol MyClassDelegate: class {
func valueChanged(myClass: MyClass)
}
class MyClass {
weak var delegate: MyClassDelegate?
var currentValue: Int {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: Int) {
currentValue = initialValue
}
}
This all works just fine. But, I want to make this class generic. So, I tried this:
protocol MyClassDelegate: class {
func valueChanged(genericClass: MyClass)
}
class MyClass<T> {
weak var delegate: MyClassDelegate?
var currentValue: T {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: T) {
currentValue = initialValue
}
}
This throws two compiler errors. First, the line declaring valueChanged in the protocol gives: Reference to generic type 'MyClass' requires arguments in <...>. Second, the call to valueChanged in the didSet watcher throws: 'MyClassDelegate' does not have a member named 'valueChanged'.
I thought using a typealias would solve the problem:
protocol MyClassDelegate: class {
typealias MyClassValueType
func valueChanged(genericClass: MyClass<MyClassValueType>)
}
class MyClass<T> {
weak var delegate: MyClassDelegate?
var currentValue: T {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: T) {
currentValue = initialValue
}
}
I seem to be on the right path, but I still have two compiler errors. The second error from above remains, as well as a new one on the line declaring the delegate property of MyClass: Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements.
Is there any way to accomplish this?
It is hard to know what the best solution is to your problem without having more information, but one possible solution is to change your protocol declaration to this:
protocol MyClassDelegate: class {
func valueChanged<T>(genericClass: MyClass<T>)
}
That removes the need for a typealias in the protocol and should resolve the error messages that you've been getting.
Part of the reason why I'm not sure if this is the best solution for you is because I don't know how or where the valueChanged function is called, and so I don't know if it is practical to add a generic parameter to that function. If this solution doesn't work, post a comment.
You can use templates methods with type erasure...
protocol HeavyDelegate : class {
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}
class Heavy<P, R> {
typealias Param = P
typealias Return = R
weak var delegate : HeavyDelegate?
func inject(p : P) -> R? {
if delegate != nil {
return delegate?.heavy(self, shouldReturn: p)
}
return nil
}
func callMe(r : Return) {
}
}
class Delegate : HeavyDelegate {
typealias H = Heavy<(Int, String), String>
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
let h = heavy as! H // Compile gives warning but still works!
h.callMe("Hello")
print("Invoked")
return "Hello" as! R
}
}
let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))
Protocols can have type requirements but cannot be generic; and protocols with type requirements can be used as generic constraints, but they cannot be used to type values. Because of this, you won't be able to reference your protocol type from your generic class if you go this path.
If your delegation protocol is very simple (like one or two methods), you can accept closures instead of a protocol object:
class MyClass<T> {
var valueChanged: (MyClass<T>) -> Void
}
class Delegate {
func valueChanged(obj: MyClass<Int>) {
print("object changed")
}
}
let d = Delegate()
let x = MyClass<Int>()
x.valueChanged = d.valueChanged
You can extend the concept to a struct holding a bunch of closures:
class MyClass<T> {
var delegate: PseudoProtocol<T>
}
struct PseudoProtocol<T> {
var valueWillChange: (MyClass<T>) -> Bool
var valueDidChange: (MyClass<T>) -> Void
}
Be extra careful with memory management, though, because blocks have a strong reference to the object that they refer to. In contrast, delegates are typically weak references to avoid cycles.

Calling same property from different classes in Swift?

If I have two explicit classes that both have the same property name, is there a way to call the property without having to define which class I'm using?
class firstClass {
var name = “Name”
init…..
}
class secondClass {
var name = “Another name”
init….
}
now another function can call
//does not work... I get an error saying AnyObject doesn't have property
func printNameOf(object: AnyObject) {
println(object.name)
}
//works but my software has a lot of classes, which means a ton of code
func printNameOf(object: AnyObject) {
if object is firstClass {
println((object as firstClass).name)
}
if object is secondClass {
println((object as secondClass).name)
}
}
You could do this by creating a protocol that both your classes conform to:
protocol NameProtocol {
var name: String {get set}
}
class firstClass: NameProtocol {
var name = "Name 1"
}
class secondCLass: NameProtocol {
var name = "Name 2"
}
func printNameOf(obj: NameProtocol) {
// You know obj has property name
println(a.name)
}