Optional Protocol Methods Without @objc

Yesterday I was watching a talk from Nikita Lutsenko about Swift and Objective-C Interoperability. At some point he states:

What Objective-C protocols are in Swift is very special. There is so much that was added specifically for it. It’s also weaker and not Swift-y, even though you can use optional. So, optional is not supported on Swift protocols, unless they are exported into Objective-C, because that time, they become Objective-C protocols. There is also no ability to talk to extensions of these protocols. Either they are concrete extensions, with no way you can talk to a protocol and build things like protocol extensions, which we all love and use in Swift. So they’re very limited altogether. If you actually are writing Swift code, please don’t use these, they make everyone’s life bad as well as they could not be used, say, in a Linux environment, where you don’t have Objective-C runtime.

He’s right! When you annotate a protocol with @objc, LLVM will generate a bunch of extra things: an isa pointer, runtime sections like: __objc_imageinfo, __objc_classref, etc. It seems that it’s casting the protocol into a NSObject, and maybe it will have a performance penalty. Also @objc protocols can not be used with struct and enums, just class types.

The following assembly is for @objc protocol:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
                                   ; 
                                   ; @protocol _TtP9Protocols8Protocol_ {
                                   ;     -method
                                   ; }
00000001002afcb0                                 dq         0x0                 ; isa, XREF=0x1002abaa0, 0x1002b03c8
00000001002afcb8                                 dq         0x1002782f0         ; name, "_TtP9Protocols8Protocol_"
00000001002afcc0                                 dq         0x0                 ; protocols
00000001002afcc8                                 dq         0x1002afd00         ; instance methods
00000001002afcd0                                 dq         0x0                 ; class methods
00000001002afcd8                                 dq         0x0                 ; optional instanceMethods
00000001002afce0                                 dq         0x0                 ; optional class methods
00000001002afce8                                 dq         0x0                 ; instance properties
00000001002afcf0                                 dd         0x00000050          ; size
00000001002afcf4                                 dd         0x00000001          ; flags
00000001002afcf8                                 db  0x20 ; ' '
00000001002afcf9                                 db  0xfd ; '.'
00000001002afcfa                                 db  0x2a ; '*'
00000001002afcfb                                 db  0x00 ; '.'
00000001002afcfc                                 db  0x01 ; '.'
00000001002afcfd                                 db  0x00 ; '.'
00000001002afcfe                                 db  0x00 ; '.'
00000001002afcff                                 db  0x00 ; '.'
00000001002afd00                                 db  0x18 ; '.'                 ; XREF=0x1002afcc8
00000001002afd01                                 db  0x00 ; '.'
00000001002afd02                                 db  0x00 ; '.'
00000001002afd03                                 db  0x00 ; '.'
00000001002afd04                                 db  0x01 ; '.'
00000001002afd05                                 db  0x00 ; '.'
00000001002afd06                                 db  0x00 ; '.'
00000001002afd07                                 db  0x00 ; '.'
00000001002afd08                                 db  0x0a ; '.'
00000001002afd09                                 db  0xf2 ; '.'
00000001002afd0a                                 db  0x26 ; '&'
00000001002afd0b                                 db  0x00 ; '.'
00000001002afd0c                                 db  0x01 ; '.'
00000001002afd0d                                 db  0x00 ; '.'
00000001002afd0e                                 db  0x00 ; '.'
00000001002afd0f                                 db  0x00 ; '.'
00000001002afd10                                 db  0xe0 ; '.'
00000001002afd11                                 db  0x82 ; '.'
00000001002afd12                                 db  0x27 ; '''
00000001002afd13                                 db  0x00 ; '.'
00000001002afd14                                 db  0x01 ; '.'
00000001002afd15                                 db  0x00 ; '.'
00000001002afd16                                 db  0x00 ; '.'
00000001002afd17                                 db  0x00 ; '.'
00000001002afd18                                 db  0x00 ; '.'
00000001002afd19                                 db  0x00 ; '.'
00000001002afd1a                                 db  0x00 ; '.'
00000001002afd1b                                 db  0x00 ; '.'
00000001002afd1c                                 db  0x00 ; '.'
00000001002afd1d                                 db  0x00 ; '.'
00000001002afd1e                                 db  0x00 ; '.'
00000001002afd1f                                 db  0x00 ; '.'
00000001002afd20                                 db  0xe0 ; '.'
00000001002afd21                                 db  0x82 ; '.'
00000001002afd22                                 db  0x27 ; '''
00000001002afd23                                 db  0x00 ; '.'
00000001002afd24                                 db  0x01 ; '.'
00000001002afd25                                 db  0x00 ; '.'
00000001002afd26                                 db  0x00 ; '.'
00000001002afd27                                 db  0x00 ; '.'
                                   ; 
                                   ; Section __objc_selrefs
                                   ; 
                                   ; Range 0x1002afd28 - 0x1002b0360 (1592 bytes)
                                   ; File offset 2817320 (1592 bytes)
                                   ; Flags : 0x10000005
                                   ; 
00000001002afd28                                 dq         0x10026daa6         ; @selector(hash), "hash", XREF=0x1000008e8, -[NSObject hashValue]+4, __TFE10ObjectiveCCSo8NSObjectg9hashValueSi+4, __TFE10FoundationSSg4hashSi+15, _swift_stdlib_NSStringNFDHashValue+27, _swift_stdlib_NSStringASCIIHashValue+10
00000001002afd30                                 dq         0x10026daab         ; @selector(isEqual:), "isEqual:", XREF=__TTWCSo8NSObjects9Equatable10ObjectiveCZFS0_oi2eefTxx_Sb+16, __TZF10ObjectiveCoi2eeFTCSo8NSObjectS0__Sb+16, _swift_stdlib_NSObject_isEqual+21
00000001002afd38                                 dq         0x10026dab4         ; @selector(hashValue), "hashValue", XREF=__TTWCSo8NSObjects8Hashable10ObjectiveCFS0_g9hashValueSi+7
00000001002afd40                                 dq         0x10026dabe         ; @selector(description), "description", XREF=__TTWC
; assembly code goes by

But when we declare just protocol, here’s what you end-up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
0000000100276b90                                 db         "_TtP9Protocols8Protocol_", 0
                                       ; 
                                       ; Section __objc_methname
                                       ; 
                                       ; Range 0x100276ba9 - 0x10027830d (5988 bytes)
                                       ; File offset 2583465 (5988 bytes)
                                       ; Flags : 0x00000002
                                       ; 
0000000100276ba9                                 db         "hash", 0           ; XREF=0x100000210, 0x1002afcf0
0000000100276bae                                 db         "isEqual:", 0       ; XREF=0x1002afcf8
0000000100276bb7                                 db         "hashValue", 0      ; XREF=0x1002afd00
0000000100276bc1                                 db         "description", 0    ; XREF=0x1002afd08
; assembly code goes by

Much more assembly, right?

So, let’s think this thoroughly, shall we? optional means that a function may or may not exist. In Objective-C, before passing a message, we have to check for respondsToSelector, otherwise it will crash at runtime. In Swift, we just call the function, followed by a ?, all safe, no crashes.

But I think that there’s a more Swifty way of doing that, by using protocol extensions, take a look:

1
2
3
4
5
6
7
8
9
protocol Protocol: class {
    func requiredMethodOne()
    func requiredMethodTwo()
}

extension Protocol {
    func optionalMethodOne() {}
    func optionalMethodTwo() {}
}

Now, if someone calls optionalMethodOne(), nothing happens, due to it’s empty implementation. We also mitigate the risk of someone, accidentally, forcing unwrap an optional functional. Not to mention the fact that, now, it’s not constraint to a specific type!

Protocol extende all the things!

Wrapping API's Using the Builder Pattern

The way I was introduced to the Design Patterns lead me to think that those clever and neat solutions were meant to be used just in big softwares solutions. I never considered using them into the small pieces of software. What do I mean by that? Please, read on.

The Builder Pattern if defined as follows:

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

@noescape Attribute

Swift 1.2 introduced us with @noescape attribute. It’s a very important feature, when we want to make our code more cleaner and stricter. Using it properly at 3am will prevent many unwanted retain cycles.

While digging into release notes we can see a bunch of clever words:

A new @noescape attribute may be used on closure parameters to functions. This indicates that the parameter is only ever called (or passed as an @noescape parameter in a call), which means that it cannot outlive the lifetime of the call. This enables some minor performance optimizations, but more importantly disables the self. requirement in closure arguments.

Working With CFunction Pointers in Swift

Swift like objC allow us to mix it with other languages, unfotunately when it comes to Swift we can only choose between our good old friend objC or ANIS C, as there is still lack of C++ support. Basically using function pointers allows us to call C functions inside Swift. Swift will automatically convert methods included in Bridge Header into Swift equivalents:

Bluetooth Low Energy the Fun Way

Today Bluetooth Low Energy can be found in many cool applications, it can be used from simple data exchange to payment terminals and the more popular usage with iBeacons. But what if we want to build something funny with it? Like some simple game not even realtime, it may be even turn based game. Imagine you do not need to go through this long setup, waiting for server players to be ready etc.

Everyone knows that building good multiplayer game is hard, multiplayer itself is hard… But here I want to show you my small proof of concept of working bluetooth low enery multiplayer game.

Easy Cast With _ObjectiveCBridgeable

Swift is out there for about a year and it’s a great programming language. I think that almost every iOS/OSX developer out there has already written couple of things in Swift (if you haven’t, go ahead and try, you won’t regret it, I promise). Although, we have many years of libs and frameworks built using Objective-C and sooner or later a project may have both Swift and Objective-C working together.

UITraitCollection Trick

Gone are the days where there was just one iPhone for developers as a target. Now we have to support multiple devices with different screen sizes. Fortunately, we have autolayout, which solves a part of this design equation, the other part is solved with UITraitCollection. Trait collection object has two size classes: horizontal and vertical. Each of these classes has three possible values: compact, regular or any. The current device+orientation can be described as a combination of the sizes.

Logging Excessive Blocks on the Main Thread

Logging excessive blocks on the main thread

Having an application running at 60 FPS is every programmers dream, and users delight. The worst users experience ever is a frozen and unresponsive screen. It’s a dreadful crime in mobile world nowadays. Users try to interact at any moment and according to Murphy’s law they will find all your mistakes. So, you better keep the main thread slim.

Autolayout and NSLocalizedString

A localized application is the one that has all of its texts, translated into the users device current language. And this, for us developers, means one thing, and one thing only. Nightmare.

Every time a new translation comes, it’s necessary to run the application and check for broken layouts. Take this quite simple UI.

Don't Forget About UIMottionEffect

UIMotionEffects was first introduced in iOS 7. The WWDC session which presented this, amongst other cool things, is named Implementing Engaging UI on iOS. Nevertheless, UIMotionEffects is still overlooked. But not today, let’s make something cool with it.

Motion effects is an easy way to react to external variations on the device’s orientation. To say, UIKit performs UI changes whenever the user tilts the device, vertically or horizontally.

Let’s use UIInterpolatingMotionEffect a subclass of UIMotionEffects, with MapKit. Notice how appealing it is.