Extending Objective-C objects using Swift

This is a short post to share something neat I thought about this week and wrote. We are working in a codebase which contains a mixture of Obj-C and Swift. We faced a code smell in a new View Controller we are building, which was facing down the barrel of a giant if/else combination.

In Zendesk our API is provided by a Rails application and as such enums are represented as strings when they are serialised in JSON. Looking at our Developer Portal where you can find about all things Zendesk Rest API. We see this example, ticket fields

{
"id": 34,
"url": "https://company.zendesk.com/api/v2/ticket_fields/34.json",
"type": "subject",
"title": "Subject",
"raw_title": "{{dc.my_title}}",
"description": "This is the subject field of a ticket",
"raw_description": "This is the subject field of a ticket",
"position": 21,
"active": true,
"required": true,
"collapsed_for_agents": false,
"regexp_for_validation": null,
"title_in_portal": "Subject",
"raw_title_in_portal": "{{dc.my_title_in_portal}}",
"visible_in_portal": true,
"editable_in_portal": true,
"required_in_portal": true,
"tag": null,
"created_at": "2009-07-20T22:55:29Z",
"updated_at": "2011-05-05T10:38:52Z",
"removable": false
}

The issue with this is that in Objective-C enums are essential typedef’ed as NSUInteger. What does that really mean in simple terms is that enums are Unsigned Integers and only Unsigned integers, converting to and from a JSON representation isn’t clean or trivial.

A naive representation of this in Objective-C would look something like this code block below, using an NSString to represent type.

@interface ZDPTicketField : NSObject
@property (nonatomic, readonly) NSString *title;
@property (nonatomic, readonly) NSString *type;
@property (nonatomic, readonly) NSString *fieldDescription;
@property (nonatomic, readonly) NSNumber *position;
@property (nonatomic, readonly) NSString *tag;
@property (nonatomic, readonly) NSString *regExpForValidation;
@property (nonatomic, readonly) NSArray<ZDPSystemFieldOption *> *systemFieldOptions;
@property (nonatomic, readonly) NSArray<ZDPCustomFieldOption *> *customFieldOptions;
@property (nonatomic, readonly) BOOL active;
@property (nonatomic, readonly) BOOL required;
@end

As you can see comparing strings is not going to be fun and it never is, it’s an enum in the backend and we should be able to offer something similar in our API client.

Enter Swift!

We are always looking to use Swift on a regular basis where and when it makes sense, this is typically brand new code and as we refactor out Obj-C. Before I hear a lot of people talking about performance and the cost of enums, they make consuming model responses from an API easier then I am in favour of it with that trade off.

We created the following which works well in Swift classes however currently the converted Swift back into Objective-C doesn’t support the this notation. It means this connivence is reserved to our Swift classes but as we migrate more and more code to Swift I think it’s an acceptable compromise.

extension ZDPTicketField {

enum TicketFieldType: String {
    case TicketForm = “ticket_form”,
TicketProblem = “ticket_problem”,
TicketIncidents = “ticket_incidents”,
Tag = “tag”,
Requester = “requester”,
CC = “cc”,
DueDate = “due_date”,
Subject = “subject”,
Description = “description”,
Comment = “comment”,
Status = “status”,
Type = “tickettype”,
BasicPriority = “basic_priority”,
Priority = “priority”,
Group = “group”,
Assignee = “assignee”,
Tagger = “tagger”,
Checkbox = “checkbox”,
Integer = “integer”,
Regex = “regexp”,
TextArea = “textarea”,
Decimal = “decimal”,
Text = “text”,
Date = “date”,
Brand = “brand”
}

var typeEnum: TicketFieldType! {
return TicketFieldType(rawValue: self.type!)
}

}

What this results is that in Swift we can now access this extended computed property in our code and it will look and feel like an original part of our model definition.

var currentTicketField = getSelectedTicketField()
if let type = currentTicketField.typeEnum {
    switch type {
        case .Subject:
        //Do something...
break
    }
}

I hope someone else finds it useful, I certainly had a little grin when it worked for me.

Show your support

Clapping shows how much you appreciated Alan Cooke’s story.