Swift 1.2 error on Objective-C protocol using getter - objective-c

This Objective-C protocol used to work in Swift 1.1, but now errors in Swift 1.2.
Objective-C Protocol stripped down to the one problematic property:
#protocol ISomePlugin <NSObject>
#property (readonly, getter=getObjects) NSArray * objects;
#end
class SomePlugin: NSObject, ISomePlugin {
var objects: [AnyObject]! = nil
func getObjects() -> [AnyObject]! {
objects = ["Hello", "World"];
return objects;
}
}
Here is the Swift 1.2 error:
Plugin.swift:4:1: error: type 'SomePlugin' does not conform to protocol 'ISomePlugin'
class SomePlugin: NSObject, ISomePlugin {
^
__ObjC.ISomePlugin:2:13: note: protocol requires property 'objects' with type '[AnyObject]!'
#objc var objects: [AnyObject]! { get }
^
Plugin.swift:6:9: note: Objective-C method 'objects' provided by getter for 'objects' does not match the requirement's selector ('getObjects')
var objects: [AnyObject]! = nil
^
If I change the Protocol (which I cannot really do since it is from a third party) to the following, the code compiles fine.
// Plugin workaround
#protocol ISomePlugin <NSObject>
#property (readonly) NSArray * objects;
- (NSArray *) getObjects;
#end
Thanks in advance.

You can implement
#property (readonly, getter=getObjects) NSArray * objects;
as a read-only computed property with an #objc attribute
specifying the Objective-C name.
If you need a stored property as backing store
then you'll have to define that separately.
Example:
class SomePlugin: NSObject, ISomePlugin {
private var _objects: [AnyObject]! = nil
var objects: [AnyObject]! {
#objc(getObjects) get {
_objects = ["Hello", "World"];
return _objects;
}
}
}

Related

How do I remove swift non-optional unwrapping with obj-c class instance?

I'm trying to achieve/force this safe & clean swift syntax
struct PopButton: View
{
var Label = PopEngineLabel(label:"Hello")
var body: some View
{
Text(Label.label)
}
}
Ojective-c
#interface PopEngineLabel : NSObject
#property (strong, nonatomic) NSString* label;
- (id)initWithLabel:(NSString*)label;
#end
#implementation PopEngineLabel
- (id)initWithLabel:(NSString*)label
{
if ( label )
self.label = label;
else
self.label = #"<null>";
return self;
}
#end
But in the swiftUI code, I get the error
error: value of optional type 'PopEngineLabel?' must be unwrapped to refer to member 'label' of wrapped base type 'PopEngineLabel'
Text(Label.label)
I can remove the errors with
Text(Label?.label ?? "default")
Text(Label!.label)
I'm assuming this is because all objective-c class/instances are implicitly optional.... BUT, the following code makes it non-optional, but crashes at runtime, as it hasn't done my objective-c initialiser (and .label is nil)
struct PopButton: View
{
var Label = PopEngineLabel()
var body: some View
{
Text(Label.label)
}
}
Can I force the user to use a constructor/initialiser AND be a non-optional in swift? (without making an intermediate swift class)
You can use nullability specifiers in Objective-C to tell Swift whether a specific property can be nil or not (and hence whether Swift should treat it as an Optional or not). By default, all reference types in Swift can be null, since they are pointers and hence by default, Swift treats all Obj-C pointer properties as implicitly unwrapped optionals.
However, this won't make the inherited initialiser from NSObject initialise label correctly (or make that initialiser unusable). So you need to assign a default value to label in the default init inherited from NSObject.
#interface PopEngineLabel : NSObject
#property (strong, nonatomic, nonnull) NSString* label;
- (instancetype)initWithLabel:(nonnull NSString*)label;
- (nonnull instancetype)init NS_UNAVAILABLE; // disable default initialiser in Swift
#end
#implementation PopEngineLabel
- (instancetype)init {
self = [super init];
if (self) {
self.label = #"<null>";
}
return self;
}
- (instancetype)initWithLabel:(nonnull NSString*)label {
self = [super init];
if (self) {
if (label) {
self.label = label;
} else {
self.label = #"<null>";
}
}
return self;
}
#end
Couple of other things to bear in mind in Objective-C:
Your init shouldn't return id, but instancetype
You should delegate to super init

Subclassing Objective-C class with factory method in Swift, the generics doesn't work

I have a Base class with a factory method written in Objective-C.(some lib)
#interface Base : NSObject
#property(nonatomic, strong) NSString *content;
+ (instancetype)baseWithContent:(NSString *)content;
#end
//==================
#implementation Base
+ (instancetype)baseWithContent:(NSString *)content {
Base* base = [[Base alloc]init];
base.content = content;
return base;
}
#end
Then I subclassing it in swift and casting it into AnyObject.(ignore the Bridging-Header part)
class Child: Base {}
var c = Child(content: "Child")
print("before casting", type(of:c))
print("after casting", type(of:c as AnyObject))
Get this strange result that it become a Base after casting.
before casting Optional<Child>
after casting Base
Actually if i use a designated initializer to override the generated-convenience initializer from objective-c, I get the right result.
class Child: Base {
init?(content:String) {
super.init()
self.content = content
}
}
before casting Optional<Child>
after casting Child
Am I making any mistake? Thanks for answering.
I'm using Xcode Version 8.1 (8B62) and Swift 3.0.1
Your factory method implementation in Obj-C is wrong. It always creates an instance of Base. To fix it:
+ (instancetype)baseWithContent:(NSString *)content {
Base *base = [[self alloc] init]; //self instead of Base
base.content = content;
return base;
}
Basically, your factory method implementation doesn't match its return type, the result are type problems in Swift because Swift trusts type declarations.

Cannot access a bridged NSArray from Swift

Why can't I use an Objective-C NSArray from Swift? According to the documentation: Working with Cocoa Data Types Swift should be bridging an NSArray<Type*>* to [Type].
I have this object defined in Objective-C:
#interface Data : NSObject
#property (strong, nonatomic, readonly) NSArray<NSNumber*>* arrayOfNumbers;
#end
This method defined in Objective-C:
#protocol DataReceiver
- (void)onDataReceived:(Data*)data;
#end
And this defined in Swift:
class MyDataReceiver : DataReceiver {
func onDataReceived(data: Data!) {
test(data.arrayOfNumbers[0]);
}
func test(num: Float) {
NSLog("%f", num);
}
}
And I'm getting the error: 'Type [NSNumber]! has no subscript members'. But this:
NSLog("%f", arrayOfNumbers[0]);
Compiles just fine....
What's happening here?
The error message does not make sense but it is caused by the fact that you are trying to pass NSNumber to a method accepting a Float. That means that the compiler is trying to find a subscript method that would return a Float but obviously it can't find it.
func test(num: NSNumber) {
NSLog("%#", num);
}
Should fix the problem.
Objective-C to Swift bridging works as expected.

Swift Classes vs. Objective-C Classes. Am I on the right track?

I am new to Swift. I've taken a couple of online course and have started converting an existing Objective-C project to Swift as a learning experience. I have a few questions if someone has time. I've searched the board but haven't found the answers. I am sorry if I missed them.
Delegate Protocols - I'm used to defining my own in Objective-C. Below I have the original Objective-C version and below it I have my new Swift version. Have I followed the correct design pattern for Swift?
I find myself making optionals for all of the properties especially objects like NSData or custom classes. Is this practice acceptable? I am not sure how I would know an initial value for most objects. I know the language wants you to set an initial value but it seems strange for certain objects.
If I am not mistaken we do not have to call self.super init in custom initializers in Swift. Is this correct?
Objective-C Version
#protocol FLOParserHandlerDelegate;
#interface FLOParserHandler: NSObject <NSXMLParserDelegate>
// Properties
#property (nonatomic, strong) NSMutableData *PHData;
#property (nonatomic, strong) NSMutableString *currentParsedCharacterData; // This grabs the characters as they come in and adds them together.
#property (nonatomic, strong) NSMutableArray *XMLDataArray; // This is the master array that holds all of the article arrays with the date, title and link objects.
#property (nonatomic, strong) NSMutableDictionary *dateTitleLinkDictionary;// This is used to gather the date, title and link in an array to added to the master array.
// Delegate Property
#property (nonatomic, weak) id <FLOParserHandlerDelegate> delegate;
// init Methods
- (id) initWithCHData: (NSMutableData *) data;
// Class Methods
-(void) startParser;
#end
#pragma mark-
#pragma mark FLOParserHandler Protocol Definition
#protocol FLOParserHandlerDelegate
#optional
- (void) floParserHandlerDidFinishParsing: (FLOParserHandler *) parserHandler;
- (void) floParserHandler: (FLOParserHandler *) parserHandler didFailWithError: (NSError *) error;
#end
Swift Version
import Foundation
protocol FLOParserHandlerDelegate
{
func floParserHandlerDidFinishParsing(parserHandler : FLOParserHandler) -> ()
func floParserHandler(parserHandler : FLOParserHandler, error : NSError) -> ()
}
// Note that we have to inherit from NSObject here. I believe this iis because we are mixing it with Objective-C.
class FLOParserHandler : NSObject, NSXMLParserDelegate
{
var PHData : NSData?
var currentParsedCharacterData : String?
var XMLDataArray : [String]?
var dateDictionary : [String:NSDate]?
var titleDictionary : [String:String]?
var linkDictionary : [String:String]?
// Delegate Property
var delegate : FLOParserHandlerDelegate?
// Init Methods
init(data : NSData)
{
self.PHData = data
}
// Class Methds
func startParser()
{
var parser = NSXMLParser(data: self.PHData)
parser.delegate = self
parser.parse()
}
}
Thank you,
Jon
Your protocol definition is valid. There is one little thing you should know about:
As with type property requirements, you always prefix type method
requirements with the class keyword when they are defined in a
protocol
protocol SomeProtocol {
class func someTypeMethod()
}
Its perfectly fine to use optionals, or you may use implicity unwrapped optionals like NSData!. In which case you should do this and where no, you may read here: Why create "Implicitly Unwrapped Optionals"?.
Shortly, you doing this in following situations:
a) Constant cannot be defined using initializtion, but you know that it would not be nil (otherwise app will crash)
b) Objective-C Api required you to use pointers, and pointers in Obj-C could be nil. In that case you use imilicity unwrapped optionals.
You always have to call 'super' if you have superclass, to be sure, that class is properly initialized.
Hope that helps.

How to deal with double composition and inheritance?

I found this related question: How do I use composition with inheritance?
I would like to do the same with Objective-C, that is to say that a GenericView knows that its property obj is a GenericObject, and that a SpecializedView knows that the very same obj property is a SpecializedObject.
Here is an example that will be clearer:
//
// Example.m
#import <UIKit/UIKit.h>
/* HEADER */
// Electrical Machine
#interface ElectricalMachine : NSObject {
}
- (void)plugIn;
#end
// Toaster
#interface Toaster : ElectricalMachine {
}
- (float)getThermostat;
#end
// GenericView
#interface GenericView : NSObject {
ElectricalMachine *machine;
}
- (void)doSomethingGeneric;
#property (nonatomic, retain) ElectricalMachine *machine;
#end
//SpecializedView
#interface SpecializedView : GenericView {
}
- (void)doSomethingSpecialized;
#end
/* IMPLEMENTATION */
// GenericView
#implementation GenericView
#synthesize machine;
- (void)doSomethingGeneric {
Toaster *toaster = [[Toaster alloc] init];
[toaster plugIn];
self.machine = toaster;
[toaster release];
}
#end
// SpecializedView
#implementation SpecializedView
- (void)doSomethingSpecialized {
/* ERROR HERE
* Incompatible types in initialization
* 'ElectricalMachine' may not respond to '-getThermostat'
*/
float r = [machine getThermostat];
r = r;
// ...
}
#end
As you see, I get an error at the end, because for SpecializedView the machine property is an ElectricalMachine, not a Toaster.
Thank you very much for your help!
Old Question
Here is the first version of my question, which was maybe too cryptic:
I have the following generic view:
#interface GenericView {
GenericObject obj;
}
- (id)doSomething;
I also have the following specialized view:
#interface SpecializedView : GenericView {
}
- (id)doSomethingElse;
I have the following object:
#interface GenericObject {
}
- (id)plugIn;
and the following specialized object:
#interface SpecializedObject : GenericObject {
}
- (float)toastTime;
Let's say I want GenericView to handle GenericObject, and SpecializedView to handle the same object, knowing that it is SpecializedObject.
Let me explain by showing implementations:
GenericView doSomething
- (id)doSomething {
[obj plugIn];
}
SpecializedView doSomethingElse
- (id)doSomethingElse {
// ERROR here
float time = [obj toastTime];
}
I will get the following warning:
'GenericObject' may not respond to '-toastBread'
and the following error:
Incompatible types in assignement
Which is logical, since I have defined the type of obj as GenericObject. I want to be able to use methods from GenericObject in GenericView, and methods from SpecializedObject in SpecializedView. Is there a way to precise that obj has to be a GenericObject in GenericView to be handled, and has to be a SpecializedObject to be dealt with in SpecializedView, without adding a property? How would you do that?
Objective-C is a dynamically-typed language and methods are resolved at runtime, not compile time. If in SpecializedView, obj is in fact of an object of type SpecializedObject (even though it's declared as GenericObject), it will in fact respond to a toastBread message. The compiler will generate a warning but you can ignore it.
If SpecializedView may have both GenericObjects and SpecializedObjects, you can make sure that obj responds to toastBread using the respondsToSelector: message (inherited from NSObject):
if ([obj respondsToSelector:#selector(toastBread)]) {
[obj toastBread];
}