├── 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 | */
--------------------------------------------------------------------------------