How to init with a generic NSFetchedResultsController? - swift

Wondering how to init a class that contains a fetched results controller?
The compiler says that T is an undeclared type. I have a couple of dynamic tables that draw their data from a fetchedResultsController. If i could make the inject frc generic that would eliminate a bunch of duplicate code and make things more maintainable.
class DynamicTableData: NSObject {
let frc: NSFetchedResultsController<T>
init(frc: NSFetchedResultsController<T>) {
self.frc = frc
super.init()
}
}

Try this. Your your generic type should conform to NSFetchRequestResult
class DynamicTableData<T:NSFetchRequestResult>: NSObject {
let frc: NSFetchedResultsController<T>
init(frc: NSFetchedResultsController<T>) {
self.frc = frc
super.init()
}
}

Related

How can an NSManagedObject conform to the NSItemProviderReading protocol?

After creating an NSManagedObject subclass of a Core Data entity, how can I make it conform properly to the NSItemProviderReading protocol? The protocol has a required initializer that must be declared directly in the class. But which designated initializer should NSItemProviderReading's init(itemProviderData:, typeIdentifier:) call?
This is what I have below:
import Foundation
import CoreData
#objc(Something)
public class Something: NSManagedObject, NSItemProviderReading {
public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
// MARK: - Item Provider Reading
public static var readableTypeIdentifiersForItemProvider: [String] {
return []
}
public required init(itemProviderData data: Data, typeIdentifier: String) throws {
// This seems very hack-y…
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
self.init(context: context)
}
}
Is calling self.init(context:) really the right way to go here?
To conform to NSItemProviderReading protocol requires conformance to the init you mentioned and also the static var readableTypeIdentifiersForItemProvider:
init(itemProviderData: Data, typeIdentifier: String)
static var readableTypeIdentifiersForItemProvider: [String]
The documentation indicates this with the Required note.
I don't see anything wrong with your init so if it compiles and works with how you plan on using the class I don't see an issue. What I would recommend is using dependency injection to pass Core Data context throughout your app, to the view controllers that need them. That way you don't have to do the annoying AppDelegate code every time (and it's safer because you can be sure the context is always available).

Change superclass property type on swift

In a super Class called TableViewCell I have a property
class TableViewCell {
var model: AnyObject?
}
In a class called CountryTableViewCell I wrote this code
class CountryTableViewCell : TableViewCell {
var model:[AnyObject]? {
didSet {
// do some stuff
}
}
}
and I got this error
property model with [AnyObject]? cannot override a property with type
Anyobject?
Is it not possible to change the property model to an array?
No, you cannot use like that. Also AnyObject should be replaced by Any if using Swift 3.
You can change your code as below:
class TableViewCell {
var model: Any?
}
class CountryTableViewCell : TableViewCell {
override var model: Any? {
didSet {
}
}
}
Now if you want to get an array of the model in didSet then you can type cast it as below code.
class CountryTableViewCell : TableViewCell {
override var model: Any? {
didSet {
if let arrModel = model as? [Any] {
// Do Stuff...
}
}
}
}
No the property's type cannot be changed. If this is allowed, it would violate type safety by:
let subcell: CountryTableViewCell = CountryTableViewCell()
let supercell: TableViewCell = subcell
supercell.model = "anything that is not an array" as NSString
let wrong = subcell.model // not an array!
#ParthAdroja's answer showed a runtime workaround for this. You won't get an array at compile type, but at least you can ensure you have an array at runtime.
In principle if the property is read-only this specialization in subclass should work (it is same as restricting the return type of a function), but it doesn't work as of Swift 3. Anyone care about this can file an SE request to the Swift team ☺.
(Additionally, Apple do have some magic to make it work with bridged Objective-C classes, but we don't know what it is yet.)

Use self in Swift class initializer without super

Consider this class
class A {
private let model: Model
init() {
self.model = Model(service: self)
}
}
You can see that this will fail since I'm using self before everything is initialized. Since this is a pure swift class there is no option to call the super initializer.
How would you do this in pure swift without NSObject superclass, possibly without lazy?
Essentially this looks wrong from design point of view. Your class A references Model and Model references A.
Logically, one of those two should be either optional variable (so that you can set it to nil), or a weak reference, otherwise you will have an ownership circle and a memory leak.
In other words, one of those two classes should be the owner of the other, you shouldn't have a mutual ownership.
Let's decide that the Model is the owned class, then:
class A {
private let model: Model
init() {
let model = Model()
self.model = model
model.service = self
}
}
Another option is to use the 2-phase initialization using an implicitly unwrapped optional
class A {
private var model: Model!
init() {
self.model = Model(service: self)
}
}

Swift - MyClass is not identical to NSManagedObject

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()
}
}

Any ideas on how to have a generic class, implementing a protocol, be cast to that protocol and allow retrieving a property?

I have a simple Result object which should contain an object (class, struct or enum) and a Bool to say whether it was cancelled or not. I need to interrogate this object along its path (before it gets to its destination, where the destination knows what kind of object to expect) to determine whether it was cancelled or not (without worrying about the accompanying object at that moment). My object looks like:
import Foundation
#objc protocol Resultable {
var didCancel: Bool { get }
}
class Result<T>: Resultable {
let didCancel: Bool
let object: T?
init(didCancel: Bool, object: T?) {
self.didCancel = didCancel
self.object = object
}
}
The idea being that my Result object can wrap the didCancel flag and the actual object (which can be of any type), and the fact that it implements the Resultable protocol means that I can interrogate it at any point to see whether it was cancelled by casting it to Resultable.
I understand (while not liking it) that the protocol has to be prefixed with #objc so that we can cast to it (according to the Apple docs). Unfortunately, when I run the code below (in a playground or in a project), I get a nasty "does not implement methodSignatureForSelector:" error message:
let test = Result(didCancel: false, object: NSArray())
println(test.didCancel)
// transform the object into the form it will be received in
let anyTest: AnyObject = test
if let castTest = anyTest as? Resultable {
println(castTest.didCancel)
}
It seems that despite the protocol being prefixed with #objc, it also wants the actual class to inherit from NSObject (and this is not a requirement that Apple makes explicit). This is obviously a problem for a generic class.
Is there anything I am missing here? Any way to get this to work? Failing that, are there any workarounds (although I strongly believe that this kind of thing should be possible - perhaps we can hope that Apple will do away with the #objc protocol casting requirement at some stage)?
UPDATE
It looks like this is solved with Swift 1.2
You can now cast to a non-ObjC protocol, and without the object having to inherit from NSObject.
It seems that despite the protocol being prefixed with #objc, it also wants the actual class to inherit from NSObject
This is not true. For example, the following code works as expected:
#objc protocol MyProtocol {
var flag: Bool { get }
}
class MyClass: MyProtocol {
let flag = true
}
let foo:AnyObject = MyClass()
if let p = foo as? MyProtocol {
println(p.flag)
}
The problems is that: Any methods/properties declared in Swift Generic classes are invisible from Objective-C. Hence, from the perspective of #objc protocol Resultable, didCancel property declared in Result<T> is invisible. That's why methodSignatureForSelector: is called.
The workaround here is very annoying: You have to have non Generic base class for Result that implements didCancel.
#objc protocol Resultable {
var didCancel: Bool { get }
}
class ResultBase: Resultable {
let didCancel: Bool
init(didCancel: Bool) { self.didCancel = didCancel }
}
class Result<T>: ResultBase, Resultable {
// ^^^^^^^^^^^^
// You have to explicitly conforms `Resultable` here as well for some reason :/
let object: T?
init(didCancel: Bool, object: T?) {
self.object = object
super.init(didCancel: didCancel)
}
}
let test = Result(didCancel: false, object: NSArray())
let anyTest: AnyObject = test
if let castTest = anyTest as? Resultable {
println(castTest.didCancel) // -> outputs "false"
}