Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WWDC 2017 Retrospective

WWDC 2017 Retrospective

Xcode 9
Swift 4
Codable
KeyPath
KVO
UIKit
AppStore
ARKit
Core ML
Core NFC
PDFKit

Kishikawa Katsumi

July 14, 2017
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. WWDC The Apple Worldwide Developers Conference is a conference held

    annually in California by Apple Inc. The event is used by Apple to showcase its new software and technologies for software developers. [email protected]
  2. Xcode 9 • All new source editor • Refactoring Swift

    Code • New Build System • Named colors • Main Thread Checker/Undefined Behavior Sanitizer • Network debugging • Multiple concurrent simulators • Parallel device testing • Control and capture screenshots • Swift 4 [email protected]
  3. 102 Platforms State of the Union 404 Debugging with Xcode

    9 406 Finding Bugs Using Xcode Runtime Tools [email protected] Reference
  4. Swift 4 • Codable • KeyPath • Type-safe KVO •

    Generics subscript • String • Dictionary [email protected]
  5. Codable (Decodable & Encodable) • SwiftωΠςΟϒͷσʔλߏ଄ʢClass, Structure, EnumerationʣΛγ ϦΞϥΠζՄೳͳσʔλʹ૬ޓʹม׵͢Δ࢓૊ΈΛఏڙ͢Δ •

    DecodableʹରԠ͍ͯ͠ΔͱγϦΞϥΠζՄೳͳσʔλʢJSONͳ Ͳʣ͔ΒΦϒδΣΫτ΁ͷม׵͕ՄೳʹͳΔɻEncodable͸ͦͷٯ ʢStructure => JSONͳͲʣ • ίϯύΠϥͷࢧԉ͕ड͚ΒΕΔʢϝιου΍Ωʔͷࣗಈੜ੒ͳͲʣ ͷͰγϯϓϧʹॻ͚Δʢ৔߹͕͋Δʣ [email protected]
  6. Codable (Decodable & Encodable) [email protected] struct Person: Codable { ...

    } let person = try! JSONDecoder().decode(Person.self, from: json) let json = try! JSONEncoder().encode(person)
  7. Codable (Decodable & Encodable) [email protected] struct Person: Codable { private

    enum CodingKeys : String, CodingKey { case fullName = “full_name" ... } ... }
  8. Example [email protected] let json = """ { "name": "Hachi", "age":

    4, "owner": { "name": "Katsumi Kishikawa", "age": 36 } } """.data(using: .utf8)! let dog = try! JSONDecoder().decode(Dog.self, from: json) print(dog)
  9. Example (GitHub API) [email protected] { "total_count": 410867, "incomplete_results": false, "items":

    [ { "id": 1828795, "name": "AFNetworking", "full_name": "AFNetworking/AFNetworking", "owner": { "login": "AFNetworking", "id": 1181541, "avatar_url": "https://avatars1.githubusercontent.com/u/1181541?v=3", "gravatar_id": "", "url": "https://api.github.com/users/AFNetworking", "html_url": "https://github.com/AFNetworking", "followers_url": "https://api.github.com/users/AFNetworking/followers", "following_url": "https://api.github.com/users/AFNetworking/following{/other_user}", "gists_url": "https://api.github.com/users/AFNetworking/gists{/gist_id}", "starred_url": "https://api.github.com/users/AFNetworking/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/AFNetworking/subscriptions", "organizations_url": "https://api.github.com/users/AFNetworking/orgs", "repos_url": "https://api.github.com/users/AFNetworking/repos", "events_url": "https://api.github.com/users/AFNetworking/events{/privacy}", "received_events_url": "https://api.github.com/users/AFNetworking/received_events", "type": "Organization", "site_admin": false }, ... ] } curl -i https://api.github.com/search/repositories?q=language:objc
  10. [email protected] @objcMembers class Repository: Object { dynamic var identifier =

    0 dynamic var name = "" dynamic var fullName = "" dynamic var isPrivate = false dynamic var htmlUrl = "" dynamic var createdAt = Date() dynamic var updatedAt = Date() dynamic var owner: User? private enum CodingKeys : String, CodingKey { case identifier = "id" case name case fullName = "full_name" case isPrivate = "private" case htmlUrl = "html_url" case createdAt = "created_at" case updatedAt = "updated_at" case owner } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) identifier = try container.decode(Int.self, forKey: .identifier) name = try container.decode(String.self, forKey: .name) fullName = try container.decode(String.self, forKey: .fullName) isPrivate = try container.decode(Bool.self, forKey: .isPrivate) htmlUrl = try container.decode(String.self, forKey: .htmlUrl) createdAt = try container.decode(Date.self, forKey: .createdAt) updatedAt = try container.decode(Date.self, forKey: .updatedAt) owner = try container.decode(User.self, forKey: .owner) } }
  11. [email protected] @objcMembers class User: Object, Decodable { dynamic var login

    = "" dynamic var identifier = 0 dynamic var type = "" dynamic var avatarUrl = "" private enum CodingKeys : String, CodingKey { case login case identifier = "id" case type case avatarUrl = "avatar_url" } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) login = try container.decode(String.self, forKey: .login) identifier = try container.decode(Int.self, forKey: .identifier) type = try container.decode(String.self, forKey: .type) avatarUrl = try container.decode(String.self, forKey: .avatarUrl) } }
  12. [email protected] struct RepositoryResponse : Decodable { let totalCount: Int let

    incompleteResults: Bool let items: [Repository] private enum CodingKeys : String, CodingKey { case totalCount = "total_count" case incompleteResults = "incomplete_results" case items } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) totalCount = try container.decode(Int.self, forKey: .totalCount) incompleteResults = try container.decode(Bool.self, forKey: .incompleteResults) items = try container.decode([Repository].self, forKey: .items) } }
  13. [email protected] var components = URLComponents(string: "https://api.github.com/search/repositories")! components.queryItems = [ URLQueryItem(name:

    "q", value: "language:objc"), URLQueryItem(name: "sort", value: "stars"), URLQueryItem(name: "order", value: "desc") ] URLSession.shared.dataTask(with: URLRequest(url: components.url!)) { data, response, error in guard let data = data else { return } do { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let repositoryResponse = try decoder.decode(RepositoryResponse.self, from: data) let repositories = repositoryResponse.items ...
  14. KeyPath [email protected] @objcMembers class Dog: Object { dynamic var name

    = "" dynamic var age = 0 dynamic var owner: Person! } \Dog.age => KeyPath<Dog, Int> \Dog.name => KeyPath<Dog, String> \Dog.owner.name => KeyPath<Dog, String>
  15. KeyPath [email protected] dog.setValue("Pochi", forKeyPath: "name") let name = dog.value(forKeyPath: "name")

    let name = dog[keyPath: \Dog.name] dog[keyPath: \Dog.name] = "Pochi" let age = dog[keyPath: \Dog.age] dog[keyPath: \Dog.age] = 4
  16. Type-safe KVO [email protected] dog.addObserver(self, forKeyPath: "name", options: [], context: nil)

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "name" { ... } } dog.observe(\Dog.name) { (dog, change) in ... }
  17. Type-safe Query [email protected] extension Results { public func filter<P: Predicate>(_

    closure: () -> P) -> Results<T> where P.ObjectType == T { return filter(closure().predicate) } } public func == <RealmObject: Object>(lhs: KeyPath<RealmObject, String>, rhs: String) -> BasicPredicate<RealmObject> { return BasicPredicate<RealmObject>(format: "%K == %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as NSString]) } public func < <RealmObject: Object, RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: RealmProperty) -> BasicPredicate<RealmObject> { return BasicPredicate<RealmObject>(format: "%K < %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as! NSNumber]) } public func > <RealmObject: Object, RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: RealmProperty) -> BasicPredicate<RealmObject> { return BasicPredicate<RealmObject>(format: "%K > %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as! NSNumber]) } ...
  18. Type-safe Query [email protected] @objcMembers class Dog: Object { dynamic var

    name = "" dynamic var age = 0 dynamic var owner: Person! let owners = LinkingObjects(fromType: Person.self, property: "dogs") } @objcMembers class Person: Object { dynamic var name = "" dynamic var age = 0 dynamic var birthdate = Date() let dogs = List<Dog>() }
  19. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  20. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  21. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  22. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  23. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  24. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  25. Type-safe Query [email protected] realm.objects(Person.self) .filter { \Person.name == "Katsumi" }

    realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  26. t>(lhs: KeyPath<RealmObject, String>, rhs: String) -> ect>(format: "%K == %@",

    arguments: [lhs._kvcKeyPathString! as NSString, rhs , RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: ealmObject> { ect>(format: "%K < %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs , RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: ealmObject> { ect>(format: "%K > %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs [email protected]
  27. 102 Platforms State of the Union 402 What's New in

    Swift 212 What's New in Foundation [email protected] Reference
  28. UIKit • Drag & Drop • Browse Files • App

    Password Autofill • Larger Navigation Bar Titles • Safe Area Layout Guides [email protected]
  29. 102 Platforms State of the Union 201 What's New in

    Cocoa Touch 203 Introducing Drag and Drop [email protected] Reference
  30. 301 Introducing the New App Store 302 What's New in

    iTunes Connect 303 What's New in StoreKit 305 Advanced StoreKit [email protected] Reference
  31. 703 Introducing Core ML 710 Core ML in depth 506

    Vision Framework: Building on Core ML [email protected] Reference
  32. PDFKit • PDFView • PDFThumbnailView • PDFDocument • PDFPage •

    PDFOutline • PDFSelection • PDFAnnotation • PDFAction [email protected]
  33. [email protected] if let documentURL = Bundle.main.url(forResource: "...", withExtension: "pdf") {

    if let document = PDFDocument(url: documentURL) { pdfView.document = document pdfView.backgroundColor = UIColor.lightGray pdfView.autoScales = true pdfView.displayMode = .singlePageContinuous pdfView.displayDirection = .vertical PDFView
  34. [email protected] if let documentURL = Bundle.main.url(forResource: "...", withExtension: "pdf") {

    if let document = PDFDocument(url: documentURL) { ... pdfThumbnailView.thumbnailSize = CGSize(width: 50, height: 75) pdfThumbnailView.layoutMode = .horizontal pdfThumbnailView.pdfView = pdfView ... PDFThumbnailView
  35. [email protected] pdfView.displayMode = .singlePage pdfView.displayMode = .singlePageContinuous pdfView.displayMode = .twoUp

    pdfView.displayMode = .twoUpContinuous pdfView.displayDirection = .vertical pdfView.displayDirection = .horizontal PDFDisplayMode/PDFDisplayDirection
  36. [email protected] if let root = pdfDocument?.outlineRoot { var stack =

    [root] while !stack.isEmpty { let current = stack.removeLast() if let label = current.label, !label.isEmpty { var indentationLevel = -1 var parent = current.parent while let _ = parent { indentationLevel += 1 parent = parent?.parent } print(String(repeating: " ", count: indentationLevel) + label) } for i in (0..<current.numberOfChildren).reversed() { stack.append(current.child(at: i)) } } } Table of Contents (PDFOutline)
  37. [email protected] ͸͡Ίʹ ຊॻͷ಺༰ʹ͍ͭͯ TechBoosterͱ͸ ͓໰͍߹Θͤઌ ୈ1ষ ReVIEWೖ໳ 1.1 ReVIEWͱ͸Կ͔ 1.2

    ReVIEWͷಛ৭ 1.3 ReVIEWͷ޲͍͍ͯΔ෼໺ɺ޲͍͍ͯͳ͍෼໺ 1.4 ReVIEWͷ՝୊ 1.5 ·ͱΊ ୈ2ষ ؀ڥߏங 2.1 ReVIEW؀ڥͷߏ੒ 2.2 MacͰͷ؀ڥߏங 2.3 LinuxͰͷ؀ڥߏங 2.4 WindowsͰͷ؀ڥߏங ୈ3ষ ࣥචΛ࢝ΊΔ 3.1 ϓϩδΣΫτΛ࡞੒͢Δ Table of Contents (PDFOutline)
  38. [email protected] if let documentURL = Bundle.main.url(forResource: "...", withExtension: "pdf") {

    if let document = PDFDocument(url: documentURL) { ... document.delegate = self ... Draw custom contents
  39. [email protected] if let document = PDFDocument(url: documentURL) { if document.isEncrypted

    && document.unlock(withPassword: "...") { if document.permissionsStatus == .owner { // owner... } else { // user... if document.allowsCopying { ... } if document.allowsPrinting { ... } ... } } ... Open protected PDF
  40. [email protected] if document.pageCount > 0 { if let page =

    document.page(at: 0) { let text = page.string let attributedText = page.attributedString let annotations = page.annotations ... } } Extract text
  41. [email protected] if let documentURL = Bundle.main.url(forResource: "...", withExtension: "pdf") {

    if let document = PDFDocument(url: documentURL) { ... document.delegate = self ... Search text (Asynchronous)
  42. [email protected] func documentDidBeginPageFind(_ notification: Notification) { ... } func documentDidBeginDocumentFind(_

    notification: Notification) { ... } func documentDidEndDocumentFind(_ notification: Notification) { ... } func documentDidEndPageFind(_ notification: Notification) { ... } func documentDidFindMatch(_ notification: Notification) { let selection = notification.userInfo["PDFDocumentFoundSelection"] ... } func didMatchString(_ instance: PDFSelection) { instance.color = .yellow selections.append(instance) ... }