Cannot access a bridged NSArray from Swift - objective-c

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.

Related

Is it safe to ignore compiler warning caused by referring to dispatch_queue_t from Swift?

I have an Objective C class which has the following property:
#property (nonatomic, strong, readonly) dispatch_queue_t couchDispatchQueue;
I have a Swift extension of that class where I reference that property like so:
couchDispatchQueue.async {
When I do that, I get the following compiler warning:
Property type 'OS_dispatch_queue * _Nullable' is incompatible with type 'dispatch_queue_t _Nullable' (aka 'NSObject *') inherited from 'BZCouchDatabase'
I can see why, since my app's generated App-Swift.h file has:
#property (nonatomic, readonly, strong) OS_dispatch_queue * _Nullable couchDispatchQueue;
while dispatch_queue_tis defined as:
typedef NSObject<OS_dispatch_queue> *dispatch_queue_t;
Edit
I've figured out the "further complication" that I was missing in my original description. This property is required by an Objective C protocol which also requires inheritance from NSObject. As soon as I make the Swift class inherit from NSObject and conform to the objective C protocol, I get the warning. The following sample code is enough to set off the warning:
Objective C:
#protocol Thingness
#property (nonatomic, strong, readonly, nullable) dispatch_queue_t couchDispatchQueue;
#end
Swift:
class Thing: NSObject, Thingness {
var couchDispatchQueue: DispatchQueue?
}
My question still is: is it safe for me to just silence this warning? Should I file a radar?
Very well described situation — but I can't reproduce it. When I declare
#property (nonatomic, strong, readonly) dispatch_queue_t couchDispatchQueue;
in an Objective-C class file, the generated header shows me
open var couchDispatchQueue: DispatchQueue! { get }
This is thus seen as a normal Swift 3 DispatchQueue, and my call to couchDispatchQueue.async produces no warning at all.
To be clear, I tried it two ways. I declared the couchDispatchQueue property in the .h file for a Thing class. I imported Thing.h into Swift. I then wrote this code in Swift:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Thing().couchDispatchQueue.async {}
}
}
extension Thing {
func test() {
self.couchDispatchQueue.async {}
}
}
Neither in the straight instance method call nor in the extension do I see any warning.

Working with Swift Arrays - How to implement an Array of Objects conforming to a protocol

In Objective-C I am often using the following "design pattern" in my code to "broadcast" the scenes input callbacks:
#protocol RPSceneDelegate <NSObject>
-(void)someMethod;
-(void)didTap;
#end
#interface RPScene : SKScene
#property (nonatomic, strong) NSMutableArray<id<RPSceneDelegate>>* delegates;
-(void)addDelegate:(id<RPSceneDelegate>)delegate_;
-(void)removeDelegate:(id<RPSceneDelegate>)delegate_;
#end
#implementation RPScene
#pragma mark - Delegate Handling
-(void)addDelegate:(id<RPSceneDelegate>)delegate_ {
if ([self.delegates containsObject:delegate_]) return;
[self.delegates addObject:delegate_];
}
-(void)removeDelegate:(id<RPSceneDelegate>)delegate_ {
if (![self.delegates containsObject:delegate_]) return;
[self.delegates removeObject:delegate_];
}
-(void)didTap {
for (id<RPSceneDelegate> delegate_ in self.delegates) {
// Just a small example to show what I am doing
[delegate_ performSelector:#selector(didTap)];
}
}
#end
Last night I tried to convert this to swift using Swift-Arrays (not NSMutableArray) but I ended up totally frustrating when checking:
array.contains(theObject)
To get a better understanding of Swift it would help me if someone could convert that little piece of code to Swift. So how do I implement that in Swift!?
EDIT: My code in Swift
protocol RPSceneDelegate {
func someMethod()
}
class RPScene: SKScene {
var delegates = [RPSceneDelegate]()
func addDelegate(delegate: RPSceneDelegate) {
if !delegates.contains(delegate) {
delegates.append(delegate)
}
}
}
What works fine is:
delegates.append(delegate)
What gives me an error is:
delegates.contains(delegate)
The Compiler says:
Cannot convert value of type 'RPSceneDelegate' to expected argument
type '#noescape (RPSceneDelegate) throws -> Bool'
Problem is. I just don't know what that means ... My first idea was that .contains() requires a function, not an instance / object but I have no clue how to implement that...
It's not a helpful error message...
What is means is that in order to check if an array contains something the compiler needs to be able to compare them. At the moment the objects only respond to a simple protocol so the compiler has no idea how to compare them.
Your protocol needs to extend Equatable, and depending on exactly what your objects conforming to this protocol are you may need to implement the equality function.

Swift 1.2 error on Objective-C protocol using getter

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;
}
}
}

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.

Using a Swift protocol in Objective-C

I'm working on converting my project from Objective-c to Swift, and a Swift class I'm using, I have a protocol I'm trying to access in an Objective-c class. My problem is, the delegate is not accessible in the objective-c class. Here is my swift class:
protocol RateButtonDelegate {
func rateButtonPressed(rating: Int)
}
class RateButtonView: UIView {
var delegate: RateButtonDelegate?
var divider1: UIView!
var divider2: UIView!
}
When I look at the MyProject-Swift.h file, I don't see the delegate:
#interface RateButtonViewTest : UIView
#property (nonatomic) UIView * divider1;
#property (nonatomic) UIView * divider2;
#end
and when I try to use rateButtonView.delegate in my Objective-c class, I get a compiler error.
Anyone know the issue? How do access a Swift protocol in Objective-c?
You need to mark your protocol with the #objc attribute for it to be accessible from Objective-C:
#objc protocol RateButtonDelegate {
func rateButtonPressed(rating: Int)
}
This page from Apple's documentation covers the issue.