├── .gitignore ├── CustomViews ├── CustomViews │ ├── SampleTable.json │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── CounterView.swift │ ├── SquaresView.swift │ ├── PacketLineExtensions.swift │ ├── Info.plist │ ├── Extensions.swift │ ├── FeedParser.swift │ ├── RemoteGitRepository.swift │ ├── TwoLabels.swift │ ├── StreamExtensions.swift │ ├── ProgressView.swift │ ├── PacketLineParser.swift │ ├── AppDelegate.swift │ ├── CounterView.xib │ ├── Base.lproj │ │ └── LaunchScreen.xib │ └── SampleTable.swift ├── CustomViews.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── CustomViewsTests │ ├── Info.plist │ └── CustomViewsTests.swift ├── RepositoryBrowser ├── api │ ├── users │ │ ├── alblue.jpg │ │ └── alblue.json │ ├── README.txt │ └── index.json ├── RepositoryBrowserWatch Extension │ ├── Assets.xcassets │ │ └── README__ignoredByTemplate__ │ ├── Info.plist │ ├── RepositoryController.swift │ ├── RepositoryListController.swift │ ├── ExtensionDelegate.swift │ └── InterfaceController.swift ├── RepositoryBrowser.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── RepositoryBrowserTests │ ├── Info.plist │ ├── ThreadsTest.swift │ ├── URITemplateTests.swift │ ├── GitHubAPITests.swift │ ├── RepositoryBrowserTests.swift │ └── NSURLExtensionsTest.swift ├── RepositoryBrowserWatch │ ├── Info.plist │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Base.lproj │ │ └── Interface.storyboard └── RepositoryBrowser │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Threads.swift │ ├── Info.plist │ ├── URITemplate.swift │ ├── DetailViewController.swift │ ├── NSURLExtensions.swift │ ├── Base.lproj │ └── LaunchScreen.xib │ ├── GitHubAPI.swift │ ├── AppDelegate.swift │ └── MasterViewController.swift ├── playing └── MyPlayground.playground │ ├── Resources │ └── alblue.png │ ├── Pages │ ├── Example Documentation.xcplaygroundpage │ │ ├── timeline.xctimeline │ │ └── Contents.swift │ └── Playground Code.xcplaygroundpage │ │ ├── Contents.swift │ │ └── timeline.xctimeline │ └── contents.xcplayground ├── SingleView ├── SingleView.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── SingleViewTests │ ├── Info.plist │ └── SingleViewTests.swift ├── GitHubDetails.swift ├── SingleView │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.swift │ ├── AppDelegate.swift │ └── Base.lproj │ │ └── LaunchScreen.xib ├── GitHubRepositoryTest.swift ├── GitHubRepository.swift ├── Cards.swift └── CardsTest.swift ├── Storyboards ├── Storyboards.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── StoryboardsTests │ ├── Info.plist │ └── StoryboardsTests.swift └── Storyboards │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── ViewController.swift │ ├── Info.plist │ ├── MessageViewController.swift │ ├── AppDelegate.swift │ └── Base.lproj │ └── LaunchScreen.xib ├── MasterDetail ├── MasterDetail.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── MasterDetailTests │ ├── Info.plist │ └── MasterDetailTests.swift └── MasterDetail │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ ├── DetailViewController.swift │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── AppDelegate.swift │ └── MasterViewController.swift ├── exploring ├── upper.swift ├── iteration.swift ├── functions.swift └── literals.swift ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | xcuserdata 4 | xcshareddata 5 | *.xcworkspace 6 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/SampleTable.json: -------------------------------------------------------------------------------- 1 | [{"title":"Sample Title","content":"Sample Content"}] 2 | -------------------------------------------------------------------------------- /RepositoryBrowser/api/users/alblue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alblue/com.packtpub.swift.essentials/HEAD/RepositoryBrowser/api/users/alblue.jpg -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch Extension/Assets.xcassets/README__ignoredByTemplate__: -------------------------------------------------------------------------------- 1 | Did you know that git does not support storing empty directories? 2 | -------------------------------------------------------------------------------- /playing/MyPlayground.playground/Resources/alblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alblue/com.packtpub.swift.essentials/HEAD/playing/MyPlayground.playground/Resources/alblue.png -------------------------------------------------------------------------------- /playing/MyPlayground.playground/Pages/Example Documentation.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CustomViews/CustomViews.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SingleView/SingleView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Storyboards/Storyboards.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetail.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RepositoryBrowser/api/README.txt: -------------------------------------------------------------------------------- 1 | The GitHub API is rate-limited. In order to test and develop 2 | against it the files in this directory can be published to a web server 3 | and used as a testing proxy. 4 | 5 | Run this in a web server (for example, python -m SimpleHTTPServer 1234) 6 | and the API can be used against http://localhost:1234/index.json. 7 | -------------------------------------------------------------------------------- /playing/MyPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /exploring/upper.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xcrun swift 2 | 3 | import func Darwin.exit 4 | 5 | let args = Process.arguments[1.. This is a block quote 23 | //: > which is merged together 24 | //: > using _italics_ or **bold** 25 | //: 26 | //: Link to [AlBlue's Blog](http://alblue.bandlem.com) 27 | //: Image of ![AlBlue](http://alblue.bandlem.com/images/AlexHeadshotLeft.png "AlBlue") -------------------------------------------------------------------------------- /CustomViews/CustomViewsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SingleView/SingleViewTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Storyboards/StoryboardsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetailTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016, Alex Blewitt 4 | Copyright (c) 2016, Packt Publishing Ltd 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /RepositoryBrowser/api/users/alblue.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": "alblue", 3 | "id": 76791, 4 | "avatar_url": "../users/alblue.jpg", 5 | "gravatar_id": "", 6 | "url": "../users/alblue.json", 7 | "html_url": "https://github.com/alblue", 8 | "followers_url": "../users/alblue/followers.json", 9 | "following_url": "../users/alblue/following{/other_user}.json", 10 | "gists_url": "../users/alblue/gists{/gist_id}.json", 11 | "starred_url": "../users/alblue/starred{/owner}{/repo}.json", 12 | "subscriptions_url": "../users/alblue/subscriptions.json", 13 | "organizations_url": "../users/alblue/orgs.json", 14 | "repos_url": "../users/alblue/repos.json", 15 | "events_url": "../users/alblue/events{/privacy}.json", 16 | "received_events_url": "../users/alblue/received_events.json", 17 | "type": "User", 18 | "site_admin": false, 19 | "name": "Alex Blewitt", 20 | "company": "Bandlem Ltd", 21 | "blog": "http://alblue.bandlem.com", 22 | "location": "Milton Keynes, UK", 23 | "email": "Alex.Blewitt+github@gmail.com", 24 | "hireable": false, 25 | "bio": null, 26 | "public_repos": 22, 27 | "public_gists": 1, 28 | "followers": 19, 29 | "following": 1, 30 | "created_at": "2009-04-22T23:57:30Z", 31 | "updated_at": "2014-10-16T16:04:16Z" 32 | } 33 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | RepositoryBrowser 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKCompanionAppBundleIdentifier 31 | com.packtpub.swift.essentials.RepositoryBrowser 32 | WKWatchKitApp 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SingleView/GitHubDetails.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | protocol GitHubDetails { 23 | func detailsURL() -> String 24 | } 25 | 26 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "86x86", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "quickLook", 41 | "subtype" : "38mm" 42 | }, 43 | { 44 | "size" : "98x98", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "quickLook", 48 | "subtype" : "42mm" 49 | } 50 | ], 51 | "info" : { 52 | "version" : 1, 53 | "author" : "xcode" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SingleView/SingleView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Storyboards/Storyboards/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /playing/MyPlayground.playground/Pages/Playground Code.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import UIKit 4 | 5 | var str = "Hello, playground" 6 | 7 | for i in 1...12 { 8 | let j = (i-7) * (i-6) 9 | let k = i 10 | print("I is \(i)") 11 | } 12 | 13 | let blue = UIColor.blueColor() 14 | let size = CGRect(x:0,y:0,width:200,height:100) 15 | let label = UILabel(frame:size) 16 | label.text = str 17 | label.textColor = blue; 18 | label.font = UIFont.systemFontOfSize(24) 19 | let alblue = UIImage(named:"alblue") 20 | 21 | import XCPlayground 22 | 23 | let page = XCPlaygroundPage.currentPage 24 | page.captureValue(alblue, withIdentifier:"Al Blue") 25 | 26 | dispatch_async(dispatch_get_main_queue()) { 27 | for n in 1...6 { 28 | if n % 2 == 0 { 29 | page.captureValue(n,withIdentifier:"even") 30 | page.captureValue(0,withIdentifier:"odd") 31 | } else { 32 | page.captureValue(n,withIdentifier:"odd") 33 | page.captureValue(0,withIdentifier:"even") 34 | } 35 | } 36 | } 37 | page.needsIndefiniteExecution = true 38 | 39 | /** 40 | Returns the string in SHOUTY CAPS 41 | - parameter input: the input string 42 | - author: Alex Blewitt 43 | - returns: The input string, but in upper case 44 | - throws: No errors thrown 45 | - note: Please don't shout 46 | - seealso: String.uppercaseString 47 | - since: 2016 48 | */ 49 | func shout(input:String) -> String { 50 | return input.uppercaseString 51 | } 52 | shout(str) 53 | -------------------------------------------------------------------------------- /RepositoryBrowser/api/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "current_user_url": "user.json", 3 | "authorizations_url": "authorizations.json", 4 | "code_search_url": "search/code?q={query}{&page,per_page,sort,order}.json", 5 | "emails_url": "user/emails.json", 6 | "emojis_url": "emojis.json", 7 | "events_url": "events.json", 8 | "feeds_url": "feeds.json", 9 | "following_url": "user/following{/target}.json", 10 | "gists_url": "gists{/gist_id}.json", 11 | "hub_url": "hub.json", 12 | "issue_search_url": "search/issues?q={query}{&page,per_page,sort,order}.json", 13 | "issues_url": "issues.json", 14 | "keys_url": "user/keys.json", 15 | "notifications_url": "notifications.json", 16 | "organization_repositories_url": "orgs/{org}/repos{?type,page,per_page,sort}.json", 17 | "organization_url": "orgs/{org}.json", 18 | "public_gists_url": "gists/public.json", 19 | "rate_limit_url": "rate_limit.json", 20 | "repository_url": "repos/{owner}/{repo}.json", 21 | "repository_search_url": "search/repositories?q={query}{&page,per_page,sort,order}.json", 22 | "current_user_repositories_url": "user/repos{?type,page,per_page,sort}.json", 23 | "starred_url": "user/starred{/owner}{/repo}.json", 24 | "starred_gists_url": "gists/starred.json", 25 | "team_url": "teams.json", 26 | "user_url": "users/{user}.json", 27 | "user_organizations_url": "user/orgs.json", 28 | "user_repositories_url": "users/{user}/repos{?type,page,per_page,sort}.json", 29 | "user_search_url": "search/users?q={query}{&page,per_page,sort,order}" 30 | } 31 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | RepositoryBrowserWatch Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | WKAppBundleIdentifier 30 | com.packtpub.swift.essentials.RepositoryBrowser.watchkitapp 31 | 32 | NSExtensionPointIdentifier 33 | com.apple.watchkit 34 | 35 | RemoteInterfacePrincipalClass 36 | $(PRODUCT_MODULE_NAME).InterfaceController 37 | WKExtensionDelegateClassName 38 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 39 | 40 | 41 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /SingleView/GitHubRepositoryTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import XCTest 23 | 24 | class GitHubRepositoryTest:XCTestCase { 25 | func testRepository() { 26 | let repo = GitHubRepository() 27 | repo.id = 1 28 | repo.name = "Grit" 29 | XCTAssertEqual(repo.detailsURL(),"https://api.github.com/repositories/1","repository details") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserTests/ThreadsTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import XCTest 23 | 24 | class ThreadsTest: XCTestCase { 25 | func testThreads() { 26 | Threads.runOnBackgroundThread { 27 | XCTAssertFalse(NSThread.isMainThread(), "Running on background thread") 28 | Threads.runOnUIThread { 29 | XCTAssertTrue(NSThread.isMainThread(), "Running on UI thread") 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserTests/URITemplateTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import XCTest 23 | 24 | class URITemplateTests: XCTestCase { 25 | 26 | func testURITemplate() { 27 | let template = "http://example.com/{blah}/blah/{?blah}" 28 | let replacement = URITemplate.replace(template,values: ["blah":"foo"]) 29 | XCTAssertEqual("http://example.com/foo/blah/",replacement,"Template replacement") 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /SingleView/SingleView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/Threads.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | class Threads { 25 | class func runOnBackgroundThread(fn:()->()) { 26 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),fn) 27 | } 28 | class func runOnUIThread(fn:()->()) { 29 | if NSThread.isMainThread() { 30 | fn() 31 | } else { 32 | dispatch_async(dispatch_get_main_queue(), fn) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Storyboards/Storyboards/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class ViewController: UIViewController { 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | // Do any additional setup after loading the view, typically from a nib. 29 | } 30 | 31 | override func didReceiveMemoryWarning() { 32 | super.didReceiveMemoryWarning() 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /SingleView/GitHubRepository.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | class GitHubRepository:GitHubDetails { 23 | class var api:String { 24 | get { 25 | return "https://api.github.com" 26 | } 27 | } 28 | var id:UInt64 = 0 29 | var name:String = "" 30 | func detailsURL() -> String { 31 | return "\(GitHubRepository.api)/repositories/\(id)" 32 | } 33 | } 34 | 35 | // let repo = GitHubRepository() 36 | // repo.id = 1 37 | // repo.name = Grit 38 | // repo.detailsURL() == https://api.github.com/repositories/1 39 | -------------------------------------------------------------------------------- /Storyboards/Storyboards/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/CounterView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class CounterView:UIView { 25 | @IBOutlet weak var label:UILabel! 26 | @IBAction func change(sender:AnyObject) { 27 | let count = (sender as! UIStepper).value 28 | label.text = "Count is \(count)" 29 | } 30 | override func intrinsicContentSize() -> CGSize { 31 | let height = max(50,label.intrinsicContentSize().height) 32 | let width = max(300,label.intrinsicContentSize().width) 33 | return CGSize(width: width, height: height) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SingleView/SingleView/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | 23 | import UIKit 24 | 25 | class ViewController: UIViewController { 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | view.backgroundColor = UIColor.blackColor() 30 | let label = UILabel(frame:view.bounds) 31 | label.textColor = UIColor.whiteColor() 32 | label.textAlignment = .Center 33 | label.text = "Welcome to Swift" 34 | view.addSubview(label) 35 | } 36 | 37 | override func didReceiveMemoryWarning() { 38 | super.didReceiveMemoryWarning() 39 | // Dispose of any resources that can be recreated. 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /SingleView/Cards.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | enum Suit { 23 | case Clubs, Diamonds, Hearts // many on one line 24 | case Spades // or on different lines 25 | } 26 | 27 | // var suit:Suit = Suit.Clubs 28 | // var suit:Suit = .Clubs 29 | 30 | enum Rank:Int { 31 | case Two = 2, Three, Four, Five 32 | case Six, Seven, Eight, Nine, Ten 33 | case Jack, Queen, King, Ace 34 | } 35 | 36 | // Rank.Two.rawValue == 2 37 | // Rank(rawValue:14)! == Rank.Ace 38 | 39 | enum Card { 40 | case Face(Rank,Suit) 41 | case Joker 42 | } 43 | 44 | // var aceOfSpades:Card = .Face(.Ace,.Spades) 45 | // var twoOfHearts:Card = .Face(.Two,.Hearts) 46 | // var theJoker:Card = .Joker 47 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch Extension/RepositoryController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import WatchKit 23 | import Foundation 24 | 25 | class RepositoryController: WKInterfaceController { 26 | @IBOutlet weak var repo: WKInterfaceLabel! 27 | @IBOutlet weak var issues: WKInterfaceLabel! 28 | @IBOutlet weak var watchers: WKInterfaceLabel! 29 | @IBOutlet weak var forks: WKInterfaceLabel! 30 | override func awakeWithContext(context: AnyObject?) { 31 | if let data = context as? [String:String] { 32 | repo.setText(data["name"]) 33 | issues.setText(data["open_issues_count"]) 34 | watchers.setText(data["watchers_count"]) 35 | forks.setText(data["forks_count"]) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserTests/GitHubAPITests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import XCTest 23 | 24 | class GitHubAPITests: XCTestCase{ 25 | func testApi() { 26 | let bundle = NSBundle(forClass:GitHubAPITests.self) 27 | if let url = bundle.URLForResource("api/index", withExtension:"json") { 28 | if let api = GitHubAPI.connect(url) { 29 | XCTAssertTrue(true,"Created API") 30 | let userRepo = api.getURLForUserRepos("alblue") 31 | userRepo.withJSONArrayOfDictionary { 32 | array in 33 | XCTAssertEqual(22,array.count,"Number of repos") 34 | } 35 | api.withUserRepos("alblue") { 36 | array in 37 | XCTAssertEqual(22,array.count,"Number of repos") 38 | } 39 | } else { 40 | XCTAssertFalse(true,"Failed to parse \(url)") 41 | } 42 | } else { 43 | XCTAssertFalse(true,"Failed to find sample API") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /SingleView/SingleViewTests/SingleViewTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | import XCTest 24 | 25 | class SingleViewTests: XCTestCase { 26 | 27 | override func setUp() { 28 | super.setUp() 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | } 31 | 32 | override func tearDown() { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | super.tearDown() 35 | } 36 | 37 | func testExample() { 38 | // This is an example of a functional test case. 39 | XCTAssert(true, "Pass") 40 | } 41 | 42 | func testPerformanceExample() { 43 | // This is an example of a performance test case. 44 | self.measureBlock() { 45 | // Put the code you want to measure the time of here. 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /CustomViews/CustomViewsTests/CustomViewsTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | import XCTest 24 | 25 | class CustomViewsTests: XCTestCase { 26 | 27 | override func setUp() { 28 | super.setUp() 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | } 31 | 32 | override func tearDown() { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | super.tearDown() 35 | } 36 | 37 | func testExample() { 38 | // This is an example of a functional test case. 39 | XCTAssert(true, "Pass") 40 | } 41 | 42 | func testPerformanceExample() { 43 | // This is an example of a performance test case. 44 | self.measureBlock() { 45 | // Put the code you want to measure the time of here. 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetailTests/MasterDetailTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | import XCTest 24 | 25 | class MasterDetailTests: XCTestCase { 26 | 27 | override func setUp() { 28 | super.setUp() 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | } 31 | 32 | override func tearDown() { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | super.tearDown() 35 | } 36 | 37 | func testExample() { 38 | // This is an example of a functional test case. 39 | XCTAssert(true, "Pass") 40 | } 41 | 42 | func testPerformanceExample() { 43 | // This is an example of a performance test case. 44 | self.measureBlock() { 45 | // Put the code you want to measure the time of here. 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Storyboards/StoryboardsTests/StoryboardsTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | import XCTest 24 | 25 | class StoryboardsTests: XCTestCase { 26 | 27 | override func setUp() { 28 | super.setUp() 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | } 31 | 32 | override func tearDown() { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | super.tearDown() 35 | } 36 | 37 | func testExample() { 38 | // This is an example of a functional test case. 39 | XCTAssert(true, "Pass") 40 | } 41 | 42 | func testPerformanceExample() { 43 | // This is an example of a performance test case. 44 | self.measureBlock() { 45 | // Put the code you want to measure the time of here. 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/SquaresView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class SquaresView: UIView { 25 | required init?(coder: NSCoder) { 26 | super.init(coder:coder) 27 | setupView() 28 | } 29 | override init(frame: CGRect) { 30 | super.init(frame:frame) 31 | setupView() 32 | } 33 | func setupView() { 34 | contentMode = .Redraw 35 | } 36 | override func drawRect(rect: CGRect) { 37 | let context = UIGraphicsGetCurrentContext() 38 | let red = UIColor.redColor().CGColor 39 | CGContextSetStrokeColorWithColor(context,red) 40 | CGContextStrokeRect(context, CGRect(x:center.x - 50, y:center.y - 50, width:100, height:100)) 41 | UIColor.greenColor().setFill() 42 | UIColor.blackColor().setStroke() 43 | CGContextFillRect(context, CGRect(x:center.x - 25, y:center.y - 25, width:50, height:50)) 44 | CGContextStrokeRect(context, CGRect(x:center.x - 25, y:center.y - 25, width:50, height:50)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserTests/RepositoryBrowserTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | import XCTest 24 | 25 | class RepositoryBrowserTests: XCTestCase { 26 | 27 | override func setUp() { 28 | super.setUp() 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | } 31 | 32 | override func tearDown() { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | super.tearDown() 35 | } 36 | 37 | func testExample() { 38 | // This is an example of a functional test case. 39 | XCTAssert(true, "Pass") 40 | } 41 | 42 | func testPerformanceExample() { 43 | // This is an example of a performance test case. 44 | self.measureBlock() { 45 | // Put the code you want to measure the time of here. 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /SingleView/CardsTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import XCTest 23 | 24 | class CardsTest:XCTestCase { 25 | func testSuit() { 26 | let suit_clubs:Suit = Suit.Clubs 27 | let clubs:Suit = .Clubs 28 | XCTAssertEqual(suit_clubs,clubs,"Clubs are equal") 29 | } 30 | func testRank() { 31 | XCTAssertEqual(Rank.Two.rawValue,2,"Rank.Two.rawValue == 2") 32 | XCTAssertEqual(Rank(rawValue: 14)!,Rank.Ace,"Rank(rawValue:14)! == Rank.Ace") 33 | } 34 | func testCard() { 35 | let aceOfSpades:Card = .Face(.Ace,.Spades) 36 | let theJoker:Card = .Joker 37 | 38 | var jokerSeen = false; 39 | var aceOfSpadesSeen = false; 40 | 41 | for card in [aceOfSpades,theJoker] { 42 | switch(card) { 43 | case .Face(.Ace,.Spades): aceOfSpadesSeen = true 44 | case .Face(let rank, let suit): XCTFail("Saw a card \(rank) of \(suit)") 45 | case .Joker: jokerSeen = true 46 | } 47 | } 48 | XCTAssert(jokerSeen) 49 | XCTAssert(aceOfSpadesSeen) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/PacketLineExtensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | extension NSOutputStream { 25 | func writePacketLine(message:String = "") -> Int { 26 | let data = message.utf8data 27 | let length = data.length 28 | if length == 0 { 29 | return writeData("0000".utf8data) 30 | } else { 31 | let prefix = (length + 4).toHex(4).utf8data 32 | return self.writeData(prefix) + self.writeData(data) 33 | } 34 | } 35 | } 36 | 37 | extension NSInputStream { 38 | func readPacketLine() -> NSData? { 39 | if let data = readData(4) { 40 | let length = data.utf8string.fromHex() 41 | if length == 0 { 42 | return nil 43 | } else { 44 | return readData(length - 4) 45 | } 46 | } else { 47 | return nil 48 | } 49 | } 50 | func readPacketLineString() -> NSString? { 51 | if let data = self.readPacketLine() { 52 | return data.utf8string 53 | } else { 54 | return nil 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/URITemplate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | class URITemplate { 25 | class func replace(template:String, values:[String:String]) -> String { 26 | var replacement = template 27 | while true { 28 | if let parameterRange = replacement.rangeOfString("\\{[^}]*\\}", options: NSStringCompareOptions.RegularExpressionSearch) { 29 | var value:String 30 | let parameter = replacement.substringWithRange(parameterRange) 31 | if parameter.hasPrefix("{?") { 32 | value = "" 33 | } else { 34 | let start = parameterRange.startIndex.successor() 35 | let end = parameterRange.endIndex.predecessor() 36 | let name = replacement.substringWithRange(Range(start:start,end:end)) 37 | value = values[name] ?? "" 38 | } 39 | replacement.replaceRange(parameterRange, with: value) 40 | } else { 41 | break 42 | } 43 | } 44 | return replacement 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class DetailViewController: UIViewController { 25 | 26 | @IBOutlet weak var detailDescriptionLabel: UILabel! 27 | 28 | 29 | var detailItem: AnyObject? { 30 | didSet { 31 | // Update the view. 32 | self.configureView() 33 | } 34 | } 35 | 36 | func configureView() { 37 | // Update the user interface for the detail item. 38 | if let detail: AnyObject = self.detailItem { 39 | if let label = self.detailDescriptionLabel { 40 | label.text = detail.description 41 | } 42 | } 43 | } 44 | 45 | override func viewDidLoad() { 46 | super.viewDidLoad() 47 | // Do any additional setup after loading the view, typically from a nib. 48 | self.configureView() 49 | } 50 | 51 | override func didReceiveMemoryWarning() { 52 | super.didReceiveMemoryWarning() 53 | // Dispose of any resources that can be recreated. 54 | } 55 | 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIStatusBarHidden 30 | 31 | UIViewControllerBasedStatusBarAppearance 32 | 33 | NSAppTransportSecurity 34 | 35 | NSAllowsArbitraryLoads 36 | 37 | 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UIStatusBarTintParameters 43 | 44 | UINavigationBar 45 | 46 | Style 47 | UIBarStyleDefault 48 | Translucent 49 | 50 | 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/DetailViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class DetailViewController: UIViewController { 25 | 26 | @IBOutlet weak var userLabel: UILabel? 27 | @IBOutlet weak var repoLabel: UILabel? 28 | @IBOutlet weak var issuesLabel: UILabel? 29 | @IBOutlet weak var watchersLabel: UILabel? 30 | 31 | var user: String? { 32 | didSet { 33 | configureView() 34 | } 35 | } 36 | var repo: String? { 37 | didSet { 38 | configureView() 39 | } 40 | } 41 | var data:[String:String]? { 42 | didSet { 43 | configureView() 44 | } 45 | } 46 | override func viewDidLoad() { 47 | super.viewDidLoad() 48 | configureView() 49 | } 50 | func configureView() { 51 | if let label = userLabel { label.text = user } 52 | if let label = repoLabel { label.text = repo } 53 | if let label = issuesLabel { 54 | label.text = self.data?["open_issues_count"] 55 | } 56 | if let label = watchersLabel { 57 | label.text = self.data?["watchers_count"] 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Storyboards/Storyboards/MessageViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class MessageViewController: UIViewController { 25 | 26 | let messages = [ 27 | "Ouch, that hurts", 28 | "Please don't do that again", 29 | "Why did you press that?", 30 | ] 31 | @IBAction func changeMessage() { 32 | message.text = messages[Int(arc4random_uniform(UInt32(messages.count)))] 33 | } 34 | @IBOutlet weak var message: UILabel! 35 | @IBAction func about(sender: AnyObject) { 36 | performSegueWithIdentifier("about", sender: sender) 37 | } 38 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 39 | if segue.identifier == "about" { 40 | let dest = segue.destinationViewController as UIViewController 41 | dest.view.backgroundColor = message.backgroundColor 42 | } 43 | } 44 | override func viewDidLoad() { 45 | let red = CGFloat(drand48()) 46 | let green = CGFloat(drand48()) 47 | let blue = CGFloat(drand48()) 48 | message.backgroundColor = UIColor( 49 | red:red, 50 | green:green, 51 | blue:blue, 52 | alpha:0.5 53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserTests/NSURLExtensionsTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import XCTest 23 | 24 | class NSURLExtensionsTest: XCTestCase { 25 | 26 | func testNSURLJSON() { 27 | let json = "{\"test\":\"value\"}".dataUsingEncoding(NSUTF8StringEncoding)! 28 | let base64 = json.base64EncodedDataWithOptions(.EncodingEndLineWithLineFeed) 29 | let data = NSString(data: base64, encoding: NSUTF8StringEncoding)! 30 | let dataURL = NSURL(string:"data:text/plain;base64,\(data)")! 31 | dataURL.withJSONDictionary { 32 | dict in 33 | XCTAssertEqual(dict["test"] ?? "", "value", "Value is as expected") 34 | } 35 | sleep(1) 36 | } 37 | func testNSURLJSONArray() { 38 | let json = "[{\"test\":\"value\"}]".dataUsingEncoding(NSUTF8StringEncoding)! 39 | let base64 = json.base64EncodedDataWithOptions(.EncodingEndLineWithLineFeed) 40 | let data = NSString(data: base64, encoding: NSUTF8StringEncoding)! 41 | let dataURL = NSURL(string:"data:text/plain;base64,\(data)")! 42 | dataURL.withJSONArrayOfDictionary { 43 | dict in 44 | XCTAssertEqual(dict[0]["test"] ?? "", "value", "Value is as expected") 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/Extensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | extension NSData { 25 | var utf8string:String { 26 | return String(data:self,encoding:NSUTF8StringEncoding)! 27 | } 28 | } 29 | 30 | extension String { 31 | var utf8data:NSData { 32 | return self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 33 | } 34 | func fromHex() -> Int { 35 | var result = 0 36 | for c in self.characters { 37 | result *= 16 38 | switch c { 39 | case "0": result += 0 40 | case "1": result += 1 41 | case "2": result += 2 42 | case "3": result += 3 43 | case "4": result += 4 44 | case "5": result += 5 45 | case "6": result += 6 46 | case "7": result += 7 47 | case "8": result += 8 48 | case "9": result += 9 49 | case "a", "A": result += 10 50 | case "b", "B": result += 11 51 | case "c", "C": result += 12 52 | case "d", "D": result += 13 53 | case "e", "E": result += 14 54 | case "f", "F": result += 15 55 | default: break 56 | } 57 | } 58 | return result; 59 | } 60 | } 61 | 62 | extension Int { 63 | func toHex(digits:Int) -> String { 64 | return String(format:"%0\(digits)x",self) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch Extension/RepositoryListController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import WatchKit 23 | import Foundation 24 | 25 | class RepositoryRowController: NSObject { 26 | @IBOutlet weak var name: WKInterfaceLabel! 27 | } 28 | class RepositoryListController: WKInterfaceController { 29 | let delegate = WKExtension.sharedExtension().delegate as! ExtensionDelegate 30 | @IBOutlet weak var repositoriesTable: WKInterfaceTable! 31 | var repos = [] 32 | override func awakeWithContext(context: AnyObject?) { 33 | super.awakeWithContext(context) 34 | if let user = context as? String { 35 | delegate.loadReposFor(user) { 36 | result in 37 | self.repositoriesTable.setNumberOfRows( 38 | result.count, withRowType: "repository") 39 | self.repos = result 40 | for (index,repo) in result.enumerate() { 41 | let controller = self.repositoriesTable 42 | .rowControllerAtIndex(index) as! RepositoryRowController 43 | controller.name.setText(repo["name"] ?? "") 44 | } 45 | } 46 | } else { 47 | repos = [] 48 | } 49 | } 50 | override func contextForSegueWithIdentifier( 51 | segueIdentifier: String, 52 | inTable table: WKInterfaceTable, 53 | rowIndex: Int) -> AnyObject? { 54 | return repos[rowIndex] 55 | } 56 | } -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/NSURLExtensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | func toStringString(dict:[String:AnyObject]) -> [String:String] { 25 | var result:[String:String] = [:] 26 | for (key,value) in dict { 27 | if let valueString = value as? String { 28 | result[key] = valueString 29 | } else { 30 | result[key] = "\(value)" 31 | } 32 | } 33 | return result 34 | } 35 | 36 | extension NSURL { 37 | func withJSONDictionary(fn:[String:String] -> ()) { 38 | let session = NSURLSession.sharedSession() 39 | session.dataTaskWithURL(self) { 40 | data,response,error -> () in 41 | if let json = try? NSJSONSerialization.JSONObjectWithData( 42 | data!, options: .AllowFragments) as? [String:AnyObject] { 43 | fn(toStringString(json!)) 44 | } else { 45 | fn([String:String]()) 46 | } 47 | }.resume() 48 | } 49 | func withJSONArrayOfDictionary(fn:[[String:String]] -> ()) { 50 | let session = NSURLSession.sharedSession() 51 | session.dataTaskWithURL(self) { 52 | data,response,error -> () in 53 | if let json = try? NSJSONSerialization.JSONObjectWithData( 54 | data!, options: .AllowFragments) as? [[String:AnyObject]] { 55 | fn(json!.map(toStringString)) 56 | } else { 57 | fn([[String:String]]()) 58 | } 59 | }.resume() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /exploring/iteration.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xcrun swift 2 | 3 | for i in 1...12 { // for-in loop over a numeric range 4 | print("i is \(i)") // print out i 5 | } 6 | 7 | for _ in 1...12 { // underscore is a hole - ignored value 8 | print("Looping...") // underscore cannot be read 9 | } 10 | 11 | // Set up data from previously 12 | var shopping = [ 13 | "Milk", 14 | "Eggs", 15 | "Coffee", 16 | "Tea", 17 | ] 18 | 19 | var costs = [ 20 | "Milk":1, 21 | "Eggs":2, 22 | "Coffee":3, 23 | "Tea":4, 24 | ] 25 | 26 | var cost = 0 27 | for item in shopping { // for-in loop iterates over collection 28 | if let c = costs[item] { // if block uses let in case cost is nil 29 | cost += c // note this is not idiomatic; see below 30 | } // for tuple iteration 31 | } 32 | cost == 10 33 | 34 | Array(costs.keys) // creates a new array based on keys 35 | Array(costs.values) // creates a new array based on values 36 | 37 | for item in costs.keys { // can iterate over keys in a dictionary 38 | print(item) 39 | } 40 | 41 | var (a,b) = (1,2) // tuple assignment; effectively a=1,b=2 42 | 43 | for (item,cost) in costs { // can iterate over key/values together 44 | print("The \(item) " + 45 | "costs \(cost)") 46 | } 47 | 48 | var sum = 0 49 | for var i=0;i<=10;i++ { // can use traditional for loops as well 50 | sum += i // however may be removed in Swift 3 51 | } 52 | sum == 55 53 | 54 | for var i=0,j=10; i<=10 && j >= 0; i++, j-- { // can use many loop variables 55 | print("\(i), \(j)"); 56 | } 57 | 58 | // --- break, continue and labels --- 59 | 60 | var deck = [1...13,1...13,1...13,1...13] 61 | suits: for suit in deck { // suit: introduces a label at this for block 62 | for card in suit { // nested for block 63 | if card == 3 { 64 | continue // goes to 'for card in suit' with next card 65 | } 66 | if card == 5 { 67 | continue suits // goes to the 'for suit in deck' with next suit 68 | } 69 | if card == 7 { 70 | break // leaves 'for card in suit' block 71 | } 72 | if card == 13 { 73 | break suits // leaves 'for suit in deck' block 74 | } 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /playing/MyPlayground.playground/Pages/Playground Code.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import WatchKit 23 | 24 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 25 | var api:GitHubAPI! 26 | var users:[String] = [] 27 | var repos:[String:[[String:String]]] = [:] 28 | func loadReposFor(user:String, fn:([[String:String]])->()) { 29 | repos[user] = [] 30 | api.withUserRepos(user) { 31 | results in 32 | self.repos[user] = results 33 | fn(results) 34 | } 35 | } 36 | func addUser(user:String) { 37 | users += [user] 38 | users.sortInPlace({ $0 < $1 }) 39 | } 40 | func applicationDidFinishLaunching() { 41 | // Perform any final initialization of your application. 42 | api = GitHubAPI.connect() 43 | addUser("alblue") 44 | } 45 | func applicationDidBecomeActive() { 46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 47 | } 48 | func applicationWillResignActive() { 49 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 50 | // Use this method to pause ongoing tasks, disable timers, etc. 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import WatchKit 23 | import Foundation 24 | 25 | class InterfaceController: WKInterfaceController { 26 | let delegate = WKExtension.sharedExtension().delegate as! ExtensionDelegate 27 | @IBOutlet weak var usersTable: WKInterfaceTable! 28 | override func awakeWithContext(context: AnyObject?) { 29 | super.awakeWithContext(context) 30 | // Configure interface objects here. 31 | let users = delegate.users 32 | usersTable.setNumberOfRows(users.count, withRowType: "user") 33 | for (index,user) in users.enumerate() { 34 | let controller = usersTable.rowControllerAtIndex(index) as! UserRowController 35 | controller.name.setText(user) 36 | delegate.api.withUserImage(user) { 37 | image in controller.icon.setImage(image) 38 | } 39 | } 40 | } 41 | override func contextForSegueWithIdentifier( 42 | segueIdentifier: String, 43 | inTable table: WKInterfaceTable, 44 | rowIndex: Int) -> AnyObject? { 45 | return delegate.users[rowIndex] 46 | } 47 | override func willActivate() { 48 | // This method is called when watch view controller is about to be visible to user 49 | super.willActivate() 50 | } 51 | override func didDeactivate() { 52 | // This method is called when watch view controller is no longer visible 53 | super.didDeactivate() 54 | } 55 | } 56 | 57 | class UserRowController: NSObject { 58 | @IBOutlet weak var name: WKInterfaceLabel! 59 | @IBOutlet weak var icon: WKInterfaceImage! 60 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Swift Essentials 2 | ================ 3 | 4 | This repository contains source code for the Packt Publishing book 5 | "Swift Essentials" for the first and second editions. Tags and branches 6 | exist for both versions. 7 | 8 | https://www.packtpub.com/application-development/swift-essentials 9 | 10 | Second edition 11 | -------------- 12 | * ISBN-10: 1785888870 13 | * ISBN-13: 978-1-78588-887-8 14 | 15 | *Chapters* 16 | 17 | * Chapter 1: [Exploring Swift](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter1) 18 | * Chapter 2: [Playing with Swift](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter2) 19 | * Chapter 3: [Creating an iOS Swift App](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter3) 20 | * Chapter 4: [Storyboard Applications with Swift and iOS](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter4) 21 | * Chapter 5: [Creating Custom Views in Swift](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter5) 22 | * Chapter 6: [Parsing Networked Data](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter6) 23 | * Chapter 7: [Building a Repository Browser](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter7) 24 | * Chapter 8: [Adding Watch Support](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition2/chapter8) 25 | 26 | First edition 27 | ------------- 28 | * ISBN-10: 1784396702 29 | * ISBN-13: 978-1-78439-670-1 30 | 31 | *Chapters* 32 | 33 | * Chapter 1: [Exploring Swift](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter1) 34 | * Chapter 2: [Playing with Swift](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter2) 35 | * Chapter 3: [Creating an iOS Swift App](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter3) 36 | * Chapter 4: [Storyboard Applications with Swift and iOS](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter4) 37 | * Chapter 5: [Creating Custom Views in Swift](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter5) 38 | * Chapter 6: [Parsing Networked Data](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter6) 39 | * Chapter 7: [Building a Repository Browser](https://github.com/alblue/com.packtpub.swift.essentials/tree/edition1/chapter7) 40 | 41 | Contact 42 | ------- 43 | 44 | Follow me on Twitter or @alblue, or mail alex.blewitt@gmail.com 45 | http://alblue.bandlem.com 46 | 47 | The book website is http://swiftessentials.org and Twitter @swiftessentials 48 | 49 | LICENSE 50 | ------- 51 | 52 | Code examples are licensed under the MIT license as contained in the 53 | LICENSE file 54 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/FeedParser.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | class FeedParser: NSObject, NSXMLParserDelegate { 25 | var inEntry:Bool = false 26 | var inTitle:Bool = false 27 | var title:String? 28 | var link:String? 29 | var items:[(String,String)] = [] 30 | init(_ data:NSData) { 31 | let parser = NSXMLParser(data: data); 32 | parser.shouldProcessNamespaces = true 33 | super.init() 34 | parser.delegate = self 35 | // If you get an EXC_BAD_ACCESS on the parse() 36 | // line, check your delegate methods are declared 37 | // to allow implicitly unwrapped optionals since 38 | // sometimes these values may be nil 39 | parser.parse() 40 | } 41 | func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName: String?, attributes: [String:String]) { 42 | switch elementName { 43 | case "entry": 44 | inEntry = true 45 | case "title": 46 | inTitle = true 47 | case "link": 48 | link = attributes["href"] 49 | default: break 50 | } 51 | } 52 | func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName: String?) { 53 | switch elementName { 54 | case "entry": 55 | inEntry = false 56 | if title != nil && link != nil { 57 | items += [(title!,link!)] 58 | } 59 | title = nil 60 | link = nil 61 | case "title": inTitle = false 62 | default: break 63 | } 64 | } 65 | func parser(parser: NSXMLParser, foundCharacters string: String) { 66 | if inEntry && inTitle { 67 | title = string 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/RemoteGitRepository.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | class RemoteGitRepository { 25 | let host:String 26 | let repo:String 27 | let port:Int 28 | init(host:String, repo:String, _ port:Int = 9418) { 29 | self.host = host 30 | self.repo = repo 31 | self.port = port 32 | } 33 | func lsRemote() -> [String:String] { 34 | var refs = [String:String]() 35 | if let (input,output) = NSStream.open(host,port) { 36 | output.writePacketLine("git-upload-pack \(repo)\0host=\(host)\0") 37 | while true { 38 | if let response = input.readPacketLineString() { 39 | let hash = String(response.substringToIndex(41)) 40 | let ref = String(response.substringFromIndex(41)) 41 | if ref.hasPrefix("HEAD") { 42 | continue 43 | } else { 44 | refs[ref] = hash 45 | } 46 | } else { 47 | break 48 | } 49 | } 50 | output.writePacketLine() 51 | input.close() 52 | output.close() 53 | } 54 | return refs 55 | } 56 | func lsRemoteAsync(fn:(String,String) -> ()) { 57 | if let (input,output) = NSStream.connect(host,port) { 58 | input.delegate = PacketLineParser(output) { (response:NSString) -> () in 59 | let hash = String(response.substringToIndex(41)) 60 | let ref = String(response.substringFromIndex(41)) 61 | if !ref.hasPrefix("HEAD") { 62 | fn(ref,hash) 63 | } 64 | } 65 | input.scheduleInRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode) 66 | input.open() 67 | output.open() 68 | output.writePacketLine("git-upload-pack \(repo)\0host=\(host)\0") 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/TwoLabels.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class TwoLabels:UIView { 25 | var left:UILabel = UILabel() 26 | var right:UILabel = UILabel() 27 | required init?(coder:NSCoder) { 28 | super.init(coder:coder) 29 | setupView() 30 | } 31 | override init(frame: CGRect) { 32 | super.init(frame:frame) 33 | setupView() 34 | } 35 | func setupView() { 36 | addSubview(left) 37 | addSubview(right) 38 | configureView() 39 | setNeedsUpdateConstraints() 40 | } 41 | func configureView() { 42 | left.text = "Left" 43 | right.text = "Right" 44 | } 45 | override func updateConstraints() { 46 | translatesAutoresizingMaskIntoConstraints = false 47 | left.translatesAutoresizingMaskIntoConstraints = false 48 | right.translatesAutoresizingMaskIntoConstraints = false 49 | removeConstraints(constraints) 50 | // left.width = right.width * 1 + 0 51 | let equalWidths = NSLayoutConstraint(item: left, attribute: .Width, relatedBy: .Equal, toItem: right, attribute: .Width, multiplier: 1, constant: 0) 52 | addConstraint(equalWidths) 53 | let options = NSLayoutFormatOptions() 54 | let namedViews = ["left":left,"right":right] 55 | addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[left]-[right]-|", options: options, metrics:nil, views: namedViews)); 56 | addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[left]-|", options: options, metrics:nil, views: namedViews)); 57 | addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[right]-|", options: options, metrics:nil, views: namedViews)); 58 | super.updateConstraints() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/StreamExtensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | extension NSStream { 25 | class func open(host:String,_ port:Int) -> (NSInputStream, NSOutputStream)? { 26 | if let (input,output) = connect(host,port) { 27 | input.open() 28 | output.open() 29 | return (input,output) 30 | } else { 31 | return nil 32 | } 33 | } 34 | class func connect(host:String,_ port:Int) -> (NSInputStream, NSOutputStream)? { 35 | var input:NSInputStream? 36 | var output:NSOutputStream? 37 | NSStream.getStreamsToHostWithName(host, port: port, inputStream: &input, outputStream: &output) 38 | guard let i = input, o = output else { 39 | return nil 40 | } 41 | return (i,o) 42 | } 43 | } 44 | 45 | extension NSInputStream { 46 | func readBytes(size:Int) -> [UInt8]? { 47 | let buffer = Array(count:size,repeatedValue:0) 48 | var completed = 0 49 | while completed < size { 50 | let read = self.read(UnsafeMutablePointer(buffer) + completed, maxLength: size - completed) 51 | if read < 0 { 52 | return nil 53 | } else { 54 | completed += read 55 | } 56 | } 57 | return buffer 58 | } 59 | func readData(size:Int) -> NSData? { 60 | if let buffer = readBytes(size) { 61 | return NSData(bytes: UnsafeMutablePointer(buffer), length: buffer.count) 62 | } else { 63 | return nil 64 | } 65 | } 66 | } 67 | 68 | extension NSOutputStream { 69 | func writeData(data:NSData) -> Int { 70 | let size = data.length 71 | var completed = 0 72 | while completed < size { 73 | let wrote = write(UnsafePointer(data.bytes) + 74 | completed, maxLength:size - completed) 75 | if wrote < 0 { 76 | return wrote 77 | } else { 78 | completed += wrote 79 | } 80 | } 81 | return completed 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/ProgressView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | @IBDesignable class ProgessView: UIView { 25 | let circle = CAShapeLayer() 26 | let square = CAShapeLayer() 27 | let progress = CAShapeLayer() 28 | @IBInspectable var progressAmount: CGFloat = 0.5 { 29 | didSet { 30 | setNeedsLayout() 31 | } 32 | } 33 | let mask = CAShapeLayer() 34 | let black = UIColor.blackColor().CGColor 35 | required init?(coder: NSCoder) { 36 | super.init(coder:coder) 37 | setupView() 38 | } 39 | override init(frame: CGRect) { 40 | super.init(frame:frame) 41 | setupView() 42 | } 43 | func setupView() { 44 | for layer in [progress, square, circle] { 45 | layer.strokeColor = black 46 | layer.fillColor = nil 47 | self.layer.addSublayer(layer) 48 | } 49 | progress.lineWidth = 10 50 | progress.strokeColor = UIColor.redColor().CGColor 51 | configureView() 52 | } 53 | func configureView() { 54 | let rect = self.bounds 55 | let sq = rect.insetBy(dx: rect.width/3, dy: rect.height/3) 56 | square.fillColor = black 57 | square.path = UIBezierPath(rect: sq).CGPath 58 | circle.path = UIBezierPath(ovalInRect: rect).CGPath 59 | let radius = min(rect.width, rect.height)/2 60 | let center = CGPoint(x: rect.midX, y: rect.midY) 61 | progress.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: CGFloat(-M_PI_2), endAngle: CGFloat(3 * M_PI_2), clockwise: true).CGPath 62 | progress.strokeStart = 0 63 | progress.strokeEnd = progressAmount 64 | mask.path = UIBezierPath(ovalInRect: rect).CGPath 65 | progress.mask = mask 66 | } 67 | override func layoutSubviews() { 68 | setupView() 69 | } 70 | @IBAction func setProgress(sender:AnyObject) { 71 | switch sender { 72 | case let slider as UISlider: progressAmount = CGFloat(slider.value) 73 | case let stepper as UIStepper: progressAmount = CGFloat(stepper.value) 74 | default: break 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/PacketLineParser.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | 24 | class PacketLineParser: NSObject, NSStreamDelegate { 25 | let output:NSOutputStream 26 | let callback:(NSString)->() 27 | var capture:PacketLineParser? 28 | init(_ output:NSOutputStream, _ callback:(NSString) -> ()) { 29 | self.output = output 30 | self.callback = callback 31 | super.init() 32 | // Since this is stored in an NSInputStream delegate which 33 | // only weakly owns this object, if no other approach is 34 | // taken this object will be thrown away before it receives 35 | // any messages. To avoid this, we create a cyclic reference 36 | // to itself to prevent it being closed, and then handle 37 | // the cleanup when the stream is ended. 38 | capture = self 39 | } 40 | func stream(stream: NSStream, handleEvent: NSStreamEvent) { 41 | let input = stream as! NSInputStream 42 | if handleEvent == NSStreamEvent.HasBytesAvailable { 43 | if let line = input.readPacketLineString() { 44 | callback(line) 45 | } else { 46 | closeStreams(input,output) 47 | } 48 | } 49 | if handleEvent == NSStreamEvent.EndEncountered || handleEvent == NSStreamEvent.ErrorOccurred { 50 | closeStreams(input,output) 51 | } 52 | } 53 | func closeStreams(input:NSInputStream,_ output:NSOutputStream) { 54 | // only run this once 55 | if capture != nil { 56 | // ensure that the capture is released 57 | capture = nil 58 | // remove from run loop 59 | output.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode) 60 | input.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode) 61 | // remove any delegates 62 | input.delegate = nil 63 | output.delegate = nil 64 | // close the streams, if they are not closed already 65 | if output.streamStatus != NSStreamStatus.Closed { 66 | // send the other client a close message 67 | output.writePacketLine() 68 | output.close() 69 | } 70 | if input.streamStatus != NSStreamStatus.Closed { 71 | input.close() 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | @UIApplicationMain 25 | class AppDelegate: UIResponder, UIApplicationDelegate { 26 | 27 | var window: UIWindow? 28 | 29 | 30 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 31 | // Override point for customization after application launch. 32 | return true 33 | } 34 | 35 | func applicationWillResignActive(application: UIApplication) { 36 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 37 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 38 | } 39 | 40 | func applicationDidEnterBackground(application: UIApplication) { 41 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 42 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 43 | } 44 | 45 | func applicationWillEnterForeground(application: UIApplication) { 46 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 47 | } 48 | 49 | func applicationDidBecomeActive(application: UIApplication) { 50 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 51 | } 52 | 53 | func applicationWillTerminate(application: UIApplication) { 54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 55 | } 56 | 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Storyboards/Storyboards/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | 23 | import UIKit 24 | 25 | @UIApplicationMain 26 | class AppDelegate: UIResponder, UIApplicationDelegate { 27 | 28 | var window: UIWindow? 29 | 30 | 31 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 32 | // Override point for customization after application launch. 33 | return true 34 | } 35 | 36 | func applicationWillResignActive(application: UIApplication) { 37 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 38 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 39 | } 40 | 41 | func applicationDidEnterBackground(application: UIApplication) { 42 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 43 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 44 | } 45 | 46 | func applicationWillEnterForeground(application: UIApplication) { 47 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 48 | } 49 | 50 | func applicationDidBecomeActive(application: UIApplication) { 51 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 52 | } 53 | 54 | func applicationWillTerminate(application: UIApplication) { 55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 56 | } 57 | 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /SingleView/SingleView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | @UIApplicationMain 25 | class AppDelegate: UIResponder, UIApplicationDelegate { 26 | 27 | var window: UIWindow? 28 | 29 | 30 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 31 | window = UIWindow() 32 | window?.rootViewController = ViewController() 33 | window?.makeKeyAndVisible() 34 | return true 35 | } 36 | 37 | func applicationWillResignActive(application: UIApplication) { 38 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 39 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 40 | } 41 | 42 | func applicationDidEnterBackground(application: UIApplication) { 43 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | func applicationWillEnterForeground(application: UIApplication) { 48 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 49 | } 50 | 51 | func applicationDidBecomeActive(application: UIApplication) { 52 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 53 | } 54 | 55 | func applicationWillTerminate(application: UIApplication) { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/CounterView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SingleView/SingleView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Storyboards/Storyboards/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/GitHubAPI.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import Foundation 23 | import UIKit 24 | 25 | class GitHubAPI { 26 | let base:NSURL 27 | let services:[String:String] 28 | let cache = NSCache() 29 | class func connect() -> GitHubAPI? { 30 | return connect("https://api.github.com") 31 | } 32 | class func connect(url:String) -> GitHubAPI? { 33 | if let nsurl = NSURL(string:url) { 34 | return connect(nsurl) 35 | } else { 36 | return nil 37 | } 38 | } 39 | class func connect(url:NSURL) -> GitHubAPI? { 40 | if let data = NSData(contentsOfURL:url) { 41 | if let json = try? NSJSONSerialization.JSONObjectWithData(data,options:.AllowFragments) as? [String:String] { 42 | return GitHubAPI(url,json!) 43 | } else { 44 | return nil 45 | } 46 | } else { 47 | return nil 48 | } 49 | } 50 | init(_ base:NSURL, _ services:[String:String]) { 51 | self.base = base 52 | self.services = services 53 | } 54 | func getURLForUserRepos(user:String) -> NSURL { 55 | let key = "r:\(user)" 56 | if let url = cache.objectForKey(key) as? NSURL { 57 | return url 58 | } else { 59 | let userRepositoriesURL = services["user_repositories_url"]! 60 | let userRepositoryURL = URITemplate.replace(userRepositoriesURL, values:["user":user]) 61 | let url = NSURL(string:userRepositoryURL, relativeToURL:base)! 62 | cache.setObject(url,forKey:key) 63 | return url 64 | } 65 | } 66 | func withUserRepos(user:String, fn:([[String:String]]) -> ()) { 67 | let key = "repos:\(user)" 68 | if let repos = cache.objectForKey(key) as? [[String:String]] { 69 | fn(repos) 70 | } else { 71 | let url = getURLForUserRepos(user) 72 | url.withJSONArrayOfDictionary { 73 | repos in 74 | self.cache.setObject(repos,forKey:key) 75 | fn(repos) 76 | } 77 | } 78 | } 79 | func getURLForUserInfo(user:String) -> NSURL { 80 | let key = "ui:\(user)" 81 | if let url = cache.objectForKey(key) as? NSURL { 82 | return url 83 | } else { 84 | let userURL = services["user_url"]! 85 | let userSpecificURL = URITemplate.replace(userURL, values:["user":user]) 86 | let url = NSURL(string:userSpecificURL, relativeToURL:base)! 87 | cache.setObject(url,forKey:key) 88 | return url 89 | } 90 | } 91 | func withUserImage(user:String, fn:(UIImage -> ())) { 92 | let key = "image:\(user)" 93 | if let image = cache.objectForKey(key) as? UIImage { 94 | fn(image) 95 | } else { 96 | let url = getURLForUserInfo(user) 97 | url.withJSONDictionary { 98 | userInfo in 99 | if let avatar_url = userInfo["avatar_url"] { 100 | if let avatarURL = NSURL(string:avatar_url, relativeToURL:url) { 101 | if let data = NSData(contentsOfURL:avatarURL) { 102 | if let image = UIImage(data: data) { 103 | self.cache.setObject(image,forKey:key) 104 | fn(image) 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | @UIApplicationMain 25 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { 26 | 27 | var window: UIWindow? 28 | 29 | 30 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 31 | // Override point for customization after application launch. 32 | let splitViewController = self.window!.rootViewController as! UISplitViewController 33 | let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController 34 | navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() 35 | splitViewController.delegate = self 36 | return true 37 | } 38 | 39 | func applicationWillResignActive(application: UIApplication) { 40 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 41 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 42 | } 43 | 44 | func applicationDidEnterBackground(application: UIApplication) { 45 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | func applicationWillEnterForeground(application: UIApplication) { 50 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 51 | } 52 | 53 | func applicationDidBecomeActive(application: UIApplication) { 54 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 55 | } 56 | 57 | func applicationWillTerminate(application: UIApplication) { 58 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 59 | } 60 | 61 | // MARK: - Split view 62 | 63 | func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool { 64 | if let secondaryAsNavController = secondaryViewController as? UINavigationController { 65 | if let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController { 66 | if topAsDetailController.detailItem == nil { 67 | // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded. 68 | return true 69 | } 70 | } 71 | } 72 | return false 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | @UIApplicationMain 25 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { 26 | 27 | var window: UIWindow? 28 | var api:GitHubAPI! 29 | var users:[String] = [] 30 | var repos:[String:[[String:String]]] = [:] 31 | 32 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 33 | // Override point for customization after application launch. 34 | let splitViewController = self.window!.rootViewController as! UISplitViewController 35 | let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController 36 | navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() 37 | splitViewController.delegate = self 38 | api = GitHubAPI.connect("https://raw.githubusercontent.com/alblue/com.packtpub.swift.essentials/master/RepositoryBrowser/api/index.json") 39 | users = [] // or load from NSUserDefaults.standardUserDefaults 40 | return true 41 | } 42 | 43 | func applicationWillResignActive(application: UIApplication) { 44 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 45 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 46 | } 47 | 48 | func applicationDidEnterBackground(application: UIApplication) { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | func applicationWillEnterForeground(application: UIApplication) { 54 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 55 | } 56 | 57 | func applicationDidBecomeActive(application: UIApplication) { 58 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 59 | } 60 | 61 | func applicationWillTerminate(application: UIApplication) { 62 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 63 | } 64 | 65 | // MARK: - Split view 66 | 67 | func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool { 68 | return true 69 | } 70 | func loadRepoNamesFor(user:String, fn:([[String:String]])->()) { 71 | repos[user] = [] 72 | api.withUserRepos(user) { 73 | results in 74 | self.repos[user] = results 75 | fn(results) 76 | } 77 | } 78 | func addUser(user:String) { 79 | users += [user] 80 | users.sortInPlace({ $0 < $1 }) 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/MasterViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class MasterViewController: UITableViewController { 25 | 26 | var detailViewController: DetailViewController? = nil 27 | var objects = NSMutableArray() 28 | 29 | 30 | override func awakeFromNib() { 31 | super.awakeFromNib() 32 | if UIDevice.currentDevice().userInterfaceIdiom == .Pad { 33 | self.clearsSelectionOnViewWillAppear = false 34 | self.preferredContentSize = CGSize(width: 320.0, height: 600.0) 35 | } 36 | } 37 | 38 | override func viewDidLoad() { 39 | super.viewDidLoad() 40 | // Do any additional setup after loading the view, typically from a nib. 41 | self.navigationItem.leftBarButtonItem = self.editButtonItem() 42 | 43 | let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:") 44 | self.navigationItem.rightBarButtonItem = addButton 45 | if let split = self.splitViewController { 46 | let controllers = split.viewControllers 47 | self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController 48 | } 49 | } 50 | 51 | override func didReceiveMemoryWarning() { 52 | super.didReceiveMemoryWarning() 53 | // Dispose of any resources that can be recreated. 54 | } 55 | 56 | func insertNewObject(sender: AnyObject) { 57 | objects.insertObject(NSDate(), atIndex: 0) 58 | let indexPath = NSIndexPath(forRow: 0, inSection: 0) 59 | self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) 60 | } 61 | 62 | // MARK: - Segues 63 | 64 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 65 | if segue.identifier == "showDetail" { 66 | if let indexPath = self.tableView.indexPathForSelectedRow { 67 | let object = objects[indexPath.row] as! NSDate 68 | let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController 69 | controller.detailItem = object 70 | controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() 71 | controller.navigationItem.leftItemsSupplementBackButton = true 72 | } 73 | } 74 | } 75 | 76 | // MARK: - Table View 77 | 78 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 79 | return 1 80 | } 81 | 82 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 83 | return objects.count 84 | } 85 | 86 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 87 | let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell 88 | 89 | let object = objects[indexPath.row] as! NSDate 90 | cell.textLabel!.text = object.description 91 | return cell 92 | } 93 | 94 | override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 95 | // Return false if you do not want the specified item to be editable. 96 | return true 97 | } 98 | 99 | override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 100 | if editingStyle == .Delete { 101 | objects.removeObjectAtIndex(indexPath.row) 102 | tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 103 | } else if editingStyle == .Insert { 104 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view. 105 | } 106 | } 107 | 108 | 109 | } 110 | 111 | -------------------------------------------------------------------------------- /CustomViews/CustomViews/SampleTable.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class SampleTable: UITableViewController { 25 | var items = [ 26 | ("First", "A first item"), 27 | ("Second", "A second item"), 28 | ] 29 | required init?(coder: NSCoder) { 30 | super.init(coder:coder) 31 | } 32 | override func viewDidLoad() { 33 | let xib = UINib(nibName:"CounterView",bundle:nil) 34 | let objects = xib.instantiateWithOwner(self, options:nil) 35 | let counter = objects.first as? UIView 36 | tableView.tableHeaderView = counter 37 | let footer = UITableViewHeaderFooterView() 38 | footer.contentView.addSubview(TwoLabels(frame:CGRect.zero)) 39 | tableView.tableFooterView = footer 40 | let url = NSURL(string: "https://raw.githubusercontent.com/alblue/com.packtpub.swift.essentials/master/CustomViews/CustomViews/SampleTable.json")! 41 | let session = NSURLSession.sharedSession() 42 | let task = session.dataTaskWithURL(url, completionHandler: {data,response,error -> Void in 43 | guard error == nil else { 44 | self.items += [("Error",error!.localizedDescription)] 45 | return 46 | } 47 | let statusCode = (response as! NSHTTPURLResponse).statusCode 48 | guard statusCode < 500 else { 49 | self.items += [("Server error \(statusCode)", 50 | url.absoluteString)] 51 | return 52 | } 53 | guard statusCode < 400 else { 54 | self.items += [("Client error \(statusCode)", 55 | url.absoluteString)] 56 | return 57 | } 58 | if let parsed = try? NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) as! NSArray { 59 | for entry in parsed { 60 | switch (entry["title"], entry["content"]) { 61 | case (let title as String, let content as String): 62 | self.items += [(title,content)] 63 | default: 64 | self.items += [("Error", "Missing unknown entry")] 65 | } 66 | } 67 | } else { 68 | self.items += [("Error", "JSON is not an array")] 69 | } 70 | self.runOnUIThread { 71 | self.tableView.backgroundColor = UIColor.redColor() 72 | self.tableView.reloadData() 73 | self.tableView.backgroundColor = UIColor.greenColor() 74 | } 75 | }) 76 | task.resume() 77 | session.dataTaskWithURL(NSURL(string:"http://alblue.bandlem.com/Tag/swift/atom.xml")!, completionHandler: {data,response,error -> Void in 78 | if let data = data { 79 | self.items += FeedParser(data).items 80 | self.runOnUIThread(self.tableView.reloadData) 81 | } 82 | }).resume() 83 | runOnBackgroundThread { 84 | let repo = RemoteGitRepository(host: "github.com", repo: "/alblue/com.packtpub.swift.essentials.git") 85 | // way of forcing true without compiler warnings of unused code 86 | let async = arc4random() >= 0 87 | if async { 88 | repo.lsRemoteAsync() { (ref:String,hash:String) in 89 | self.items += [(ref,hash)] 90 | self.runOnUIThread(self.tableView.reloadData) 91 | } 92 | } else { 93 | for (ref,hash) in repo.lsRemote() { 94 | self.items += [(ref,hash)] 95 | } 96 | self.runOnUIThread(self.tableView.reloadData) 97 | } 98 | } 99 | } 100 | func runOnBackgroundThread(fn:()->()) { 101 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),fn) 102 | } 103 | func runOnUIThread(fn:()->()) { 104 | if NSThread.isMainThread() { 105 | fn() 106 | } else { 107 | dispatch_async(dispatch_get_main_queue(), fn) 108 | } 109 | } 110 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 111 | return items.count 112 | } 113 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 114 | let (title,subtitle) = items[indexPath.row] 115 | let cell = tableView.dequeueReusableCellWithIdentifier("prototypeCell")! 116 | let titleLabel = cell.viewWithTag(1) as! UILabel 117 | let subtitleLabel = cell.viewWithTag(2) as! UILabel 118 | titleLabel.text = title 119 | subtitleLabel.text = subtitle 120 | return cell 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowserWatch/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 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 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 |
61 | 62 | 63 | 64 |
65 |
66 | 67 |
68 | 69 | 70 | 71 | 72 | 73 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 |
90 | -------------------------------------------------------------------------------- /RepositoryBrowser/RepositoryBrowser/MasterViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Alex Blewitt, Bandlem Ltd 2 | // Copyright (c) 2016, Packt Publishing Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | import UIKit 23 | 24 | class MasterViewController: UITableViewController { 25 | 26 | var detailViewController: DetailViewController? = nil 27 | var app:AppDelegate! 28 | 29 | override func awakeFromNib() { 30 | super.awakeFromNib() 31 | if UIDevice.currentDevice().userInterfaceIdiom == .Pad { 32 | self.clearsSelectionOnViewWillAppear = false 33 | self.preferredContentSize = CGSize(width: 320.0, height: 600.0) 34 | } 35 | } 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | // Do any additional setup after loading the view, typically from a nib. 40 | let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:") 41 | self.navigationItem.rightBarButtonItem = addButton 42 | if let split = self.splitViewController { 43 | let controllers = split.viewControllers 44 | self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController 45 | } 46 | app = UIApplication.sharedApplication().delegate 47 | as? AppDelegate 48 | } 49 | 50 | override func didReceiveMemoryWarning() { 51 | super.didReceiveMemoryWarning() 52 | // Dispose of any resources that can be recreated. 53 | } 54 | 55 | func insertNewObject(sender: AnyObject) { 56 | let alert = UIAlertController( 57 | title: "Add user", 58 | message: "Please select a user to add", 59 | preferredStyle: .Alert) 60 | alert.addAction(UIAlertAction( 61 | title: "Cancel", style: .Cancel, handler: nil)) 62 | alert.addAction(UIAlertAction( 63 | title: "Add", style: .Default) { 64 | alertAction in 65 | let username = alert.textFields![0].text 66 | self.app.addUser(username!) 67 | Threads.runOnUIThread { 68 | self.tableView.reloadData() 69 | } 70 | }) 71 | alert.addTextFieldWithConfigurationHandler { 72 | textField -> Void in 73 | textField.placeholder = "Username"; 74 | } 75 | presentViewController(alert, animated: true, completion: nil) 76 | } 77 | 78 | // MARK: - Segues 79 | 80 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 81 | if segue.identifier == "showDetail" { 82 | if let indexPath = self.tableView.indexPathForSelectedRow { 83 | // get the details controller 84 | let controller = (segue.destinationViewController as! 85 | UINavigationController).topViewController 86 | as! DetailViewController 87 | // set the details 88 | let user = app.users[indexPath.section] 89 | let repo = app.repos[user]![indexPath.row] 90 | controller.repo = repo["name"] ?? "" 91 | controller.user = user 92 | controller.data = repo 93 | controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() 94 | controller.navigationItem.leftItemsSupplementBackButton = true 95 | } 96 | } 97 | } 98 | 99 | // MARK: - Table View 100 | 101 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 102 | return app.users.count 103 | } 104 | 105 | override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 106 | let cell = UITableViewHeaderFooterView() 107 | let user = app.users[section] 108 | cell.textLabel!.text = user 109 | app.api.withUserImage(user) { 110 | image in 111 | let minSize = min(cell.frame.height, cell.frame.width) 112 | let squareSize = CGSize(width:minSize, height:minSize) 113 | let imageFrame = CGRect(origin:cell.frame.origin, size:squareSize) 114 | Threads.runOnUIThread { 115 | let imageView = UIImageView(image:image) 116 | imageView.frame = imageFrame 117 | cell.addSubview(imageView) 118 | cell.setNeedsLayout() 119 | cell.setNeedsDisplay() 120 | } 121 | } 122 | return cell 123 | } 124 | 125 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 126 | let user = app.users[section] 127 | if let repos = app.repos[user] { 128 | return repos.count 129 | } else { 130 | app.loadRepoNamesFor(user) { _ in 131 | Threads.runOnUIThread { 132 | tableView.reloadSections( 133 | NSIndexSet(index: section), 134 | withRowAnimation: .Automatic) 135 | } 136 | } 137 | return 0 138 | } 139 | } 140 | 141 | override func tableView(tableView: UITableView, 142 | cellForRowAtIndexPath indexPath: NSIndexPath) 143 | -> UITableViewCell { 144 | let cell = tableView.dequeueReusableCellWithIdentifier( 145 | "Cell", forIndexPath: indexPath) 146 | let user = app.users[indexPath.section] 147 | let repo = app.repos[user]![indexPath.row] 148 | cell.textLabel!.text = repo["name"] ?? "" 149 | return cell 150 | } 151 | 152 | override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 153 | // Return false if you do not want the specified item to be editable. 154 | return false 155 | } 156 | 157 | } 158 | 159 | -------------------------------------------------------------------------------- /exploring/functions.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xcrun swift 2 | 3 | // Set up data from previously 4 | var shopping = [ 5 | "Milk", 6 | "Eggs", 7 | "Coffee", 8 | "Tea", 9 | ] 10 | 11 | var costs = [ 12 | "Milk":1, 13 | "Eggs":2, 14 | "Coffee":3, 15 | "Tea":4, 16 | ] 17 | 18 | 19 | // --- Functions --- 20 | 21 | func costOf( // define function costOf 22 | items:[String], // first argument is array of String 23 | _ costs:[String:Int]) // second is dictionary of String:Int 24 | -> Int { // which returns an Int 25 | var cost = 0 // cost is local to this function 26 | for item in items { // loop as before 27 | if let ci = costs[item] { 28 | cost += ci 29 | } 30 | } 31 | return cost // return the cost from the function 32 | } 33 | costOf(shopping,costs) == 10 // call function 34 | 35 | // --- Named arguments --- 36 | 37 | func costOfNamed( // same function but with named args 38 | basket items:[String], // basket is the external parameter name 39 | prices costs:[String:Int]) // prices is the external parameter name 40 | -> Int { 41 | var cost = 0 // cost is local to this function 42 | for item in items { // loop as before 43 | if let ci = costs[item] { 44 | cost += ci 45 | } 46 | } 47 | return cost // return the cost from the function 48 | } 49 | costOfNamed(basket:shopping,prices:costs) // call with named arguments 50 | 51 | // --- Default arguments --- 52 | 53 | func costOfDefault( // same function but with default value 54 | items items:[String], // same as before 55 | costs:[String:Int] = costs) // parameter defaults to 'costs' 56 | -> Int { 57 | var cost = 0 // cost is local to this function 58 | for item in items { // loop as before 59 | if let ci = costs[item] { 60 | cost += ci 61 | } 62 | } 63 | return cost // return the cost from the function 64 | } 65 | costOfDefault(items:shopping) // uses default costs 66 | costOfDefault(items:shopping, costs:costs) // call with explicit costs value 67 | 68 | // --- guards --- 69 | 70 | func cardName(value:Int) -> String { 71 | guard value >= 1 && value <= 13 else { 72 | return "Unknown card" 73 | } 74 | let cardNames = [11:"Jack",12:"Queen",13:"King",1:"Ace",] 75 | return cardNames[value] ?? "\(value)" 76 | } 77 | cardName(11) == "Jack" 78 | cardName(15) == "Unknown card" 79 | 80 | func firstElement(list:[Int]) -> String { 81 | guard let first = list.first else { 82 | return "List is empty" 83 | } 84 | return "Value is \(first)" 85 | } 86 | firstElement([]) == "List is empty" 87 | firstElement([1,2,3]) == "Value is 1" 88 | 89 | // --- Variadic functions --- 90 | 91 | func minmax(numbers:Int...) // numbers... is variadic, acts as array 92 | -> (Int,Int) { // can return multiple values in a tuple 93 | var min = Int.max // Int.max is the largest Int 94 | var max = Int.min // Int.min is the smallest Int 95 | for number in numbers { // loop through numbers 96 | if number < min { 97 | min = number // and remember smallest 98 | } 99 | if number > max { 100 | max = number // and largest 101 | } 102 | } 103 | return (min,max) // (min,max) is tuple which get returned 104 | } 105 | 106 | minmax(1,2,3,4) // == (1,4) // can pass in arbitrary number of args 107 | // minmax() == (Int.max,Int.min) // but doesn't make sense with zero args 108 | 109 | func minmaxOpt(numbers:Int...) // same function but with 110 | -> (Int,Int)? { // an optional tuple return type 111 | var min = Int.max 112 | var max = Int.min 113 | if numbers.count == 0 { // if no numbers are provided 114 | return nil // then return nil (no value) 115 | } else { 116 | for number in numbers { 117 | if number < min { 118 | min = number 119 | } 120 | if number > max { 121 | max = number 122 | } 123 | } 124 | return(min,max) // otherwise return a tuple with values 125 | } 126 | } 127 | minmaxOpt() // == nil 128 | minmaxOpt(1) // == (1,1) 129 | minmaxOpt(1,2,3,4) // == (1,4) 130 | 131 | // --- Structures --- 132 | 133 | struct MinMax { // can create structures 134 | var min:Int // min must be specified upon creation 135 | var max:Int // max must be specified upon creation 136 | } 137 | 138 | func minmaxStruct(numbers:Int...) // function as before 139 | -> MinMax? { // except returning a struct 140 | var minmax = MinMax(min:Int.max, max:Int.min) // init struct 141 | if numbers.count == 0 { 142 | return nil // nil as before for optionality 143 | } else { 144 | for number in numbers { 145 | if number < minmax.min { 146 | minmax.min = number // assign to struct member 147 | } 148 | if number > minmax.max { 149 | minmax.max = number // assign to struct member 150 | } 151 | } 152 | return minmax // return struct as a whole 153 | } 154 | } 155 | minmaxStruct(1,2,3,4) 156 | 157 | // --- Error handling --- 158 | 159 | struct Oops : ErrorType { 160 | let message: String 161 | } 162 | 163 | // throw Oops(message: "Something went wrong") // will throw an error 164 | 165 | func cardNameError(value:Int) throws -> String { 166 | guard value >= 1 && value <= 13 else { 167 | throw Oops(message:"Unknown card") 168 | } 169 | let cardNames = [11:"Jack",12:"Queen",13:"King",1:"Ace",] 170 | return cardNames[value] ?? "\(value)" 171 | } 172 | // cardNameError(1) == "Ace" 173 | // cardNameError(15) // Unknown card 174 | 175 | do { 176 | let name = try cardNameError(15) 177 | print("You chose \(name)") 178 | } catch { 179 | print("You chose an invalid card") // error is 'error' here 180 | } 181 | 182 | do { 183 | let name = try cardNameError(15) 184 | print("You chose \(name)") 185 | } catch let e { // error is 'e' here 186 | print("There was a problem \(e)") // can also use where clauses 187 | } 188 | 189 | let aceOptional = try? cardNameError(1) // == "Ace" 190 | let unknown = try? cardNameError(15) // == nil 191 | 192 | let value = 13 193 | if let card = try? cardNameError(value) { 194 | print("You chose: \(card)") 195 | } 196 | 197 | func deferExample() { 198 | defer { // executed at function return 199 | print("C") 200 | } 201 | print("A") 202 | defer { 203 | print("B") 204 | } 205 | } 206 | deferExample() // prints A, B, C 207 | 208 | func deferEarly() { 209 | defer { 210 | print("C") 211 | } 212 | print("A") 213 | return 214 | defer { // generates an unreachable warning 215 | print("B") 216 | } 217 | } 218 | deferEarly() // prints A, C 219 | 220 | -------------------------------------------------------------------------------- /exploring/literals.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xcrun swift 2 | 3 | // Try out these expressions in the Swift interpreter 4 | // 5 | // $ xcrun swift 6 | // Welcome to Swift! Type :help for assistance. 7 | // 1> "Hello World" 8 | // $R0: String = "Hello World" 9 | // 2> 3+4 10 | // $R1: Int = 7 11 | // 3> $R0 12 | // $R2: String = "Hello World" 13 | // 4> $R1 14 | // $R3: Int = 7 15 | 16 | // Literal Values and Expressions 17 | 18 | "Hello World" // String 19 | 3+4 // Int 20 | 21 | 3.141 // Double (64 bit) 22 | 299_792_458 // Int with underscores for readability 23 | -1 // Int is signed 32 or 64 bit 24 | 1_800_123456 // Underscores have no effect on value 25 | 26 | 0b1010011 // 83 decimal: binary values begin 0b 27 | 0o123 // 83 decimal: octal values begin 0o 28 | 0123 // 123 decimal: leading zeros decimal 29 | 0x7b // 123 decimal: hex values begin 0x 30 | 31 | 3.141 // 64-bit Double value 32 | Float(3.141) // 32-bit Float value 33 | Float80(3.141) // 80-bit Float value 34 | 35 | 299.792458e6 // 299792458 as a Double: e6 is *10^6 36 | 299.792_458_e6 // 299792458: underscores can be used 37 | 38 | 0x1p8 // 256 as a Double: p8 is *2^8 39 | 0x1p10 // 1024 as a Double: p10 is *2^10 40 | 0x4p10 // 4096 as a Double: p10 is *2^10 41 | 42 | 1e-1 // 0.1 as a Double: e-1 is *10^-1 43 | 1e-2 // 0.01 as a Double: e-2 is *10^-2 44 | 0x1p-1 // 0.5 as a Double: p0-1 is *2^-1 45 | 0x1p-2 // 0.5 as a Double: p0-2 is *2^-2 46 | 0xAp-1 // 5 as a Double: p0-1 is *2^-1 47 | 48 | "\\" // \ character 49 | "\0" // nul character 50 | "\'" // single quote character 51 | "\"" // double quote character 52 | "\t" // tab character 53 | "\n" // newline character 54 | "\r" // carriage return character 55 | "\u{20AC}" // unicode character (Euro) € 56 | "\u{1F600}" // unicdoe character (Smiley) 😀 57 | 58 | "3+4 is \(3+4)" // interpolated string is "3+4 is 7" 59 | "7x2 is \(7*2)" // interpolated string is "7x2 is 14" 60 | 61 | let pi = 3.141 // let introduces a constant pi 62 | // pi = 3 // cannot re-assign constants 63 | // error: cannot assign to 'let' value 'pi' 64 | var i = 0 // type infers i is Int 65 | ++i // ++ increment for integral types 66 | // ++ and -- may be removed in Swift 3 67 | 68 | let e:Float = 2.718 // specify type to change default 69 | let ff:UInt8 = 255 // Int8,Int16,Int32,Int64 and UInt* 70 | let ooff = UInt16(ff) // Can convert to a different type 71 | // Int8(255) // 255 cannot be represented in Int8 72 | // error: integer overflows when converted from 'Int' to 'Int8' 73 | // UInt8(Int8(-1)) // negative cannot be in unsigned 74 | // error: negative integer cannot be converted to unsigned type 'UInt8' 75 | 76 | var shopping = [ // arrays start with [ and end with ] 77 | "Milk", // comma separated elements 78 | "Eggs", // type of array is type of elements[] 79 | "Coffee", // trailing comma is recommended 80 | ] 81 | 82 | // trailing commas permit new entries to be added to the end of 83 | // an array without incurring an SCM diff and so should be used 84 | // 85 | // compare the following: which looks better in an SCM diff? 86 | // 87 | // var shopping = [ 88 | // "Milk", 89 | // "Eggs", 90 | // "Coffee", 91 | // + "Tea", 92 | // ] 93 | // 94 | // var shopping = [ 95 | // "Milk", 96 | // "Eggs", 97 | // - "Coffee" 98 | // + "Coffee", 99 | // + "Tea" 100 | // ] 101 | 102 | var costs = [ // Dictionaries store key/value pairs 103 | "Milk":1, // Keys can be any (hashable) object 104 | "Eggs":2, // colon separates key from value 105 | "Coffee":3, // value can be any type 106 | ] 107 | 108 | var m = shopping[0] // subscripts arrays == "Milk" 109 | var c = costs["Milk"] // subscripts dictionaries == 1? 110 | shopping.count // returns a count of elements 3 111 | shopping += ["Tea"] // += is an array append operator 112 | shopping.count // returns a count of elements, now 4 113 | // costs["Tea"] = "" // assignment is typechecked in dict 114 | // error: cannot assign value of type 'String' to type 'Int?' 115 | costs["Tea"] = 4 // assignment into a dictionary 116 | costs.count // count works on dictionaries too 117 | 118 | var shoppingSet: Set = [ // Sets are unordered 119 | "Milk", 120 | "Eggs", 121 | "Coffee", 122 | ] 123 | // var shoppingSet = Set( [ "Milk", "Eggs", "Coffee", ] ) // same as above 124 | shoppingSet.contains("Milk") // contains used to test membership 125 | shoppingSet.contains("Tea") // == false 126 | shoppingSet.remove("Coffee") // remove items, returns "Coffee"? 127 | shoppingSet.remove("Tea") // returns nil if item is not present 128 | shoppingSet.insert("Tea") // insert can be used to add items 129 | shoppingSet.contains("Tea") // == true 130 | 131 | var cannotBeNil: Int = 1 // non-optional cannot be assigned nil 132 | // cannotBeNil = nil 133 | // error: cannot assign a value of type 'nil' to a value of type 'Int' 134 | var canBeNil: Int? = 1 // returns Optional(1) 135 | canBeNil = nil // nil can be assigned to optional 136 | 137 | var opt = Optional(1) // may have values opt:Int? == 1 138 | opt == nil // false; optional with value is not nil 139 | opt! == 1 // unwrap a known optional to get value 140 | opt = nil // optionals can be nil if not present 141 | // opt! // in nil case unwrapping causes a crash 142 | // fatal error: unexpectedly found nil while unwrapping an Optional value 143 | 144 | var implicitlyUnwrappedOptional:Int! = 1// unwrapped on demand 145 | implicitlyUnwrappedOptional + 2 // == 3 146 | implicitlyUnwrappedOptional = nil // assign nil 147 | // implicitlyUnwrappedOptional + 2 // causes error 148 | // fatal error: unexpectedly found nil while unwrapping an Optional value 149 | 150 | 1 ?? 2 // == 1 151 | nil ?? 2 // == 2 152 | Optional(1) ?? 2 // == 1 153 | 154 | costs["Tea"] ?? 0 // == 4 155 | costs["Sugar"] ?? 0 // == 0 156 | 157 | var cost = 0 158 | if let cc = costs["Coffee"] { // introduces new constant 'cc' for body 159 | cost += cc // cc is non-nil and already unwrapped 160 | } // cc is no longer in scope here 161 | 162 | if let cm = costs["Milk"], let ct = costs["Tea"] { 163 | cost += cm + ct // both cm and ct are unwrapped 164 | } // only if both are not nil 165 | 166 | if let cb = costs["Bread"] { // can add else clauses as well 167 | cost += cb 168 | } else { 169 | print( "No bread" ) // else clause run if costs["Bread"] nil 170 | } 171 | 172 | // var i from above 173 | i = 17 // % is the remainder operator 174 | i % 2 == 0 ? "Even" : "Odd" // ternary if operator is test ? t : f 175 | 176 | var position = 21 177 | switch position { // switch can be any expression 178 | case 1: print("1st") // case statements do not fall through 179 | case 2: print("2nd") // each is independent 180 | case 3: print("3rd") 181 | case 4...20: // ranges can be used as a match 182 | print("\(position)th") // prints if >= 4 and <= 20 183 | case position where (position % 10) == 1: // as can where clauses 184 | print("\(position)st") 185 | case let p where (p % 10) == 2: // also can use local constants 186 | print("\(p)nd") 187 | case let p where (p % 10) == 3: 188 | print("\(p)rd") 189 | default: print("\(position)th") // default for catch-all case 190 | } 191 | // prints 21st 192 | 193 | 4...10 ~= 4 // 4 matches the pattern (range) 4...10 194 | 4...21 ~= 4 // 21 does not match 4...10 195 | 196 | 1...10 ~= 10 // 10 is in 1...10 197 | 1..<10 ~= 10 // 10 is not in 1..<10 198 | 1..<10 == 1...9 // both are 1 to 9 inclusive 199 | 200 | -------------------------------------------------------------------------------- /MasterDetail/MasterDetail/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | 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 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | --------------------------------------------------------------------------------