├── readme.md └── swift_tips.md /readme.md: -------------------------------------------------------------------------------- 1 | Swift Tips 2 | ---------- 3 | 4 | These are tips we at [Hopscotch](http://gethopscotch.com) have learned from using Swift in a mostly Objective C project. We think these tips are good but we're still learning. Find a mistake? Make a Pull Request and help us learn better! 5 | 6 | [Show me the tips!](https://github.com/jbrennan/swift-tips/blob/master/swift_tips.md) 7 | ===================================================================================== 8 | -------------------------------------------------------------------------------- /swift_tips.md: -------------------------------------------------------------------------------- 1 | Swift Tips 2 | ========== 3 | 4 | Here are some tips for programming in Swift in Objective C projects. Please update this as you learn new things! These are in no particular order... 5 | 6 | Handy Links 7 | ----------- 8 | 9 | - [Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097) 10 | - [Using Swift with Objective C](https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216-CH2-XID_0) 11 | - [FAQ on Swift](http://www.raywenderlich.com/74138/swift-language-faq) 12 | 13 | Using Objective C code in Swift 14 | ------------------------------- 15 | 16 | In order to use an Objective C class or file in Swift you must expose the class in the "Swift Bridging header" file, which is called `Hopscotch-Bridging-Header.h`. Anything you import in there will be exposed to Swift code. 17 | 18 | ### Casting in for-loops 19 | 20 | If you have an `NSArray` and you want to loop over elements in Swift, you can use the following pattern: 21 | 22 | ``` 23 | for aView in foundationArray as [UIView] { 24 | // aView is of type UIView 25 | } 26 | ``` 27 | 28 | ### Objective C enums in Swift 29 | 30 | If you have an Objective C enum like the following: 31 | 32 | ``` 33 | typedef NS_ENUM(NSUInteger, JBMyEnumType) { 34 | JBMyEnumTypeFirst, 35 | JBMyEnumTypeSecond 36 | }; 37 | ``` 38 | 39 | When you use this enum in Swift, the values will lose their two letter prefix (e.g., "JB"), so the first value becomes `JBNyEnumType.MyEnumTypeFirst`. 40 | 41 | ### Gesture Recognizers and Target/Action pairs 42 | 43 | In Objective C you can set up a target action pair in different ways, depending on the parameters you want. For example: 44 | 45 | ``` 46 | UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan)]; // expects a method named `- (void)pan` 47 | UIPanGestureRecognizer *otherPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panWithGesture:)]; // expects a method named `- (void)panWithGesture:(UIPanGestureRecognizer)gesture` 48 | ``` 49 | 50 | In Swift, if you want to speficy the action method taking a parameter, you **also** need to include the colon, even though Swift doesn't use colons in method signatures like Objective C: 51 | 52 | ``` 53 | let pan = UIPanGestureRecognizer(target: self, action: "panWithGesture:") // expects a method named `func panWithGesture(gesture: UIPanGestureRecognizer)` 54 | ``` 55 | 56 | Using Swift code in Objective C 57 | ------------------------------- 58 | 59 | ### Importing Swift code 60 | 61 | To import Swift symbols you must import the Swift header. In our project this is called `Hopscotch-Swift.h`. Import that wherever you need to use it. 62 | 63 | At first glance it looks like we can't just import this in our `pch` file because then our `Constants.h` file can't find things like CGFloat. I'm sure there's a way to fix this, we should look into it. We should also consider breaking the constants file up more. 64 | 65 | ### Protocols 66 | 67 | #### NSObject 68 | 69 | Swift protocols you create that conform to `` must instead conform to `NSObjectProtocol` because in Swift protocol names must be different than class names (I think). So your protocol would look like: 70 | 71 | 72 | ``` 73 | protocol MyProtocol: NSObjectProtocol { 74 | ... 75 | } 76 | ``` 77 | 78 | #### Adopting Swift protocols in Objective C 79 | 80 | To make your Objective C class conform to a Swift protocol, you must first make sure you import the Swift header (mentioned above) and you must also make sure your protocol has the `@objc` keyword in front of it. 81 | 82 | ``` 83 | /** MyProtocol.swift */ 84 | @objc protocol MyProtocol: NSObjectProtocol { 85 | ... 86 | } 87 | 88 | /** MyConformingClass.h */ 89 | #import "Hopscotch-Swift.h" 90 | @interface MyConformingClass: NSObject 91 | ... 92 | @end 93 | ``` 94 | 95 | Pragma mark 96 | ----------- 97 | 98 | To do a pragma mark you use the following syntax 99 | 100 | ``` 101 | // MARK: - Awesome bit of methods 102 | ``` 103 | 104 | 105 | NSLocalizedStrings 106 | ------------------ 107 | 108 | Apparently the localized string macros have been replaced with one function: 109 | 110 | ``` 111 | NSLocalizedString(key: String, tableName: String?, bundle: NSBundle, value: String, comment: String) 112 | ``` 113 | 114 | Where you can omit table, bundle, and value (they have default values) and just use something like: 115 | 116 | ``` 117 | NSLocalizedString("Hello", comment: "standard greeting") 118 | ``` 119 | 120 | 121 | Initializers 122 | ------------ 123 | 124 | ### "With" initializers 125 | 126 | Swift seems to automatically convert "with" initializers to its `init()` syntax. This is expected for things like `initWithFrame:` which becomes `init(frame:)` but it *also* seems to happen for things like `+ buttonWithSuperview:` which becomes `init(superview:)`. 127 | 128 | 129 | Closures 130 | -------- 131 | 132 | ### Retain cycles 133 | 134 | Swift didn't solve the problem of strongly capturing `self` in closures. That sucks. What you can do is provide a "capture list" of values in the closure and explicitly declare self as a weak or unowned reference. You do the following: 135 | 136 | ``` 137 | var closure = { 138 | [weak self] in 139 | self?.doSomething() 140 | self?.doSomethingElse() 141 | } 142 | ``` 143 | 144 | When you use `weak` that makes `self` an Optional, which means it could go away and be nil by the time the closure executes. So, you have to either use the unwrapped optional (i.e., `ronBurgundy?`) or "let it out" by using the if-let syntax (`if let strongSelf = self { ... }`) 145 | 146 | You can use `unowned` instead of `weak` to avoid the "optional" issue, but only do so if you know for sure that the value will still exist. If you try to reference it and it's nil, the app will crash. This seems less safe, and we should probably generally use `weak` until we understand the memory issues better. 147 | 148 | ### Return values 149 | 150 | [From the docs](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html): 151 | 152 | >Single statement closures implicitly return the value of their only statement. 153 | 154 | Which means something like 155 | 156 | ``` 157 | button.tapAction = { 158 | [weak self] in 159 | self?.animateOutWithCompletion(nil) 160 | } 161 | ``` 162 | 163 | Won't compile. I think this is because `self` is `weak`, which means it's an optional type. So, it's possible that `self?.whatever()` won't execute because `self` could be nil. So the return type can't be verified. 164 | 165 | 166 | Protocols 167 | --------- 168 | 169 | ### Delegates 170 | 171 | To make a Swift protocol that represents a delegate, you should declare the protocol as a "class" type: 172 | 173 | ``` 174 | protocol MyDelegate : class { 175 | // methods/properties 176 | } 177 | ``` 178 | 179 | This means the protocol can only be conformed to by reference types (e.g., a `struct` can't conform to it). The reason why you should do this is so you can make the delegate property be `weak` so retain cycles can be avoided. 180 | 181 | ``` 182 | class MyClass { 183 | weak var delegate : MyDelegate? // weak properties must be Optionals 184 | } 185 | ``` 186 | 187 | Enums 188 | ----- 189 | 190 | ### Naming enum cases 191 | 192 | It appears you can't name an enum case to be called `Type`. My guess is this is some kind of existing known member already in Swift objects (I'd love to have a docs link for this!). Come up with a better name. 193 | 194 | ``` 195 | enum MyEnum { 196 | case Type // Will compile but not really usable. 197 | case MyType // Just peachy. 198 | } 199 | ``` 200 | --------------------------------------------------------------------------------