getter and setter in Swift (from ObjC)

I get ObjC code with a lot of constructs like this:

in header file:


#import <Cocoa/Cocoa.h>
@interface AAPLView : NSView
{
    NSImage *image;
}
@property(strong) NSImage *image;
@end


in implementation:


- (NSImage *)image {
    return image;
}
- (void)setImage:(NSImage *)newImage {
    if (image != newImage) {
        image = newImage;
        [self setNeedsDisplay:YES];
    }
}


How to port in swift ?


If I write :

import Cocoa
class AAPLView: NSView {
    var image : NSImage? {
        get {
            return self.image
        }
        set (newImage) {
            if (image != newImage) {
                self.image = newImage;
                self.needsDisplay = true
            }
        }
    }


I get an infinite loop on get as self.image calls itself repeatidly (why not the problem in ObjC ?)


So I write :

import Cocoa
class AAPLView: NSView {
    var image : NSImage? {
        didSet (newImage) {
            if (image != newImage) {
                self.image = newImage;
                self.needsDisplay = true
            }
        }
    }


What about the getter in this case ?

- (NSImage *)image {
    return image;
Answered by OOPer in 98785022

In fact my code is pure Swift, no ObjC inside (ObjC is the sample code from WWDC than I am trying to port to Swift).

If the sample code I'm seeing is near enough to the WWDC code, The `AAPLSlide` inherits `NSCollectionViewItem`, and I believe you have not rewritten NSCollectionViewItem (or its ancestor classes) in Swift. So, your code in Swift needs to interact with classes written in Objective-C.


super refers to the var "selected" in ancestor class that I override ?

Yes.


The property `selected` is declared in NSCollectionViewItem class header as:

@property (getter=isSelected) BOOL selected;

Objective-C compiler interprets it as:

- in the private ivar section

    BOOL _selected;

- and accessor methods

- (BOOL)isSelected;
- (void)setSelected:(BOOL)selected;


So, if `setSelected` is overridden in the Objective-C code, only setter of the property is overridden.

As you know, Swift cannot override only setter, so you need to keep getter as defined in the ancestor class, that is the part:

        get {
            return super.selected
        }

and it internally calls supeclass's getter `isSelected` as Objective-C method.

Try this:


import Cocoa
class AAPLView: NSView {
     var image : NSImage? {
          didSet {
               self.needsDisplay = image != oldValue)
          }
     }

}


In Swift, maintenance of the value "behind" the property comes for free. All you need to do is the extra work when a new value is set.

OK, that means that I use set or didSet only if I have a specific action to perform beyong mere value allocation.


WHat about the get ?


If I have in ObjC something lke :


- (NSInteger)pixelsHigh {
    if (imageProperties == nil) {
        [self loadMetadata];
    }
    return [[imageProperties valueForKey:(NSString *)kCGImagePropertyPixelHeight] intValue];
}

do I need a get in Swift ? Or just write :


    var pixelsHigh : Int {
        // get { ?
            if (imageProperties == nil) {
                self.loadMetadata()
            }
            return imageProperties!.valueForKey(String(kCGImagePropertyPixelHeight))!.integerValue
        // }
    }


If that's correct,, why should I declare a var and not make pixelsHigh a func pixelsHigh()


In such a case:


- (AAPLImageFile *)imageFile {
    return (AAPLImageFile *)(self.representedObject);
}

Should I create a var or a func() ?

why should I declare a var and not make pixelsHigh a func pixelsHigh()

If you can control whole parts of your app, it's your choice, to make it a `var` -- computed property, or a `func` with no parameters.


If you need to mix Swift code and Objective-C code in an app, you should follow the interoperability rule of Swift with Objective-C.


With only this method signature given:

- (NSInteger)pixelsHigh {

Swift imports it as a method with no parameters:

    func pixelsHigh() -> Int {


But, if there's a property declaration matching with the method name:

@property(readonly) NSInteger pixelsHigh;

Swift imports it as a readonly (so, you can omit `get {}`) computed property:

    var pixelsHigh: Int {


Should I create a var or a func() ?

If your code is nealy the same as 2015-10-21 version which I'm seeing, the `imageFile` method is private and used only in the implementation file.

So, it's your choice. Do as you like it.

The point is that in Swift, a stored property has its private backing storage (aka "instance variable") built in and maintained for free. That means you don't have to write an actual getter or setter, but let Swift do the work for you. This limits you to a didSet or willSet clause, if that does the extra work you want.


If you need to write an actual getter or setter (which make it a computed property rather than a stored property), there's no private storage managed for you, so you typically need to provide a private stored property where the data is actually stored.


For your second example, you might have to do this, or it might be a pure computed property (with no storage and so no private property) or it could be a property that's lazily initialized with a closure. Swift gives you more (and more subtle) choices than Obj-C, so you don't have to limit yourself to the Obj-C way of thinking about things.

Still a bit confusing for me, even after reading through in detail Swift programming language doc.


So, in the case of imageFile (only used in implementation indeed), I wrote a function


    func imageFile() -> ImageFile? {
        if (self.representedObject != nil) {
            return (self.representedObject as! ImageFile)
        } else {
            return nil
        }
    }


Certainly because of problems elsewhere in my code, self.representedObject is always nil


representedObject is only in implementation and set (in ObjC) by :

- (void)setRepresentedObject:(id)newRepresentedObject {
    [super setRepresentedObject:newRepresentedObject];
    [self.imageFile requestPreviewImage];
}


I converted setRepresentedObject in Swift with a didSet:

    override var representedObject: AnyObject? {
        didSet (newRepresentedObject) {
            super.representedObject = newRepresentedObject
            self.imageFile()?.requestPreviewImage()

            if let myImageFile = self.imageFile() {
                myImageFile.requestPreviewImage()
            } else {
                print("no image file for representedObject")
            }
        }
    }


but newRepresentedObject is always nil. Am I doing sthing wrong in representedObject handling ?



In the same ObjC, there is another declaration in the class

@implementation AAPLSlide
- (void)setSelected:(BOOL)selected {
    [super setSelected:selected];
    // Relay the new "selected" state to our AAPLSlideCarrierView.
    [(AAPLSlideCarrierView *)[self view] setSelected:selected];
}


I cannot figure out how [(AAPLSlideCarrierView *)[self view] setSelected:selected]; can apply to another class @interface AAPLSlideCarrierView : NSView


Should I write


    override var selected : Bool {
        get {
            return self.selected
        }
        set {
            super.selected = newValue
            (self.view as! SlideCarrierView).selected = newValue
        }
    }

It seems my words are a little too short.

The `imageFile` exists only in the class's implementation file, but `setRepresentedObject` method is another thing.

As you added `override` on top of it, the property is declared in an anscestor class, which is written in Objective-C. The declaration looks like this, if rewritten in Swift:

    private var _representedObject: AnyObject?

    var representedObject: AnyObject? {
        get {
            return _representedObject
        }
        set {
            _representedObject = newValue
        }
    }

Your overridden declaration re-declare the property as a renewed stored property, which means it has no relationship with the original backend store `_representedObject`.


You need to override it as:

    override var representedObject: AnyObject? {
        get {
            return super.representedObject
        }
        set(newRepresentedObject) {
            super.representedObject = newRepresentedObject
          
            //...
            self.imageFile?.requestPreviewImage()
        }
    }

To make all thing consistent with Objective-C ancestor class, you always need to override properties as computed property and access the original backend store using `super`.


Thus, the `selected` property should be overridden like this:

    override var selected: Bool {
        get {
            return super.selected
        }
        set {
            super.selected = newValue
            (self.view as! SlideCarrierView).selected = newValue
        }
    }

You need to return super.selected than self.selected. (Or else, as you see, get into infinite loop.)

Thanks again.

In fact my code is pure Swift, no ObjC inside (ObjC is the sample code from WWDC than I am trying to port to Swift).

So, when we say

return super.selected

super refers to the var "selected" in ancestor class that I override ?

Accepted Answer

In fact my code is pure Swift, no ObjC inside (ObjC is the sample code from WWDC than I am trying to port to Swift).

If the sample code I'm seeing is near enough to the WWDC code, The `AAPLSlide` inherits `NSCollectionViewItem`, and I believe you have not rewritten NSCollectionViewItem (or its ancestor classes) in Swift. So, your code in Swift needs to interact with classes written in Objective-C.


super refers to the var "selected" in ancestor class that I override ?

Yes.


The property `selected` is declared in NSCollectionViewItem class header as:

@property (getter=isSelected) BOOL selected;

Objective-C compiler interprets it as:

- in the private ivar section

    BOOL _selected;

- and accessor methods

- (BOOL)isSelected;
- (void)setSelected:(BOOL)selected;


So, if `setSelected` is overridden in the Objective-C code, only setter of the property is overridden.

As you know, Swift cannot override only setter, so you need to keep getter as defined in the ancestor class, that is the part:

        get {
            return super.selected
        }

and it internally calls supeclass's getter `isSelected` as Objective-C method.

getter and setter in Swift (from ObjC)
 
 
Q