Swift init flexibility ? vs objc

I finally got out of my problem (with the help of this forum in another thread, thanks), but I would like to clarify a point of Swift syntax vs objc.


In objc, it is possible (may be not desirable) for an init to create an instance which is of a subclass's type.


Here is an example taken from a tutorial :

DesktopEntity is aclass with 2 subclasses : DesktopImageEntity and DesktopFolderEntity

entityForURL can return either as one of the subclasses or nil


+ (DesktopEntity *)entityForURL:(NSURL *)url {
    NSString *typeIdentifier;
    if ([url getResourceValue:&typeIdentifier forKey:NSURLTypeIdentifierKey error:NULL]) {
        NSArray *imgTypes = [NSImage imageTypes];
        if ([imgTypes containsObject:typeIdentifier]) {
            return [[DesktopImageEntity alloc] initWithFileURL:url];
        } else if ([typeIdentifier isEqualToString:(NSString *)kUTTypeFolder]) {
            return [[DesktopFolderEntity alloc] initWithFileURL:url];
        }
    }
    return nil;
}

this class method is called from an init in DesktopEntity

- (id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type {
    NSURL *url = [[NSURL alloc] initWithPasteboardPropertyList:propertyList ofType:type];
    self = [DesktopEntity entityForURL:url];
    return self;
}

Checking in debugger shows that self is effectively of type DesktopImageEntity or DesktopFolderEntity


I have tried to achieve same thing in Swift, but could not find how.

init can return nil or DesktopEntity, but never a subtype.

However, compiler admits, in the init, the following syntax, but fails in execution of the downcast:

(self as? DesktopImageEntity)!.image = NSImage(byReferencingURL: url!)


Am I missing something, or is it just impossible in Swift ? (I thought to use generics, but could not get it work).

Answered by OOPer in 110451022

So, I could confirm `initWithPasteboardPropertyList:ofType:` is called while executing `enumerateDraggingItemsWithOptions:forView:classes:searchOptions:usingBlock:`.

First, conformance to NSPasteboardReading is dynamically checked, then `+ readableTypesForPasteboard:`, `+ readingOptionsForType:pasteboard:`, and then `initWithPasteboardPropertyList:ofType:` is called. Some efforts finding workarounds (even if it contains unrecommended hacks) were futile...


Sorry for bothering and adding some noises to this thread. I really regret I was not trying to fully understand the discussion in the other thread.

is it just impossible in Swift ?

It seems to be a correct answer, impossible.


If I were given that code to translate into Swift, I would implement `initWithPasteboardPropertyList:ofType:` as a class method, not an initializer.

OK, impossible in Swift 2 (let me wait for Swift 5 ?)


In the code, the initializer is called in enumerateDraggingItemsWithOptions to instanciate draggingItem; I understood that this has to call the initializer (am I wrong ?)


- (void)tableView:(NSTableView *)tableView updateDraggingItemsForDrag:(id<NSDraggingInfo>)draggingInfo {
    if ([draggingInfo draggingSource] != tableView) {
        NSArray *classes = @[[DesktopEntity class], [NSPasteboardItem class]];
        NSTableCellView *tableCellView = [tableView makeViewWithIdentifier:@"ImageCell" owner:self];
        __block NSInteger validCount = 0;
        [draggingInfo enumerateDraggingItemsWithOptions:0 forView:tableView classes:classes searchOptions:nil usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {
            if ([draggingItem.item isKindOfClass:[DesktopEntity class]]) {
                DesktopEntity *entity = (DesktopEntity *)draggingItem.item;

From the docs:


-=-

NSDraggingItem objects have extremely limited lifetimes. Do not retain these items as changing outside of the prescribed lifetimes have no impact on the drag.

When the

NSDraggingSession
method
beginDraggingSessionWithItems:event:source:
is called, the dragging items passed to the method are consumed immediately and are not retained.


Any further changes to the dragging item associated with the returned

NSDraggingSession
must be done via the enumeration method
enumerateDraggingItemsWithOptions:forView:classes:searchOptions:usingBlock:
.


When enumerating,

NSDraggingItem
instances are created right before being given to the enumeration Block. After returning from the Block, the
NSDraggingItem
instance is no longer valid.

-=-

Sorry, shame I was missing the basic usage of the class... I'll re-consider the latter part of my last reply, having KMT's comment in mind.

Accepted Answer

So, I could confirm `initWithPasteboardPropertyList:ofType:` is called while executing `enumerateDraggingItemsWithOptions:forView:classes:searchOptions:usingBlock:`.

First, conformance to NSPasteboardReading is dynamically checked, then `+ readableTypesForPasteboard:`, `+ readingOptionsForType:pasteboard:`, and then `initWithPasteboardPropertyList:ofType:` is called. Some efforts finding workarounds (even if it contains unrecommended hacks) were futile...


Sorry for bothering and adding some noises to this thread. I really regret I was not trying to fully understand the discussion in the other thread.

Swift init flexibility ? vs objc
 
 
Q