Swift generics and extension functions override - swift

Having a strange problem with generics on Swift
have a NSManagedObject subclasses hierarchy with extensions:
BaseCloudKitItem<-MyObject
extension BaseCloudKitItem {
func updateDataWithCKRecord(record: CKRecord!, inManagedObjectContext context: NSManagedObjectContext!) {
cloudKitRecordID = record?.recordID.recordName
cloudKitType = record?.recordType
println("\(self) updateDataWithCKRecord:inManagedObjectContext: called")
}
}
extension MyObject {
override func updateDataWithCKRecord(record: CKRecord!, inManagedObjectContext context: NSManagedObjectContext!) {
super.updateDataWithCKRecord(record, inManagedObjectContext: context)
title = record?.objectForKey("title") as? NSString
}
}
have a generic class:
class CloudFetcher<T : BaseCloudKitItem> {
class func someFetchingAndMappingMethod(completion:(() -> Void)?) {
someFetchingMethodWithCompletion { (records, error) in
if let err = error {
completion?(nil, err)
} else {
NSManagedObjectContext.saveDataInBackgroundWithBlock({ (localContext) in
T.deleteAllInContext(localContext)
for record in records {
let object = T.createEntityInContext(localContext) as T
object.updateDataWithCKRecord(record, inManagedObjectContext: localContext)
}
}, completion: completion)
}
}
}
}
and usage:
CloudFetcher<MyObject>.someFetchingAndMappingMethod { _ in
}
Passed class into generic CloudFetcher is MyObject
So the problem is I discovered that methods called inside generic from the base class BaseCloudKitItem, not from MyObject.
If I change the CloudFetcher interface declaration from class CloudFetcher<T : BaseCloudKitItem> to class CloudFetcher<T : MyObject> it works perfect, but there is no point to use generics this way.
Also, in logs from println() inside target method I can see needed class, but needed method is still not getting called:
<MyObject: 0x7aab7130> (entity: MyObject; id: 0x802f5bf0 <x-coredata:///InstrumentToken/t1AAF2438-9DF7-44DA-89B2-C3C1BE3D91FE17> ; data: {
cloudKitRecordID = "fb4fac88-40e5-4aef-af3c-6e36867dbf5f";
cloudKitType = MyObject;
title = nil;
}) updateDataWithCKRecord:inManagedObjectContext: called
Thats looks pretty strange for me, maybe somebody can offer me someway I can solve it?
Thanks!

Related

Is it possible to call generic object constructor using reflection in swift?

let's say we have a class
class Test: NSObject {
let batman: String
let spiderman: Int
let superman: NSObject
init(batman: String, spiderman: Int, superman: NSObject) {
self.batman = batman
self.spiderman = spiderman
self.superman = superman
}
}
And a generic method for initialization:
func resolve<T: NSObject>(args: Any...) throws -> T {
// if let object = initWithReflectionSomeHow(T.className, arg1, arg2...) {
// return object
// } else {
// throw exception
// }
}
I found a way to to this without parameters like this:
func resolve<T: NSObject>() throws -> T {
if let objectClass = NSClassFromString(T.className) as? T.Type {
let object = objectClass.init()
return object
} else {
//throw classNotFoundException
}
}
So I would just call:
let obj = try resolve() as Test
but not sure how I can inject parameters. Is it even possible?

How can you specify a generic based on the type an extension is applied to? [duplicate]

I'm looking to be able to pull out an instance of a UIView subclass from a Nib.
I'd like to be able to call MyCustomView.instantiateFromNib() and have an instance of MyCustomView. I'm almost ready to just port the working Objective-C code I have via the bridging header, but figured I'd try the idiomatic approach first. That was two hours ago.
extension UIView {
class func instantiateFromNib() -> Self? {
let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)
for topLevelObject in topLevelObjects {
if (topLevelObject is self) {
return topLevelObject
}
}
return nil
}
}
Now if (topLevelObject is self) { is wrong because "Expected type after 'is'". What I've tried after that shows a lot about what I don't understand about the Swift type system.
if (topLevelObject is Self) {
if (topLevelObject is self.dynamicType) {
if (topLevelObject is self.self) {
A million other variations that are not even wrong.
Any insight is appreciated.
Using the approach from How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
you can define a generic helper method which infers the type of self from the calling context:
extension UIView {
class func instantiateFromNib() -> Self? {
return instantiateFromNibHelper()
}
private class func instantiateFromNibHelper<T>() -> T? {
let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)
for topLevelObject in topLevelObjects {
if let object = topLevelObject as? T {
return object
}
}
return nil
}
}
This compiles and works as expected in my quick test. If
MyCustomView is your UIView subclass then
if let customView = MyCustomView.instantiateFromNib() {
// `customView` is a `MyCustomView`
// ...
} else {
// Not found in Nib file
}
gives you an instance of MyCustomView, and the type is
inferred automatically.
Update for Swift 3:
extension UIView {
class func instantiateFromNib() -> Self? {
return instantiateFromNibHelper()
}
private class func instantiateFromNibHelper<T>() -> T? {
if let topLevelObjects = Bundle.main.loadNibNamed("CustomViews", owner: nil, options: nil) {
for topLevelObject in topLevelObjects {
if let object = topLevelObject as? T {
return object
}
}
}
return nil
}
}
I believe the conditional expression you're looking for is topLevelObject.dynamicType == self
Combining this with unsafeBitCast (which, by Apple's own documentation, "Breaks the guarantees of Swift's type system"), we can forcefully downcast topLevelObject to self's type. This should be safe because we already made sure that topLevelObject is the same type as self
This is one way to get around the helper method using generics that Martin R described.
extension UIView {
class func instantiateFromNib() -> Self? {
let bundle = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)
for topLevelObject in topLevelObjects {
if topLevelObject.dynamicType == self {
return unsafeBitCast(topLevelObject, self)
}
}
return nil
}
}
Note that Apple also says in their documentation for unsafeBitCast:
There's almost always a better way to do anything.
So be careful!

Swift 2.0: Protocol Extension Class Method returning Self

In order to extend some functionalities of my NSManagedObject subclasses, I have defined a series of protocols:
protocol ManagedObjectFindable {
static func find(format:String, arguments: [AnyObject]?, inContext context:NSManagedObjectContext, entityName:String?) -> Self?
}
protocol UniquelyIdentifiable: ManagedObjectFindable {
var identifier: NSNumber? { get }
static func findWithIdentifier(identifier: Int, inContext context:NSManagedObjectContext) -> Self?
}
So then every NSManagedObject that has identifier in its data model entity can conform to UniquelyIdentifiable.
For that purpose I am utilising Swift 2.0 Protocol Extensions, where:
extension UniquelyIdentifiable {
static func findWithIdentifier(identifier: Int, inContext context:NSManagedObjectContext) -> Self? {
return self.find("identifier == %lld", arguments: [NSNumber(longLong: Int64(identifier))], inContext: context, entityName:nil)
}
}
Where find is defined as:
extension NSManagedObject: ManagedObjectFindable {
/** returns single entity if found, nil otherwise */
class func find(format:String, arguments: [AnyObject]?, inContext context:NSManagedObjectContext, entityName:String? = nil) -> Self? {
let objectEntityName:String
if let name = entityName {
objectEntityName = name
} else {
objectEntityName = String(self)
}
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(objectEntityName, inManagedObjectContext: context)
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: format, argumentArray: arguments)
var persistentEntityº:NSManagedObject?
context.performBlockAndWait {
do {
let fetchResults = try context.executeFetchRequest(fetchRequest)
if (fetchResults.count != 0){
persistentEntityº = fetchResults.first as? NSManagedObject
}
} catch {}
}
if let persistentEntity = persistentEntityº {
return _safeObjectSelfCast(persistentEntity)
} else {
return nil
}
}
}
func _unsafeObjectSelfCast<T>(obj: AnyObject!) -> T { return obj as! T }
func _safeObjectSelfCast<T>(obj: AnyObject) -> T? { return obj as? T }
Now these methods correctly return Self? and compiler is silent on the coding time, however when compiling it gives me that error Method 'findWithIdentifier(_:inContext:)' in non-final class must return 'Self' to conform to protocol 'UniquelyIdentifiable'
Now the thing is that if instead of implementing that method in a protocol extension I would just extend my NSManagedObject subclass, it will go fine, but that kills the purpose of protocol extensions, when you are completely duplicating the same code across dozens of your NSManagedObject subclasses.
Any workaround, or I am really missing something?
Short answer:
Change Self? in extension to the NSManagedObject?.
Long answer: Self in protocol requirement acts as a placeholder for the class that will implement that protocol. So if you have
protocol SomeProtocol {
func returnSomething() -> Self
}
That means that if you implement it on Int, function returnSomething() should return Int, and if you implement it on Double it should return Double.
Since you are implementing UniquelyIdentifiable on NSManagedObject and your protocol has Self? requirement, you should return NSManagedObject?.

Using 'self' in class extension functions in Swift

I'm looking to be able to pull out an instance of a UIView subclass from a Nib.
I'd like to be able to call MyCustomView.instantiateFromNib() and have an instance of MyCustomView. I'm almost ready to just port the working Objective-C code I have via the bridging header, but figured I'd try the idiomatic approach first. That was two hours ago.
extension UIView {
class func instantiateFromNib() -> Self? {
let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)
for topLevelObject in topLevelObjects {
if (topLevelObject is self) {
return topLevelObject
}
}
return nil
}
}
Now if (topLevelObject is self) { is wrong because "Expected type after 'is'". What I've tried after that shows a lot about what I don't understand about the Swift type system.
if (topLevelObject is Self) {
if (topLevelObject is self.dynamicType) {
if (topLevelObject is self.self) {
A million other variations that are not even wrong.
Any insight is appreciated.
Using the approach from How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
you can define a generic helper method which infers the type of self from the calling context:
extension UIView {
class func instantiateFromNib() -> Self? {
return instantiateFromNibHelper()
}
private class func instantiateFromNibHelper<T>() -> T? {
let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)
for topLevelObject in topLevelObjects {
if let object = topLevelObject as? T {
return object
}
}
return nil
}
}
This compiles and works as expected in my quick test. If
MyCustomView is your UIView subclass then
if let customView = MyCustomView.instantiateFromNib() {
// `customView` is a `MyCustomView`
// ...
} else {
// Not found in Nib file
}
gives you an instance of MyCustomView, and the type is
inferred automatically.
Update for Swift 3:
extension UIView {
class func instantiateFromNib() -> Self? {
return instantiateFromNibHelper()
}
private class func instantiateFromNibHelper<T>() -> T? {
if let topLevelObjects = Bundle.main.loadNibNamed("CustomViews", owner: nil, options: nil) {
for topLevelObject in topLevelObjects {
if let object = topLevelObject as? T {
return object
}
}
}
return nil
}
}
I believe the conditional expression you're looking for is topLevelObject.dynamicType == self
Combining this with unsafeBitCast (which, by Apple's own documentation, "Breaks the guarantees of Swift's type system"), we can forcefully downcast topLevelObject to self's type. This should be safe because we already made sure that topLevelObject is the same type as self
This is one way to get around the helper method using generics that Martin R described.
extension UIView {
class func instantiateFromNib() -> Self? {
let bundle = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)
for topLevelObject in topLevelObjects {
if topLevelObject.dynamicType == self {
return unsafeBitCast(topLevelObject, self)
}
}
return nil
}
}
Note that Apple also says in their documentation for unsafeBitCast:
There's almost always a better way to do anything.
So be careful!

Swift cast self in class func

i have an issue in swift where i want to check for protocol conformance in a class func and then use some static property when the check succeeds. Here's the playground code:
import UIKit
#objc protocol SearchableObject {
static var sortDescriptors: [NSSortDescriptor] { get }
}
class Test: NSObject {
}
extension Test {
class func loadData() {
if self is SearchableObject {
println(valueForKey("sortDescriptors"))
}
else {
println("not searchable")
}
}
}
class SearchableTest: Test, SearchableObject {
class var sortDescriptors: [NSSortDescriptor] {
return [NSSortDescriptor(key: "property", ascending: true)]
}
}
Test.loadData() // prints "not searchable"
SearchableTest.loadData() // prints "Optional((\n "(property, ascending, compare:)"\n))"
the problem is i can check for protocol conformance, but i can't cast self, that's why i'm using valueForKey().
(self as SearchableObject).sortDescriptors
^ 'SearchableObject' does not have a member named 'sortDescriptors'
casts self to an instance conforming to SearchableObject making the static var inaccessible. As don't like the valueForKey() approach i want to ask whether there is a better way to achieve this?
Thanks in advance :)
In class methods, self is a class, not instance. So the right - but non working - way is:
class func loadData() {
if let cls = self as? SearchableObject.Type {
// ^^^^^
println(cls.sortDescriptors)
// ^ [!] error: accessing members of protocol type value 'SearchableObject.Type' is unimplemented
}
As you can see, it's not implemented :(
No worries, there is a workaround. As long as the protocol is declared as #objc, AnyClass has that properties. You can cast self to AnyClass, then call .sortDescriptors:
class func loadData() {
if self is SearchableObject.Type {
println((self as AnyClass).sortDescriptors)
}
Essentially, this workaround is the same as valueForKey, but it doesn't use strings at least.
self as SearchableObject returns a SearchableObject instance. But you want the class. That's SearchableObject.Type.
So you meant this:
extension Test {
class func loadData() {
if let s = self as? SearchableObject.Type { // <<===
println(s.sortDescriptors)
}
else {
println("not searchable")
}
}
}
But "accessing members of protocol type value...is unimplemented." So the compiler knows about this, and specifically can't handle it currently. Of course all of this is fine if you use instances:
import Foundation
protocol SearchableObject {
var sortDescriptors: [NSSortDescriptor] { get }
}
class Test {}
extension Test {
func loadData() {
if let s = self as? SearchableObject {
println(s.sortDescriptors)
}
else {
println("not searchable")
}
}
}
class SearchableTest: Test, SearchableObject {
static var sharedSortDescriptors = [NSSortDescriptor(key: "property", ascending: true)]
var sortDescriptors: [NSSortDescriptor] { return self.dynamicType.sharedSortDescriptors }
}
Test().loadData() // prints "not searchable"
SearchableTest().loadData()