├── 3b-Classes.playground ├── contents.xcplayground └── Contents.swift ├── 2b-Safety.playground ├── contents.xcplayground └── Contents.swift ├── 3a-Classes.playground ├── contents.xcplayground └── Contents.swift ├── Intermediate-Swift.playground ├── contents.xcplayground └── Contents.swift ├── 1-Playgrounds.playground ├── contents.xcplayground └── Contents.swift ├── 2a-Safety.playground ├── contents.xcplayground └── Contents.swift ├── LICENSE ├── README.markdown └── .gitignore /3b-Classes.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /2b-Safety.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /3a-Classes.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Intermediate-Swift.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /1-Playgrounds.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /2a-Safety.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Greg Heo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /3a-Classes.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | ### iOSDevUK 2016 — Intermediate Swift 3 | 4 | This is the completed playground from part 3a on Classes, covering Core Graphics. 5 | */ 6 | import UIKit 7 | 8 | // Slightly better way of drawing, taking advantage of CGContext as a class 9 | 10 | let bounds = CGRect(x: 0, y: 0, width: 300, height: 200) 11 | UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0) 12 | 13 | let path = UIBezierPath(ovalIn: bounds) 14 | 15 | let context = UIGraphicsGetCurrentContext()! 16 | context.setFillColor(UIColor.cyan.cgColor) 17 | context.addPath(path.cgPath) 18 | context.drawPath(using: .fill) 19 | 20 | if let contextImage = context.makeImage() { 21 | let uiImage = UIImage(cgImage: contextImage) 22 | } 23 | 24 | UIGraphicsEndImageContext() 25 | 26 | 27 | // The even better classy all-class way 28 | 29 | let renderer = UIGraphicsImageRenderer(size: bounds.size) 30 | let renderedImage = renderer.image { (renderContext) in 31 | let context = renderContext.cgContext 32 | 33 | context.setFillColor(UIColor.magenta.cgColor) 34 | context.addPath(path.cgPath) 35 | context.drawPath(using: .fill) 36 | } 37 | 38 | renderedImage 39 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # iOSDevUK 2016 — Intermediate Swift 2 | 3 | Hello, and welcome to the repository for the workshop materials for _Intermediate Swift_. 4 | 5 | You’ll find the following playgrounds here: 6 | 7 | * **1-Playgrounds**: Completed playground from part 1 of the workshop covering playgrounds. This is also the starter playground for part 2. 8 | 9 | * **2a-Safety**: Completed playground from part 2 of the workshop covering safety on formerly stringly-typed parameters. 10 | 11 | * **2b-Safety**: Completed playground from part 2 of the workshop covering closure capture. 12 | 13 | * **3a-Classes**: Completed playground from part 3 of the workshop covering the class-based Core Graphics API. 14 | 15 | * **3b-Classes**: Completed playground from part 3 of the workshop covering the class-based Grand Central Dispatch API. 16 | 17 | * **Intermediate-Swift**: A playground with notes and code mixed together summarizing what was covered in the workshop. Makes a good memory refresher for the future! 18 | 19 | If you have any questions or feedback or corrections, please get in touch! You can reach me on Twitter as [@gregheo](https://twitter.com/gregheo) or by email at [greg@gregheo.com](mailto:greg@gregheo.com). 20 | 21 | -------------------------------------------------------------------------------- /2a-Safety.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | ### iOSDevUK 2016 — Intermediate Swift 3 | 4 | This is the completed playground from part 2a on Safety. 5 | */ 6 | import UIKit 7 | import PlaygroundSupport 8 | 9 | class PlaygroundViewController: UIViewController { 10 | var button: UIButton! 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | self.view.backgroundColor = UIColor.red 16 | 17 | button = UIButton(type: .roundedRect) 18 | 19 | // #selector! 20 | button.addTarget(self, action: #selector(PlaygroundViewController.buttonTapped(sender:)), for: .touchUpInside) 21 | 22 | button.setTitle("Hello iOSDevUK", for: .normal) 23 | button.setTitleColor(.white, for: .normal) 24 | button.center = CGPoint(x: 50, y: 50) 25 | button.sizeToFit() 26 | self.view.addSubview(button) 27 | } 28 | 29 | func buttonTapped(sender: UIButton) { 30 | print("button tapped!") 31 | self.view.backgroundColor = .brown 32 | 33 | // #keyPath! 34 | let kvcButton = self.value(forKeyPath: #keyPath(PlaygroundViewController.button)) 35 | print("kvcButton: \(kvcButton)") 36 | } 37 | } 38 | 39 | PlaygroundPage.current.liveView = PlaygroundViewController() 40 | -------------------------------------------------------------------------------- /3b-Classes.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | ### iOSDevUK 2016 — Intermediate Swift 3 | 4 | This is the completed playground from part 3b on Classes, covering Grand Central Dispatch. 5 | */ 6 | import UIKit 7 | import PlaygroundSupport 8 | PlaygroundSupport.PlaygroundPage.current.needsIndefiniteExecution = true 9 | 10 | // Creating your own queue 11 | let myQueue = DispatchQueue(label: "com.iosdevuk.myqueue") 12 | myQueue.sync { 13 | print("Hello from my queue") 14 | } 15 | 16 | DispatchQueue.global(qos: .background).async { 17 | print("Hello from the background queue") 18 | } 19 | 20 | let item1 = DispatchWorkItem { 21 | print("1") 22 | } 23 | 24 | let item2 = DispatchWorkItem { 25 | print("2") 26 | } 27 | 28 | let item3 = DispatchWorkItem { 29 | print("3") 30 | } 31 | 32 | let now = DispatchTime.now() 33 | DispatchQueue.global().async(execute: item1) 34 | DispatchQueue.global().asyncAfter(deadline: now + 2, execute: item2) 35 | DispatchQueue.global().asyncAfter(deadline: now + DispatchTimeInterval.milliseconds(2800), execute: item3) 36 | item3.cancel() 37 | item3.isCancelled 38 | 39 | let globalWrapper: Void = { 40 | print("Inside the wrapper!") 41 | }() 42 | 43 | DispatchQueue.main.async { globalWrapper } 44 | DispatchQueue.global().async { globalWrapper } 45 | -------------------------------------------------------------------------------- /1-Playgrounds.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | ### iOSDevUK 2016 — Intermediate Swift 3 | 4 | This is the completed playground from part 1 on Playgrounds. 5 | */ 6 | import UIKit 7 | import PlaygroundSupport 8 | 9 | // View 10 | let myView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 100)) 11 | myView.backgroundColor = UIColor.orange 12 | 13 | // View controller 14 | class PlaygroundViewController: UIViewController { 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | self.view.backgroundColor = UIColor.red 19 | 20 | let button = UIButton(type: .roundedRect) 21 | button.setTitle("Hello iOSDevUK", for: .normal) 22 | button.setTitleColor(.white, for: .normal) 23 | button.center = CGPoint(x: 50, y: 50) 24 | button.sizeToFit() 25 | self.view.addSubview(button) 26 | } 27 | } 28 | 29 | PlaygroundPage.current.liveView = PlaygroundViewController() 30 | 31 | // Making colours "live viewable" 32 | extension UIColor: PlaygroundLiveViewable { 33 | public var playgroundLiveViewRepresentation: PlaygroundLiveViewRepresentation { 34 | let colourView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) 35 | colourView.backgroundColor = self 36 | 37 | return .view(colourView) 38 | } 39 | } 40 | 41 | PlaygroundPage.current.liveView = UIColor.blue 42 | -------------------------------------------------------------------------------- /2b-Safety.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | ### iOSDevUK 2016 — Intermediate Swift 3 | 4 | This is the completed playground from part 2b on Safety (closures). 5 | */ 6 | import UIKit 7 | 8 | class OperationsService { 9 | var queuedClosures: [() -> Void] = [] 10 | 11 | func performOperation(closure: () -> Void) { 12 | closure() 13 | } 14 | 15 | func enqueueOperation(closure: @escaping () -> Void) { 16 | queuedClosures.append(closure) 17 | } 18 | 19 | func performQueuedOperations() { 20 | for closure in queuedClosures { 21 | closure() 22 | } 23 | 24 | queuedClosures = [] 25 | } 26 | } 27 | 28 | class ViewController { 29 | let operations = OperationsService() 30 | var counter = 1 31 | 32 | func testOperation() { 33 | operations.performOperation { 34 | print("Hello") 35 | // whoa, no self. required! 36 | counter += 1 37 | } 38 | 39 | operations.enqueueOperation { 40 | print("I was enqueued!") 41 | self.counter += 1 42 | } 43 | 44 | operations.enqueueOperation { 45 | print("I was also enqueued") 46 | self.counter += 1 47 | } 48 | 49 | operations.performQueuedOperations() 50 | } 51 | } 52 | 53 | let vc = ViewController() 54 | vc.testOperation() 55 | 56 | print("Counter is at \(vc.counter)") 57 | 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /Intermediate-Swift.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # iOSDevUK 2016 — Intermediate Swift 3 | 4 | Thanks for attending the Intermediate Swift workshop! This playground is a summary of the concepts covered for your reference. 5 | 6 | If you have any questions or feedback, please get in touch; you can reach me as [@gregheo](https://twitter.com/gregheo) on Twitter or at [greg@gregheo.com](mailto:greg@gregheo.com) via email. 7 | */ 8 | import UIKit 9 | //: Remember, you can access all kinds of playground-specific goodies with the `PlaygroundSupport` module. Both `UIView` and `UIViewController` conform to the `PlaygroundLiveViewable` protocol, which means they can be displayed right in the playground. 10 | import PlaygroundSupport 11 | 12 | //: To see the results of something right inline with the playground code, click the "See Results" button in the sidebar. Try it out with this lovely red view. 13 | let redView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 14 | redView.backgroundColor = .red 15 | 16 | //: If you have a more substantial view controller or you don’t want the view inline, you can display it in the assistant editor by setting the playground's live view property. 17 | PlaygroundPage.current.liveView = redView 18 | 19 | //: Remember, live view controllers in the assistant view are fully interactive and great for protyping your interfaces. 20 | 21 | /*: 22 | ## Safety 23 | 24 | Safety is a continuing theme in Swift, from optionals to strong typing to more robust error handling. 25 | 26 | A common bug in Objective-C code comes from the use of stringly-typed parameters. Thanks to the dynamic nature of the Objective-C runtime, you can pass strings representing keys and selectors. That can be great for making decisions at run time, but that also means you lose the compile-time checking. 27 | 28 | Swift 3 introduces some new syntax for dealing with formerly stringly-typed things. Selectors in Swift used to be `StringLiteralConvertible` (now known as `ExpressibleByStringLiteral`), so you could use a plain String and work your way up. 29 | */ 30 | // This is a valid `Selector` 31 | "viewDidLoad" 32 | 33 | // This is an explicit `Selector`. In Swift 2, a lot of people didn’t bother and just went with the string. 34 | Selector("viewDidLoad") 35 | 36 | // New in Swift 3: #selector, which will be checked at compile time. 37 | #selector(UIViewController.viewDidLoad) 38 | 39 | /*: 40 | Similarly, `#keyPath` lets you refer to KVC-compliant properties in a safe, checked manner. 41 | */ 42 | #keyPath(UIView.backgroundColor) 43 | 44 | /*: 45 | Note that the APIs themselves are unchanged and still expect strings! `#selector` and `#keyPath` are still generating strings; it’s just the compiler is checking them first. 46 | 47 | Take a look at the sidebar output at what the type of `someRandomKeypath` is reported as. You can also option-click on `someRandomKeypath` to see what type it is. 48 | */ 49 | let someRandomKeypath = #keyPath(UIColor.cgColor) 50 | type(of: someRandomKeypath) 51 | 52 | /*: 53 | ### Closures 54 | 55 | Remember, closures are objects that can be stored and passed around. When a closure expression is a parameter to a function, it used to be **escaping** by default, meaning closure could outlive the scope of the function. If the closure doesn’t escape the function body, you could annotate the parameter with `@noescape` in Swift 2. 56 | 57 | In Swift 3, this is now flipped around: closures are non-escaping by default, and you need to annotate them with `@escaping`. 58 | 59 | Check out the Swift evolution proposal for more details: [Make non-escaping closures the default](https://github.com/apple/swift-evolution/blob/master/proposals/0103-make-noescape-default.md) 60 | 61 | ### Access levels 62 | 63 | Who can access what? 64 | 65 | * `open` and `public`: anyone! 66 | * `internal`: inside the same module 67 | * `fileprivate`: inside the same file 68 | * `private`: inside the same { scope } 69 | 70 | `public` vs `open` makes a difference for classes: `public` classes are accessible but cannot be subclassed or have their methods overridden from outside the defining module. `open` classes don’t have this restriction and are completely accessible, subclassable, and method-overridable. 71 | 72 | ## Clarity & API Design 73 | 74 | Many of the APIs you know and love look very different in Swift 3. You can read all the details in the [swift.org API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) or rely on the code completion and your own recognition ability. 75 | 76 | Generally, this means keeping the base method name short, and moving more verbiage into the parameter names. It also means many of the parameter names themselves are shorter! If you’re at all familiar with the existing APIs, this should be more of a temporary annoyance. 77 | 78 | ### Grand Central Dispatch 79 | 80 | One of the bigger changes in Swift 3 is to the Grand Central Dispatch (GCD) API. What used to be a C-based approach with opaque contexts and lots of functions is now a fully class-based API! 81 | 82 | **Dispatch queues** are represented by the `DispatchQueue` class. You can create your own queue, or access a global queue with the 83 | */ 84 | DispatchQueue(label: "com.test.myqueue").sync { 85 | // code for my custom queue goes here 86 | } 87 | DispatchQueue.main.async { 88 | // code for the main queue goes here 89 | } 90 | DispatchQueue.global(qos: .background).async { 91 | // background queue code goes here 92 | } 93 | 94 | /*: 95 | **Work items** are for collecting bits of code to be run repeatedly or scheduled. You can think of them as thin wrappers around closures for interacting with GCD in Swift, although most methods will accept both closures and work items. 96 | 97 | Work items can be created, run, scheduled, queued, and cancelled. 98 | */ 99 | let workItem = DispatchWorkItem { 100 | // code for the work item goes here 101 | } 102 | 103 | // run 104 | workItem.perform() 105 | 106 | // queue 107 | DispatchQueue.main.async(execute: workItem) 108 | 109 | // cancel; will never run in the future 110 | workItem.cancel() 111 | 112 | /*: 113 | Check out the Swift evolution proposal for more details on GCD in Swift: [Modernize libdispatch for Swift 3 naming conventions](https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md) 114 | */ --------------------------------------------------------------------------------