Substituting classes that adopt a particular protocol (Objective-c) - objective-c

In Objective-c if two classes adopt a particular protocol can instances of the classes be used interchangeably?
Say I have the following code:
#protocol MyProtocol
#required
#property (nonatomic, retain) SomeObject *object;
#end
#interface ClassA <MyProtocol>
#property (nonatomic, retain) SomeObject *object;
// ...
#end
#interface ClassB <MyProtocol>
#property (nonatomic, retain) SomeObject *object;
// ...
#end
Can I substitute (id <MyProtocol>)instanceOfClassB when a method expects an instance of ClassA ?

Nope. Instances of different classes that conform to the same protocol can be used interchangeably when the API is explicitly typed that way though, e.g.:
- (void)someMethod:(id <MyProtocol>)someObj;

Related

Declare protocol conformance in objective-c protocol forward-declaration

Assume a class has been defined like this in OriginalObject.h:
#protocol OriginalDelegate;
#interface OriginalObject : NSObject {}
#property (nullable, nonatomic, weak) id<OriginalDelegate> delegate;
#end
#protocol OriginalDelegate <NSObject>
// … delegate method declarations …
#end
Now, in ExtendedObject.h, I want to do this:
#import "OriginalObject.h"
#protocol ExtendedDelegate;
#interface ExtendedObject : OriginalObject {}
#property (nullable, nonatomic, weak) id<ExtendedDelegate> delegate;
#end
#protocol ExtendedDelegate <OriginalDelegate>
// … additional delegate method declarations …
#end
Attempting this gives me the following warning on the #property … delegate; line of ExtendedObject.h:
Property type 'id<ExtendedDelegate> _Nullable' is incompatible with type 'id<OriginalDelegate> _Nullable' inherited from 'OriginalObject'
It appears that the compiler doesn't know ExtendedDelegate will conform to OriginalDelegate. Moving the full protocol declaration above the ExtendedObject interface in ExtendedObject.h resolves the warning:
#import "OriginalObject.h"
#protocol ExtendedDelegate <OriginalDelegate>
// … additional delegate method declarations …
#end
#interface ExtendedObject : OriginalObject {}
#property (nullable, nonatomic, weak) id<ExtendedDelegate> delegate;
#end
What I'd like to know is… is there any way to tell the compiler in the forward-declaration that ExtendedDelegate will conform to OriginalDelegate (enabling use of something more like the first version of ExtendedObject.h above)?
Neither of the following attempts at such a forward-declaration seem to be valid syntax:
#protocol ExtendedDelegate <OriginalDelegate>;
#protocol ExtendedDelegate : OriginalDelegate;

How to make a subclass conform to a protocol in Objective-C?

I have a protocol which a base class implements. I have subclasses for which I want to enforce the protocol.
#class JSData;
#protocol JSDataProtocol <NSObject, NSCopying, NSMutableCopying>
#property (nonatomic, readonly) NSString *dataType;
#property (nonatomic, readwrite) id value;
- (JSData *)value;
- (BOOL)hasMeta;
// ...
#end
The base class is
#interface JSData : NSObject <JSDataProtocol, JSHashable, JSEquatable>
#end
I am then creating other classes like
#interface JSSymbol: JSData
- (NSString *)name;
- (JSData *)value;
#end
But the problem here is since JSData conforms to JSDataProtocol, if I missed implementing some method in JSSymbol, Xcode does not show any warning and I get runtime error. Is there a way to enforce that JSSymbol implements all methods defined in the JSDataProtocol?
JSSymbol derives from JSData, which already implements the JSDataProtocol methods. So no, you can't get compile-time errors for not implementing those methods.
If you really want this, you could either:
move the JSDataProtocol declaration from JSData down to the concrete classes.
get rid of JSData entirely and make JSDataProtocol inherit from JSHashable & JSEquatable
If you get rid of JSData, your protocol definition can be streamlined to:
#protocol JSDataProtocol <NSObject, NSCopying, NSMutableCopying,
JSHashable, JSEquatable>
#property (nonatomic, readonly) NSString *dataType;
#property (nonatomic, readwrite) id<JSDataProtocol> value;
#property (nonatomic, readonly) BOOL hasMeta;
#end

Declare instance variable in Objective-C and set in Swift

I want to be able to set the value of an instance variable from my Objective-C class in my Swift class. In my Swift class, I want to be able to say something like cameraViewController.ingestViewController = self and have that set the value of ingestViewController in my Objective-C class. Here is some code to demonstrate:
PhotoViewController.swift:
class PhotoViewController : UIViewController {
let cameraViewController = // reference to the CameraViewController
cameraViewController.ingestViewController = self
}
CameraViewController.h:
#interface CameraViewController : GSKCameraViewController
#end
CameraViewController.m:
#interface CameraViewController ()
#property (nonatomic, strong) UIView *toolbar;
#property (nonatomic, strong) UIButton *cameraButton;
#property (class, nonatomic, strong) UIViewController *ingestViewController;
#end
#implementation CameraViewController
UIViewController *ingestViewController
// rest of implementation
#end
I continue to get the error Value of type 'CameraViewController?' has no member 'ingestViewController'.
#property (class, nonatomic, strong) UIViewController *ingestViewController;
This is a class property, not instance variable property.
So just remove class attribute.
You've declared the ingestViewController property as a class property, not an instance property.
Remove the class attribute of the #property.
#property (nonatomic, strong) UIViewController *ingestViewController;
Once that is fixes, you need to make the property public. Move it to the .h file:
#interface CameraViewController : GSKCameraViewController
#property (nonatomic, strong) UIViewController *ingestViewController;
#end
All of the properties in the .m are private.
Lastly, remove the unnecessary line:
UIViewController *ingestViewController
from the .m file. That is actually declaring a global variable and is not in any way associated with the property of the same name.

Is it possible to define a property with Class type that conforms to protocol?

For example, I have MyFancyData protocol. How can I specify that MyFancyDataClass property accepts only classes that conforms to this protocol.
#interface MyObject : NSObject
#property Class MyFancyDataClass;
#property id<MyFancyData> myFancyDataClass;
Do you mean something like this?
#interface MyObject : NSObject
#property (nonatomic, assign) Class<MyFancyData> cls;
#end

How to properly subclass a delegate property in Objective-C?

In subclassing a class, I want to also subclass a delegate of the parent class given that the subclass now has additional functionality. What's the best way to go about doing this? If I just declare another delegate property in the subclass with the same name I would get a warning "Property type 'id' is incompatible with type 'id' inherited from 'ParentClass'
Given this example that produces the warning:
// Class A
#protocol ClassADelegete;
#interface ClassA : NSObject
#property (nonatomic, weak) id<ClassADelegete> delegate;
#end
#protocol ClassADelegete <NSObject>
- (void)classADidSomethingInteresting:(ClassA *)classA;
#end
// Class B
#protocol ClassBDelegete;
#interface ClassB : ClassA
#property (nonatomic, weak) id<ClassBDelegete> delegate; // Warning here
#end
#protocol ClassBDelegete <ClassADelegete>
- (void)classBDidSomethingElse:(ClassB *)classB;
#end
Two solutions that remove the warning are.
1) In the subclass, place the protocol definition before the class definition. This is what UITableViewDelegate in UITableView.h does:
// Class B
#class ClassB;
#protocol ClassBDelegete <ClassADelegete>
- (void)classBDidSomethingElse:(ClassB *)classB;
#end
#interface ClassB : ClassA
#property (nonatomic, weak) id<ClassBDelegete> delegate;
#end
2) In the subclass, add the original protocol alongside the new one:
// Class B
#protocol ClassBDelegete;
#interface ClassB : ClassA
#property (nonatomic, weak) id<ClassADelegete, ClassBDelegete> delegate;
#end
#protocol ClassBDelegete <ClassADelegete>
- (void)classBDidSomethingElse:(ClassB *)classB;
#end
I assume (1) works as Apple do it this way, Option (2) removes the warning but I haven't compiled and run anything setup this way.
Follow the example of NSTableView and NSOutlineView.
NSOutlineView is a subclass of NSTableView, and defines its own protocol for its dataSource and delegate.
NSTableView declares its delegate this way:
- (void)setDelegate:(id <NSTableViewDelegate>)delegate;
- (id <NSTableViewDelegate>)delegate;
and NSOutlineView:
- (void)setDelegate:(id <NSOutlineViewDelegate>)anObject;
- (id <NSOutlineViewDelegate>)delegate;
Apparently the compiler is more lenient with bare method declarations than it is with property declarations.
Unlike NSTable/OutlineView, you might want to make the subclass's protocol inherit from the base class's protocol, e.g.
#protocol SpecializedProtocol <BaseProtocol>
... it probably depends on the situation.