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

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.

Related

Making existing Objective C class conform to swift protocol via extensions

I have 2 versions of the same model in the project (and I can't get rid of the legacy one). It is Customer (legacy code) and struct CustomerModel - modern Swift implementation of the model.
I have a custom UITableViewCell which used to have setup(withCustomer: CustomerModel) method. It worked well for a new model, but now I need to use legacy one to setup same cells.
I decided to define CustomerDisplayable protocol and make both models conform it.
Here is the code:
Customer.h
#interface Customer : NSObject
#property (nonatomic, strong) NSString* name;
#property (nonatomic, strong) NSString* details;
#end
CustomerModel.swift
struct CustomerModel {
let name: String
let details: String?
init(withJSON json: [String: Any]) {
name = json["name"] as! String
details = json["details"] as? String
}
}
CustomerDisplayable.swift
protocol CustomerDisplayable {
var name: String { get }
var details: String? { get }
var reviewCount: Int { get }
var reviewRating: Double { get }
}
extension Customer: CustomerDisplayable {
var reviewCount: Int { return 100 }
var reviewRating: Double { return 4.5 }
}
extension CustomerModel: CustomerDisplayable {
var reviewCount: Int { return 100 }
var reviewRating: Double { return 4.5 }
}
I expected that as Customer.h has already properties name & details - it will conform this protocol and extension above will work. But I get a compiling error in my extension:
Type 'Customer' does not conform to protocol 'CustomerDisplayable'.
Xcode offers a quick fix - Protocol requires property 'name' with type 'String'; do you want to add a stub.
If I agree Xcode add stubs I end up with name and details computable getters but Xcode shows new compile errors:
extension Customer: CustomerDisplayable {
var details: String? {
return "test"
}
var name: String {
return "test"
}
var reviewCount: Int { return 100 }
var reviewRating: Double { return 4.5 }
}
'details' used within its own type
Getter for 'name' with Objective-C selector 'name' conflicts with the previous declaration with the same Objective-C selector
Any ideas how to solve this problem? I really want to have this protocol and abstract interface for both model representations.
The only solution I came to is to rename properties in CustomerDisplayable
NOTE: Real models are much more complex, but this code is demonstrating the problem.
Hmm, this looks, IMO, like something that probably ought to be considered a bug in the Swift compiler. I'd suggest filing a report at http://bugs.swift.org .
Here's what appears to be going on:
As you've noticed, Swift doesn't seem to notice when an Objective-C selector fulfills a retroactive protocol requirement, which is the part I'd probably file as a bug.
When you explicitly try to add name and details properties to your extension, Swift 3 notices that the extension is on an NSObject subclass and automatically exposes the properties to Objective-C. Objective-C, of course, can't have two methods with the same selector, so you get the error you've been seeing. Swift 4 doesn't automatically expose everything to Objective-C anymore, so you won't get this error there, and in Swift 3 you can work around this by adding the #nonobjc keyword. But then:
Once you do add the property in an extension, it shadows the original Objective-C property, making it hard to get at the correct value to return in the property.
Unfortunately, I can't think of a clean workaround, although I can think of an ugly, hacky one involving the Objective-C runtime. My favorite part is the way we have to use string-based NSSelectorFromString since #selector will choke from the presence of the #nonobjc shadowed property. But it works:
extension Customer: CustomerDisplayable {
#nonobjc var details: String? {
return self.perform(NSSelectorFromString("details")).takeUnretainedValue() as? String
}
#nonobjc var name: String {
return self.perform(NSSelectorFromString("name")).takeUnretainedValue() as? String ?? ""
}
}
Again, I'd recommend filing a bug report so that we don't have to do crap like this in the future.
EDIT: Never mind all this! I'm wrong. Disregard everything I said. The only reason the retroactive protocol didn't work was because you didn't have nullability specifiers in your Objective-C class, so Swift didn't know whether they could be nil or not and thus interpreted types as String!. Should have noticed that, d'oh d'oh d'oh. Anyway, I don't know if you're able to edit the original Objective-C class's definition to add nullability specifiers, but if you can, the original protocol will work fine retroactively with no hacks.
#interface Customer : NSObject
#property (nonatomic, nonnull, strong) NSString* name;
#property (nonatomic, nullable, strong) NSString* details;
- (nonnull instancetype)initWithName:(nonnull NSString *)name details: (nonnull NSString *)details;
#end
NSString in Objective-C is different than the native Swift value type String. Conversion rules I find are too obscure to memorize. In this case it appears the bridging brings in NSString * as: ImplicitlyUnwrappedOptional<String>
So if you don't mind the legacy Customer type dictating some aspects of your Swift code, change types of name and details to String! everywhere and all is well.
Otherwise, you'll have to change your protocol to have new names, say displayName and displayDetails, and reference the underlying properties. Presumably you have the freedom to do this and you achieve what is perhaps an important abstraction layer anyway. Then just dutifully list all the obvious implementations of all four properties in each extension block. Because the conversion from String! to String or String? is automatic, the code looks a bit trivial and bloated, but it also works fine.

Crash when calling Obj-C property from Swift extension

Using this Objective-C property:
#interface TSOnboardingPersonalizeViewController
#property (nonatomic, weak) NSObject<PersonalizeContentCoordinatorDelegate> * _Nullable delegate;
#end
crashes in a Swift extension like this:
extension TSOnboardingPersonalizeViewController {
func next() {
self.delegate?.performAction(.Forward)
}
}
POing self.delegate shows:
(lldb) po self.delegate
▿ Optional<protocol<PersonalizeContentCoordinatorDelegate>>
▿ Some : <MyApp.PersonalizeContentCoordinator: 0x8e964cf58140>
But it doesn't crash if I cast the property to the protocol type:
extension TSOnboardingPersonalizeViewController {
func next() {
if let delegate = self.delegate as? PersonalizeContentCoordinatorDelegate {
delegate.performAction(.Forward)
}
}
}
POing delegate shows:
(lldb) po delegate
<MyApp.PersonalizeContentCoordinator: 0x7f86bcf59930>
Why do I need to explicity cast the property to the PersonalizeContentCoordinatorDelegate protocol type?
I'm using Swift 2.2 and Xcode 7.3
I think it could be due to the fact that the delegate is an NSObject, which probably doesn't have a performAction method. So, even though the delegate conforms to the protocol in Objective-C, Swift probably sees an NSObject (which doesn't include that method).
This seems like a "Lost in Translation" sort of thing, where Objective-C is telling Swift "This object conforms to this protocol." Then when executed, Swift tries calling that method on an NSObject which obviously doesn't include that method and crashes. This would explain why casting would work, because until you tell Swift you have an object of a certain type, it can't execute any of that type's specific methods (even if the underlying object actually is that type).
This is just a guess, though and should definitely be taken with a grain of salt (and maybe a better answer if one comes along).

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.

Is there a way to wrap protocol buffers into Objective-C and still take advantage of inheritance?

I want to use Protocol Buffers in an iOS project. I'm trying to avoid making the whole project into an Objective-C++ fiasco, so I want to wrap the C++ protobuf classes into Objective-C ones. I have several dozen protobuf messages, and while I have done this successfully one class at a time, ideally I would like to use inheritance to minimize the repeated code. I'm new to Objective-C and I haven't used what little I knew of C++ in 10 years, so this has mostly been an exercise in frustration. Below is an example of how I have wrapped a single message.
Code
.proto:
message MessageA {
optional string value = 1;
}
MessageAWrapper.h:
#import <Foundation/Foundation.h>
#interface MessageAWrapper : NSObject
#property (nonatomic) NSString *value;
+ (id)fromString:(NSString *)string;
- (NSString *)serialize;
#end
MessageAWrapper.mm:
#import "MessageA.h"
#import "message.pb.h"
#interface MessageAWrapper ()
#property (nonatomic) MessageA *message;
#end
#implementation MessageAWrapper
- (id)init
{
self = [super init];
if (self) {
self.message = new MessageA();
}
return self;
}
- (void)dealloc {
delete self.message;
self.message = NULL;
}
- (NSString *)value {
return [NSString stringWithUTF8String:self.message->value().c_str()];
}
- (void)setValue:(NSString *)value {
self.message->set_value([value UTF8String]);
}
- (NSString *)serialize {
std::string output;
self.message->SerializeToString(&output);
return [NSString stringWithUTF8String:output.c_str()];
}
+ (id)fromString:(NSString *)string {
MessageA *message = new MessageA();
message->ParseFromString([string UTF8String]);
MessageAWrapper *wrapper = [[MessageAWrapper alloc] init];
wrapper.message = message;
return wrapper;
}
#end
Goal
There is a lot of code here that will be repeated dozens of times in which the only variation is the wrapped class type (init, dealloc, serialize, fromString), so ideally I would like to put it on a parent ProtobufMesssage class instead. Unfortunately I've had no success in making this work because I can't find a way for the parent class to know the class its children are using, which is required for example in init and fromString.
Things I've attempted
struct
template class
void*
Obstacles I've encountered
can't find a way to store a reference to a class/type
can't have any C++ headers or code in the .h file (as this requires the whole project to be Objective-C++)
difficulty keeping references to the protobuf message parents (Message or MessageLite) because they are abstract
As I said I have very little understanding of C++ or Objective-C; most of my experience is with much higher level languages like Python and Java (though I do mostly understand basic C things like pointers).
Is this perhaps not even possible? Am I approaching it wrong or missing something obvious? Any help would be much appreciated. Thanks.
I don't know much about C++ at all, but can't you declare the Objective-C property to be a Message *?
You've already separated the C++ code from the header by declaring the property in the .mm file, the problem you will have is with instance methods named by the compiler (value() and set_value()) and only being valid methods for the subclass. It might help to use the Reflection class to get and set fields by their name. Here is an excerpt from Google's message.h showing this:
Message* foo = new Foo;
const Descriptor* descriptor = foo->GetDescriptor();
const FieldDescriptor* text_field = descriptor->FindFieldByName("text");
assert(text_field != NULL);
assert(text_field->type() == FieldDescriptor::TYPE_STRING);
assert(text_field->label() == FieldDescriptor::LABEL_OPTIONAL);
const Reflection* reflection = foo->GetReflection();
assert(reflection->GetString(foo, text_field) == "Hello World!");
You could create Objective-C -objectForKey: and -setObject:forKey: instance methods that typecheck and get or set the value (confusingly, the key in the case of MessageAWrapper would be #"value"). Your subclasses would not even need to be aware of the C++ code.
You can also separate the creator function in -init and +fromString: method into something like, +_createNewInstance;
+(Message*)_createNewInstance{ return new MessageA(); }
allowing your subclasses of MessageWrapper to reuse all code except for creating the C++ object.
While Objective C has very powerful instrospection capabilities, C++ is more limited. You do have RTTI (Run time type information), but it's not even as powerful as the Objective C counterpart.
However, it might be enough for you. Within your Objective C++ class, you might find the type of you message object with the typeid operator:
if( (typeid(self.message) == typed(foo)){
//doSomething
else if( (typeid(self.message) == typed(bar)){
// doSomething else
}
Maybe the best option is to add another indirection level. Make an Objective C class hierarchy that wraps all your protocol buffer C++ classes and then create another Objective C that uses those classes (as delegates maybe). I believe this might be a better option. Use C++ only for those unavoidable cases.
Good luck!

What's the most robust and readable way of ensuring objects conform to a interface/protocol in Objective C?

I'm trying code to an interface (or a protocol in Objective C terminology), not an implementation.
It's critical that we check objects conform to protocol before calling methods on them to prevent crashes.
Three Ways
In compiler
At runtime
Both
Best Solution... Surely Number 1?
I thought the best way would be in the compiler:
Warnings ahoy if you screw up
Eliminates conformsToProtocol:/respondsToSelector: boilerplate
At runtime it's too late if you made a mistake - the best you can do is not execute the code/show an error
But I see a lot of code that's doing it at runtime. Why?
Is it a readability issue - needing id <Protocol> everywhere?
My Question
What's the most robust and readable way of ensuring objects conform to a interface/protocol?
Code
1. Checking In Compiler
#interface ReportController : NSObject {
id <ReportGenerator> generator;
id <ReportSender> sender;
id report;
}
#implementation ReportController
-(id)initWithReportGenerator:(id <ReportGenerator>)generator_
reportSender:(id <ReportSender>)sender_ {
// Usual init stuff
generator = generator_;
sender = sender_;
return self;
}
-(void)generateAndSend {
report = [generator generate];
[sender sendReport:report];
}
#end
2. Checking At Runtime
#interface ReportController : NSObject {
id generator;
id sender;
id report;
}
#implementation ReportController
-(id)initWithReportGenerator:(id)generator_
reportSender:(id)sender_ {
// Usual init stuff
generator = generator_;
sender = sender_;
return self;
}
-(void)generateAndSend {
if ([generator conformsToProtocol:#protocol(ReportGenerator)] &&
[sender conformsToProtocol:#protocol(ReportSender)]) {
report = [generator generate];
[sender sendReport:report];
} else {
[NSException raise:NSInternalInconsistencyException format:#"Objects didn't respond to protocols..."];
}
}
#end
You should use both. Consider e.g.:
#protocol Proto
- (void)someFunction;
#end
#interface C : NSObject
- (void)proto:(id<Proto>)p;
#end
// ...
NSString *s = #"moo";
id i = s;
C *c = [[C alloc] init];
[c proto:s]; // warns
[c proto:i]; // doesn't warn
Objective-C and Cocoa are too dynamic to generally check such things at compile time (NSProxy standins, classes dynamically adding methods and protocols, ...).
It is nice to catch as many of such errors at compile-time as possible, but that alone is not sufficient.
As long as you don't use plain id as the type, the compiler will at least warn you if you make a mistake at compile time. So you should be fine with your code example #1.
Of course, sometimes you might be forced to work with an id object that you get from a subsystem that is not under your control. In such cases you can cast the object back to the type you think it has (e.g. id <ReportGenerator>), but you are usually better off if you perform a runtime check first. Better be safe than sorry...
On a final note: If your protocol has optional parts (declared with the #optional keyword), then for those parts you will obviously be able to do runtime checks only. The #required keyword mentioned by apurv is necessary only if you want to be explicit in your protocol declaration (a protocol's parts are required by default), or if you mix optional and required parts.
You should create methods with #required type in protocol.
So whatever class wants to take a contract with this protocol, will must have to implement those methods.
It will definitely make sure that the required methods are available at compile time only.