Swift - MyClass is not identical to NSManagedObject - swift

Can someone explain to me why the following playground code gives me the error
EventEntity is not identical to NSManagedObject
import UIKit
import CoreData
class Table<T: NSManagedObject> {
func toFetchedResultsController() -> FetchedResultsController<T> {
return FetchedResultsController<T>()
}
}
class EventEntity: NSManagedObject {
}
class FetchedResultsController<T: NSManagedObject> {
}
class Factory<T: NSManagedObject>: NSObject {
var fetchedResultsController: FetchedResultsController<T>
init(fetchedResultsController: FetchedResultsController<T>) {
self.fetchedResultsController = fetchedResultsController
super.init()
}
}
class TableViewDataSource: NSObject {
init(factory: Factory<NSManagedObject>) {
super.init()
}
}
var dataSource: TableViewDataSource
let fetchedResultsController = Table<EventEntity>().toFetchedResultsController()
let factory = Factory(fetchedResultsController: fetchedResultsController)
dataSource = TableViewDataSource(factory: factory)
I had a problem with an app I'm developing in Swift. I created this playground to reproduce the problem.
What is the reason for this error? EventEntity is actually a subclass of NSManagedObject and T is a generics that specifies a NSManagedObject class requirement.
Thank you!

Look at your TableViewDataSource initializer:
init(factory: Factory<NSManagedObject>) {
super.init()
}
Here, you're specifying that the initializer wants a Factory<NSManagedObject>, and then later you pass it a Factory<EventEntity>. These types are not identical.
I think what you meant to do was to specify that TableViewDataSource also works with a generic type (so long as it's some NSManagedObject), like so:
class TableViewDataSource<T: NSManagedObject>: NSObject {
init(factory: Factory<T>) {
super.init()
}
}

Related

Swift: Cannot override mutable property with read-only property 'xxx'

I don't understand the meaning of this error. This is what I am trying to do:
protocol P {
var testTitle: String? { get }
}
class MyViewController: UIViewController, P {
var testTitle: String? {
didSet {
self.title = testTitle
}
}
}
final class TestViewController: MyViewController {
var testTitle: String? { // Error occurs here
return "Test"
}
}
And I'm getting:
Cannot override mutable property with read-only property 'testTitle'
Could you help me understanding what's going on? Maybe it's trivial for some people but not for me.
Thanks for your help.
You cannot implement a subclass to be more restrictive than the base class (it would only work the other way round). So if the base class implements a read/write property, you cannot make it read only in your derived class.
Therefore:
final class TestViewController: MyViewController {
override var testTitle: String? { // No error occurs here
get {
return "Test"
}
set {
super.testTitle = newValue
}
}
}
The protocol requires that the adopting class implements a property testTitle with a getter but it does not forbid to declare the property with getter and setter.
To override a property you have to override the (entire) signature in the class, not the protocol requirement.
As testTitle is declared as read/write you cannot override it as read-only.

If I subclass a class, can I specify a certain subclass an instance variable should be?

I have an NSObject subclass, BaseClass. BaseClass is a placeholder class for a couple subclasses, SubClassA and SubClassB. There is an instance variable that I have present on both of the subclasses. They're the same name, and are both of a corresponding subclass of another object. They're often used in very similar ways, so I wanted to move some functionality from my SubClassA and SubClassB to the BaseClass. However, I need access to that variable.
If I move the variable into the BaseClass, I am unable to specify the proper subclass of it in SubClassA and SubClassB, saying I can't override it. If I use the common parent class of this instance variable in the BaseClass, I lose some access to things that aren't common between how SubClassA and SubClassB work.
This is a more primitive example, but the basics of what I'm trying to do. This example obviously does not work. Are my only options to choose having to define common functionality within SubClassA and SubClassB or is there a proper way to achieve my goal here?
class BaseClass: NSObject {
var myObject: MyObject
}
class SubClassA: BaseClass {
override var myObject: MyObjectA
}
class SubClassB: BaseClass {
override var myObject: MyObjectB
}
class MyObject: NSObject { }
class MyObjectA: MyObject { }
class MyObjectB: MyObject { }
This gives me the error:
Property 'myObject' with type 'MyObjectA' cannot override a property with type 'MyObject'
How about using generic? For simplicity, I removed NSObject
class MyObject {
}
class MyObjectA: MyObject {
}
class MyObjectB: MyObject {
}
class BaseClass<T> where T : MyObject {
var myObject: T?
}
class SubClassA: BaseClass<MyObjectA> {
}
class SubClassB: BaseClass<MyObjectB> {
}
Instead of putting the myObject related code into BaseClass, you could put it into a protocol extension. Consider this:
class BaseClass {
}
class SubClassA: BaseClass, HasMyObject {
var myObject: MyObjectA
}
class SubClassB: BaseClass {
var myObject: MyObjectB
}
class MyObject { }
class MyObjectA: MyObject { }
class MyObjectB: MyObject { }
protocol HasMyObject {
associatedtype MyObjectClass
var myObject: MyObjectClass { get set }
}
This is conceptually very similar to using generics, but would separate your myObject related code from the rest of the code in your class. Whether this is actually preferable over generics depends on your coding style and specific use-cases.
You can create a class function in your BaseClass that returns a class that inherits from a BaseObject (myObject) and override it for whichever class you need.
class BaseClassObject: NSObject {
}
class BaseClass: NSObject {
func generateClass() -> NSObject {
return BaseClassObject()
}
}
class BranchedObject: BaseClassObject {
}
class SubClassA: BaseClass {
var myObject: NSObject?
override func generateClass() -> NSObject {
return BranchedObject()
}
override init() {
super.init()
self.myObject = self.generateClass()
}
}

Accessing Class var on AnyClass variable in Swift 4

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.

Swift protocol on class instance never fires

I have a swift protocol, but it never fires.
I have 1 class which is an instance, and the other is a class where I want to manage an object;
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
override init() {
super.init()
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static var instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init()
My issue is that the protocol never fires and does not output "turn is completed"
How can I resolve this?
Edit: How do I set the delegate?
Many thanks
In case you have describe create custom init.
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(with delegate: TurnDelegate?) {
self.delegate = delegate
delegate?.turnIsCompleted()
}
}
Than:
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init(with: c2)
Output:
Turn is completed
You forgot to set the delegate.
Usually the delegate is set in an init method. The method in the protocol is called later in another method for example
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(delegate: TurnDelegate?) {
self.delegate = delegate
}
func turnSomething()
{
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static let instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2 = ClassTwo.instance
let c1 = ClassOne(delegate: c2)
c1.turnSomething()
However for this purpose especially in conjunction with a singleton I'd prefer a callback closure rather than protocol / delegate. The benefit is less overhead and the callback is directly connected to the calling method.
class ClassOne : NSObject {
func turnSomething()
{
let c2 = ClassTwo.instance
c2.turn {
print ("Turn is completed")
}
}
}
class ClassTwo: NSObject {
static let instance = ClassTwo()
func turn(completion: ()->()) {
// do heavy work
completion()
}
}
let c1 = ClassOne()
c1.turnSomething()
Delegates in all their glory do have their drawbacks too. One of them is that relationships between objects and their delegates have to be established explicitly. In Cocoa there are typically two ways of doing this. One is connecting a delegate IBOutlet in InterfaceBuilder, the other is doing it programmatically. As #OlegGordiichuck points out you could do it in the initializer, but generally in Cocoa delegates tend to be properties. In your case this would boil down to instantiate objects of ClassTwo and ClassOne and then manually set the delegate of c2 as in
c2.delegate = c1
This however defeats your notification mechanism and you would have to have a separate method for notifying the delegate (Which is again typical, as usually your delegate cannot know about is significant other during its construction. Moreover the construction of the originator is usually not something the delegate would have to know about).

How to make self.dynamicType into a typealias

Basically I want to add a typealias to UIViewController or any other default swift classes. The reasoning behind this is that I want to abstract my code so that I can access some static functions by just using this instead of self.dynamicType
extension UIViewController {
typealias this = TheClassThatSubclassedThis
}
class DetailViewController: UIViewController {
func doStuff() {
this.doStaticStuff()
}
static func doStaticStuff() {
...
}
}
I know this is possible by creating a protocol, then just implement said protocol to the class I want to implement it to, like this
protocol CanAccessStaticSelf {
typealias this
}
class DetailVC: UIViewController, CanAccessStaticSelf {
typealias this = DetailVC
}
But is there a more efficient way to do this? Like for example, by just subclassing a certain class or by extending a superclass?
Like this for example
extension UIViewController {
public static var defaultNibName: String {
return self.description().componentsSeparatedByString(".").dropFirst().joinWithSeparator(".")
}
}
class DetailVC: UIViewController, CanAccessStaticSelf {
func doSomeStuffAgain() {
// no other code just subclass and I can access self.dynamicType as just `this`
print(this.defaultNibName)
}
}
Try this instead:
protocol CanAccessStaticSelf {
typealias this = Self
}
...but what you are trying to achieve looks somewhat confusing to me ;-(
Thanks to this proposal from Erica Sadun we all might be able to use the Self keyword for that in the near future.
For instance:
class MyClass {
static func staticMethod() { ... }
func instanceMethod() {
MyClass.staticMethod()
Self.staticMethod()
}
}
it is not possible to access through this.
but you can access through "UIViewController.defaultNibName".