読者です 読者をやめる 読者になる 読者になる

のほほん停留所

つぶやきのなりそこないの溜まり場

Xcodeの検索機能まとめ

f:id:Nonchalanttan:20170326174253p:plain

Xcodeの検索機能には様々な機能があります。意外と知らない人も多いかもしれないのでまとめます。

検索範囲

f:id:Nonchalanttan:20170326174812p:plain

検索ボックスの下のプロジェクト名の部分を選択すると選択範囲をファルダ/ファイル単位で設定ができます

大文字と小文字の区別

f:id:Nonchalanttan:20170326174355p:plain

Ignoring Case

大文字と小文字を区別しないで検索

Matching Case

大文字と小文字を区別して検索

一致条件

f:id:Nonchalanttan:20170326174947p:plain

Containing

部分一致

Matching

完全一致

Starting With

前方一致

Ending With

後方一致

検索対象・検索方法

f:id:Nonchalanttan:20170326175111p:plain

Text

文字列検索

Reference

f:id:Nonchalanttan:20170326180625p:plain

プロパティ名やクラス名などを対象に検索

Definitions

f:id:Nonchalanttan:20170326180636p:plain

検索範囲内で定義されたプロパティ名やクラス名などを対象に検索

Regular Expression

正規表現

Call Hierarchy

f:id:Nonchalanttan:20170326182427p:plain

メソッド名やプロパティ名の依存関係

特殊文字の検索

f:id:Nonchalanttan:20170326183807p:plain f:id:Nonchalanttan:20170326183815p:plain

検索ボックスの虫眼鏡アイコンをタップして、Insert Patternを押すと特殊文字を検索ボックスに追加できます

ファイル名の検索

f:id:Nonchalanttan:20170326184228p:plain

Command + Shift + O を押すとファイル名の検索窓を表示できます

おまけ

f:id:Nonchalanttan:20170326180843p:plain

コード内でも右クリックで検索できます。上に挙げたような様々な検索方法が呼び出せます

minHeightを保ちつつContentViewに合わせたUIScrollViewをStoryboard上で定義する

やりたいこと

デフォルトは画面の高さで、中の要素が画面の高さを越えたらスクロールするViewを設定したい

  • 中の要素が画面の高さに収まる場合 f:id:Nonchalanttan:20170325015352p:plain
  • 中の要素が画面の高さに収まらない場合 f:id:Nonchalanttan:20170325015301p:plain

Storyboardの設定

f:id:Nonchalanttan:20170325014416p:plain

  1. UIScrollViewを追加する
    • UIScrollViewの上下左右の制約を画面に合わせる f:id:Nonchalanttan:20170325014524p:plain
  2. UIScrollViewの中にUIViewを追加する
    • UIViewの上下左右の制約をUIScrollViewに合わせる。Paddingを設定したければ、ここでConstantを設定する
    • UIViewの高さ・幅の制約をUIScrollViewに合わせる。Paddingを設定している場合は逆算した値をConstantに設定する(ここではPaddingを上下左右でそれぞれ8で設定しているので、逆算した値は-16になる) f:id:Nonchalanttan:20170325014608p:plain
  3. UIViewの中にUILabelを追加する
    • UILabelの上下左右の制約をUIViewに合わせる。Paddingを設定したければ、ここでConstantを設定する f:id:Nonchalanttan:20170325014738p:plain
  4. [重要] UIViewの高さの制約のremove at build timeを有効にする!
    • これを設定することで、中の要素が画面の高さを越えたらスクロール可能になる f:id:Nonchalanttan:20170325014918p:plain
  5. [重要] UIViewにgreaterThanOrEqualToの高さの制約を設定する!
    • これを設定することで、UIViewの高さの最小値を設定することができる f:id:Nonchalanttan:20170325015101p:plain

上の手順を踏めば、コードを書かずに中の要素に合わせて伸縮するScrollViewを実現できます! 本当はUIViewの高さのEqual制約は設定しなくても動くのですが、Storyboard上でエラーが出てしまうので、それを回避するためにremove at build timeを有効にした制約を設定しています。

おまけ

remove at build timeを有効にした制約はStoryboard上では普通の制約とは異なって表現されます。

f:id:Nonchalanttan:20170325015512p:plain

お分かりいただけたでしょうか?制約を表す線が青ではなくて黒になってますね!わかりやすい! :thinking_face:

UILabelの改行まとめ

UILabelの改行の種類をいつも調べてる気がするので備忘録代わりにまとめた

let label = UILabel(frame: .zero)
label.lineBreakMode = .byWordWrapping

lineBreakModeの型であるNSLineBreakModeのドキュメントを見ると、改行方法は6種類ある

NSLineBreakMode - UIKit | Apple Developer Documentation

byWordWrapping

Wrapping occurs at word boundaries, unless the word itself doesn’t fit on a single line. See Characters and Grapheme Clusters in String Programming Guide for a discussion of issues related to determining word boundaries.

f:id:Nonchalanttan:20170317195406p:plain

単語の境界で折り返す

byCharWrapping

Wrapping occurs before the first character that doesn’t fit.

f:id:Nonchalanttan:20170317200447p:plain

文字で折り返す

byClipping

Lines are simply not drawn past the edge of the text container.

f:id:Nonchalanttan:20170317200325p:plain

折り返さず、端を超えた文字は表示されない

byTruncatingHead

The line is displayed so that the end fits in the container and the missing text at the beginning of the line is indicated by an ellipsis glyph. Although this mode works for multiline text, it is more often used for single line text.

f:id:Nonchalanttan:20170317200802p:plain

文字列末尾が表示され、行の先頭に三点リーダ

byTruncatingTail

The line is displayed so that the beginning fits in the container and the missing text at the end of the line is indicated by an ellipsis glyph. Although this mode works for multiline text, it is more often used for single line text.

f:id:Nonchalanttan:20170317200954p:plain

文字列先頭が表示され、行の末尾に三点リーダ

byTruncatingMiddle

The line is displayed so that the beginning and end fit in the container and the missing text in the middle is indicated by an ellipsis glyph. This mode is used for single-line layout; using it with multiline text truncates the text into a single line.

f:id:Nonchalanttan:20170317201055p:plain

文字列先頭と末尾が表示され、中央に三点リーダ

「Swiftデザインパターン」に出てきたパターンまとめ

「Swiftデザインパターン」をパラパラと読んだので、せっかくなので出てきたパターンをまとめてみた。基本的に概要と実装しか書いていないので、細かいところは書籍を参照していただければ。

https://www.amazon.co.jp/Swift-Programmers-SELECTION/dp/4798142492

生成に関するパターン

Prototypeパターン

プロトタイプ と呼ばれる既存のオブジェクトをコピーすることで、新しいオブジェクトを作成するデザインパターンである。これにより、オブジェクトを使用するコンポーネントからオブジェクト作成するコードを隠蔽できる。

Classだと参照渡しになるが、NSCopyingプロトコルに準拠させてcopy()を呼ぶとディープコピーが可能になる。

class Appointment: NSObject, NSCopying {
    var name: String
    var day: String
    var place: String

    func printDetails(label: String) {
        print("\(label) with \(name) on \(day) at \(place)")
    }

    func copyWithZone(zone: NSZone) -> AnyObject {
        return Appointment(name: self.name, day: self.day, place: self.place)
    }
}

var beerMeeting = Appointment(name: "Bob", day: "Mon", place: "Joe's Bar")

var workMeeting = beerMeeting.copy() as! Appointment
workMeeting.name = "Alice"
workMeeting.day = "Fri"
workMeeting.place = "Conference Rm 2"

beerMeeting.printDetails(label: "Social") // Social with Bob on Mon at Joe's Bar
workMeeting.printDetails(label: "Work")  // Work with Alice on Fri at Conference Rm 2

Singletonパターン

アプリケーションにおいて特定の型のオブジェクトを1つだけ存在させるデザインパターンである。オブジェクトを作成しても利用可能な現実のリソースが増えない、またはロギングといったアクティビティを統一したい場合に使用される。

class Logger {
    private var data = [String]()

    private init() {

    }

    private func doLog(msg: String) {
        data.append(msg)
    }

    private func doPrintLog() {
        for msg in data {
            print("Log: \(msg)")
        }
    }

    func log(msg: String) {
        Logger.sharedInstance.log(msg: msg)
    }

    func printLog() {
        Logger.sharedInstance.printLog()
    }

    private class var sharedInstance: Logger {
        get {
            struct SingletonWrapper {
                static let singleton = Logger()
            }
            return SingletonWrapper.singleton
        }
    }
}

Object Poolパターン

単一のインスタンスではなく、複数の同じ型のオブジェクトへのアクセスを可能にするデザインパターンである。まったく同じオブジェクトがいくつかあり、新しいインスタンスの生成にコストがかかるために、それらの作成を管理しなければならない場合に使用される。CocoaフレームワークのUITableViewCellの再利用などが代表例である。

class Pool<T> {
    private var data = [T]()

    init(items: [T]) {
        data.reserveCapacity(items.count)
        data.append(contentsOf: items)
    }

    func getFromPool() -> T? {
        guard !data.isEmpty else {
            return nil
        }
        return self.data.remove(at: 0)
    }
}

Factory Methodパターン

共通のプロトコルを実装する、または同じベースクラスを共有するクラスの中からどれかを選択できる場合に使用されるデザインパターンである。

class RentalCar {
    private var nameBV: String
    private var passengersBV: Int
    private var priceBV: Float

    init(name: String, passengers: Int, price: Float) {
        self.nameBV = name
        self.passengersBV = passengers
        self.priceBV = price
    }

    final var name: String {
        return nameBV
    }

    final var passengers: Int {
        return passengersBV
    }

    final var price: Float {
        return priceBV
    }

    class func createRentalCar(passengers: Int) -> RentalCar? {
        switch passengers {
        case 0...3:
            return Compact()
        case 4...8:
            return SUV()
        default:
            return nil
        }
    }
}

class Compact: RentalCar {
    init() {
        super.init(name: "VM Golf", passengers: 3, price: 20)
    }
}

class SUV: RentalCar {
    init() {
        super.init(name: "Cadillac Escalade", passengers: 8, price: 75)
    }
}

Abstract Factoryパターン

Factory Methodパターンに似ているが、呼び出し元のコンポーネントが関連するオブジェクトのファミリまたはグループを取得できるという違いがある。

class CarFactory {

    func createFloorplan() -> Floorplan {
        fatalError("Not implemented")
    }

    func createSuspension() -> Suspension {
        fatalError("Not implemented")
    }

    func createDrivetrain() -> Drivetrain {
        fatalError("Not implemented")
    }

    final class func getFactory(cars: Cars) -> CarFactory? {
        switch cars {
        case .compact:
            return CompactCarFactory()
        case .suv:
            return SUVCarFactory()
        }
    }
}

class CompactCarFactory: CarFactory {
    override func createFloorplan() -> Floorplan {
        return StandardFloorplan()
    }

    override func createSuspension() -> Suspension {
        return RoadSuspension()
    }

    override func createDrivetrain() -> Drivetrain {
        return FrontWheelDrive()
    }
}

class SUVCarFactory: CarFactory {
    override func createFloorplan() -> Floorplan {
        return LongFloorplan()
    }

    override func createSuspension() -> Suspension {
        return OffRoadSuspension()
    }

    override func createDrivetrain() -> Drivetrain {
        return AllWheelDrive()
    }
}

let factory = CarFactory.getFactory(cars: Cars.compact)

if let factory = factory {
    let car = Car(carType: Cards.compact, floor: factory.createFloorplan(), suspension: factory.createSuspension(), drive: factory.createDrivetrain())
}

Builderパターン

オブジェクトの設定をその作成から切り離すために使用されるデザインパターンである。

class Burger {
    let customerName: String
    let veggieProduct: Bool
    let patties: Int
    let pickles: Bool
    let mayo: Bool
    var ketchup: Bool
    let lettuce: Bool
    let cook: Cooked

    enum Cooked: String {
        case rare = "Rare"
        case normal = "Normal"
        case welldone = "Well Done"
    }

    init(name: String, veggie: Bool, patties: Int, pickles: Bool, mayo: Bool, ketchup: Bool, lettuce: Bool, cook: Cooked) {
        self.customerName = name
        self.veggieProduct = veggie
        self.patties = patties
        self.pickles = pickles
        self.mayo = mayo
        self.ketchup = ketchup
        self.lettuce = lettuce
        self.cook = cook
    }
}

class BurgerBuilder {
    private var veggie = false
    private var pickles = true
    private var mayo = true
    private var ketchup = true
    private var lettuce = true
    private var cooked = Burger.Cooked.normal
    private var patties = 2

    func setVeggie(choice: Bool) { self.veggie = choice }
    func setPickles(choice: Bool) { self.pickles = choice }
    func setMayo(choice: Bool) { self.mayo = choice }
    func setKetchup(choice: Bool) { self.ketchup = choice }
    func setLettuce(choice: Bool) { self.lettuce = choice }
    func setCooked(choice: Burger.Cooked) { self.cooked = choice }
    func setPatties(choice: Int) { self.patties = choice }

    func buildObject(name: String) -> Burger {
        return Burger(name: name, veggie: veggie, patties: patties, pickles: pickles, mayo: mayo, ketchup: ketchup, lettuce: lettuce, cook: cooked)
    }
}

構造に関するパターン

Adapterパターン

関連する機能を提供する2つのオブジェクトをーそれらのAPIに互換性がなかったとしてもー組み合わせることができる。

SalesDataSource, NewCoStaffMemberはそれぞれに互換性がないが、NewCoStaffMemberにEmployeeDataSourceを適用したExtensionを生やして組み合わせることを可能にしている。またラッパークラスを用意することでも組み合わせることを可能にできる。

struct Employee {
    var name: String
    var title: String
}

protocol EmployeeDataSource {
    var employees: [Employee] { get }
    func searchByName(name: String) -> [Employee]
    func searchByTitle(title: String) -> [Employee]
}

class DataSourceBase: EmployeeDataSource {
    var employees = [Employee]()

    func searchByName(name: String) -> [Employee] {
        return search(selector: { e -> Bool in
            return e.title.range(of: name) != nil
        })
    }

    func searchByTitle(title: String) -> [Employee] {
        return search(selector: { e -> Bool in
            return e.title.range(of: title) != nil
        })
    }

    private func search(selector: (Employee) -> Bool) -> [Employee] {
        var results = [Employee]()
        for e in employees {
            if selector(e) {
                results.append(e)
            }
        }
        return results
    }
}

class SalesDataSource: DataSourceBase {
    override init() {
        super.init()
        employees.append(Employee(name: "Alice", title: "VP of Sales"))
        employees.append(Employee(name: "Bob", title: "Account Exec"))
    }
}

class NewCoStaffMember {
    private var name: String
    private var role: String

    init(name: String, role: String) {
        self.name = name
        self.role = role
    }

    func getName() -> String {
        return name
    }

    func getJob() -> String {
        return role
    }
}

class NewCoDirectory {
    private var staff: [String: NewCoStaffMember]

    init() {
        staff = [
            "Hans": NewCoStaffMember(name: "Hans", role: "Corp Counsel"),
            "Greta": NewCoStaffMember(name: "Greta", role: "VP, Legal")
        ]
    }

    func getStaff() -> [String: NewCoStaffMember] {
        return staff
    }
}

extension NewCoDirectory: EmployeeDataSource {
    var employees: [Employee] {
        return getStaff().values.map { sv -> Employee in
            return Employee(name: sv.getName(), title: sv.getJob())
        }
    }

    func searchByName(name: String) -> [Employee] {
        return createEmployees() {
            return $0.getName().range(of: name) != nil
        }
    }

    func searchByTitle(title: String) -> [Employee] {
        return createEmployees() {
            return $0.getJob().range(of: title) != nil
        }
    }

    private func createEmployees(filter filterClosure: @escaping ((NewCoStaffMember) -> Bool)) -> [Employee] {
        return getStaff().values
            .filter { filterClosure($0) }
            .map { Employee(name: $0.getName(), title: $0.getJob())}
    }
}

Bridgeパターン

Adapterパターンと同じように見えるが、Adapterパターンとの最大の相違点は意図であり、実装ではない。

主に「クラス階層の爆発」などの問題を解決する。

protocol ClearMessageChannel {
    func send(message: String)
}

protocol SecureMessageChannel {
    func sendEncryptedMessage(encryptedText: String)
}

class Communicator {
    private let clearChannel: ClearMessageChannel
    private let secureChannel: SecureMessageChannel

    init(clearChannel: ClearMessageChannel,
         secureChannel: SecureMessageChannel) {
        self.clearChannel = clearChannel
        self.secureChannel = secureChannel
    }

    func sendCleartextMessage(message: String) {
        self.clearChannel.send(message: message)
    }

    func sendSecureMessage(message: String) {
        self.secureChannel.sendEncryptedMessage(encryptedText: message)
    }
}

protocol Message {
    init(message: String)
    func prepareMessage()
    var contentToSend: String { get }
}

class ClearMessage: Message {
    private var message: String

    required init(message: String) {
        self.message = message
    }
    func prepareMessage() {
         // do nothing.
    }

    var contentToSend: String {
        return message
    }
}

class EncryptedMessage: Message {
    private var clearText: String
    private var cipherText: String?

    required init(message: String) {
        self.clearText = message
    }

    func prepareMessage() {
        cipherText = String(clearText.characters.reversed())
    }

    var contentToSend: String {
        return cipherText!
    }
}

protocol Channel {
    func sendMessage(msg: Message)
}

class LandlineChannel: Channel {
    func sendMessage(msg: Message) {
        print("Landline: \(msg.contentToSend)")
    }
}

class WirelessChannel: Channel {
    func sendMessage(msg: Message) {
        print("Wireless: \(msg.contentToSend)")
    }
}

class CommunicatorBridge: ClearMessageChannel, SecureMessageChannel {
    private var channel: Channel

    init(channel: Channel) {
        self.channel = channel
    }

    func send(message: String) {
        let msg = ClearMessage(message: message)
        sendMessage(msg: msg)
    }

    func sendEncryptedMessage(encryptedText: String) {
        let msg = EncryptedMessage(message: encryptedText)
        sendMessage(msg: msg)
    }

    private func sendMessage(msg: Message) {
        msg.prepareMessage()
        channel.sendMessage(msg: msg)
    }
}

var bridge = CommunicatorBridge(channel: LandlineChannel())
var comms = Communicator(clearChannel: bridge, secureChannel: bridge)

comms.sendCleartextMessage(message: "Hello!")
comms.sendSecureMessage(message: "This is a secret")

Decoratorパターン

オブジェクトを作成するために使用されるクラスを変更せずに、個々のサブジェクトの振る舞いを変更できるようにするパターンである。

通常は、クラスを直接変更するほうが簡単なのだが、サードパーティーのライブラリなどの理由で変更が困難な場合に有効である。

class Purchase {
    private let product: String
    private let price: Float

    init(product: String, price: Float) {
        self.product = product
        self.price = price
    }

    var description: String {
        return product
    }

    var totalPrice: Float {
        return price
    }
}

class BasePruchaseDecorator: Purchase {
    init(purchase: Purchase) {
        super.init(product: purchase.description, price: purchase.totalPrice)
    }
}

class PurchaseWithGiftWrap: BasePruchaseDecorator {
    override var description: String {
        return "\(super.description) + giftwrap"
    }
    override var totalPrice: Float {
        return super.totalPrice + 2
    }
}

class PurchaseWithRibbon: BasePruchaseDecorator {
    override var description: String {
        return "\(super.description) + ribbon"
    }
    override var totalPrice: Float {
        return super.totalPrice + 1
    }
}

let purchase = PurchaseWithRibbon(purchase:
                    PurchaseWithGiftWrap(purchase:
                        Purchase(product: "Sunglasses", price: 25)))

print(purchase.description) // Sunglasses + giftwrap + ribbon
print(purchase.totalPrice)  // 28.0

Compositeパターン

個々のオブジェクトとオブジェクトのコレクションからなるツリーを一貫した方法で扱えるようにするパターンである。

protocol CarPart {
    var name: String { get }
    var price: Float { get }
}

class Part: CarPart {
    let name: String
    let price: Float

    init(name: String, price: Float) {
        self.name = name
        self.price = price
    }
}

class CompositePart: CarPart {
    let name: String
    let parts: [CarPart]

    init(name: String, parts: [CarPart]) {
        self.name = name
        self.parts = parts
    }

    var price: Float {
        return parts.reduce(0) { $0 + $1.price }
    }
}

Facadeパターン

共通のタスクを実行するための複雑なAPIの使用を単純にするパターンである。

class TreasureMap {
    enum Treasures {
        case galleon
        case buried_gold
        case sunken_jewels
    }

    struct MapLocation {
        let gridLetter: Character
        let gridNumber: UInt
    }

    func findTreasure(type: Treasures) -> MapLocation {
        switch type {
        case .galleon:
            return MapLocation(gridLetter: "D", gridNumber: 6)
        case .buried_gold:
            return MapLocation(gridLetter: "C", gridNumber: 2)
        case .sunken_jewels:
            return MapLocation(gridLetter: "F", gridNumber: 12)
        }
    }
}

class PirateShip {
    struct ShipLocation {
        let NorthSourh: Int
        let EastWest: Int
    }

    var currentPosition: ShipLocation
    var movementQueue = DispatchQueue(label: "shipQ")

    init() {
        currentPosition = ShipLocation(NorthSourh: 5, EastWest: 5)
    }

    func moveToLocation(location: ShipLocation, callback: @escaping (ShipLocation) -> Void) {
        movementQueue.async {
            self.currentPosition = location
            callback(self.currentPosition)
        }
    }
}

class PirateCrew {
    let workQueue = DispatchQueue(label: "crewWorkQ")

    enum Actions {
        case attackShip
        case digForGold
        case diveForJewels
    }

    func performAction(action: Actions, callback: @escaping (Int) -> Void) {
        workQueue.async {
            var prizeValue = 0
            switch action {
            case .attackShip:
                prizeValue = 10000
            case .digForGold:
                prizeValue = 5000
            case .diveForJewels:
                prizeValue = 1000
            }
            callback(prizeValue)
        }
    }
}

enum TreasureTypes {
    case ship
    case buried
    case sunken
}

class PirateFacade {
    private let map = TreasureMap()
    private let ship = PirateShip()
    private let crew = PirateCrew()

    func getTreasure(type: TreasureTypes) -> Int? {
        var prizeAmount: Int?

        var treasureMapType: TreasureMap.Treasures
        var crewWorkType: PirateCrew.Actions

        switch type {
        case .ship:
            treasureMapType = .galleon
            crewWorkType = .attackShip
        case .buried:
            treasureMapType = .buried_gold
            crewWorkType = .digForGold
        case .sunken:
            treasureMapType = .sunken_jewels
            crewWorkType = .diveForJewels
        }

        let treasureLocation = map.findTreasure(type: treasureMapType)

        let sequence: [Character] = ["A", "B", "C", "D", "E", "F", "G"]
        let eastWestPos = sequence.enumerated().filter({ $0.1 == treasureLocation.gridLetter}).map { $0.0 }.first!
        let shipTarget = PirateShip.ShipLocation(NorthSourh: Int(treasureLocation.gridNumber), EastWest: eastWestPos)

        let semaphore = DispatchSemaphore(value: 0)

        ship.moveToLocation(location: shipTarget) { location in
            self.crew.performAction(action: crewWorkType, callback: { prize in
                prizeAmount = prize
                semaphore.signal()
            })
        }

        semaphore.wait(timeout: .distantFuture)
        return prizeAmount
    }
}

Flyweightパターン

複数の呼び出し元のコンポーネントに同じデータオブジェクトを共有させるパターンである。

class Owner: NSObject, NSCopying {
    var name: String
    var city: String

    init(name: String, city: String) {
        self.name = name
        self.city = city
    }

    func copy(with zone: NSZone? = nil) -> Any {
        return Owner(name: self.name, city: self.city)
    }
}

class FlyweightFactory {
    class func createFlyweight() -> Flyweigt {
        return Flyweigt(owner: ownerSingleton)
    }

    private class var ownerSingleton: Owner {
        get {
            struct SingletonWrapper {
                static let singleton = Owner(name: "Anonymous", city: "Anywhere")
            }
            return SingletonWrapper.singleton
        }
    }
}

class Flyweigt {
    private let extrinsicOwner: Owner
    private var intrinsicOwner: Owner?

    init(owner: Owner) {
        self.extrinsicOwner = owner
    }

    var name: String {
        get {
            return intrinsicOwner?.name ?? extrinsicOwner.name
        }
        set (value) {
            decoupleFromExtrinsic()
            intrinsicOwner?.name = value
        }
    }

    var city: String {
        get {
            return intrinsicOwner?.city ?? extrinsicOwner.city
        }
        set (value) {
            decoupleFromExtrinsic()
            intrinsicOwner?.city = value
        }
    }

    private func decoupleFromExtrinsic() {
        if intrinsicOwner == nil {
            intrinsicOwner = extrinsicOwner.copy(with: nil) as? Owner
        }
    }
}

Proxyパターン

オブジェクトを別オブジェクトまたはリソースへのインターフェイスとして機能させる必要がある場合に使用される。

protocol HttpHeaderRequest {
    func getHeader(url: String, header: String) -> String?
}

class HttpHeaderRequestProxy: HttpHeaderRequest {
    private let semaphore = DispatchSemaphore(value: 0)

    func getHeader(url urlString: String, header: String) -> String? {
        var headerValue: String?

        let url = URL(string: urlString)
        let request = URLRequest(url: url!)
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let httpResponse = response as? HTTPURLResponse {
                headerValue = httpResponse.allHeaderFields[header] as? String
            }
            self.semaphore.signal()
        }.resume()
        semaphore.wait(timeout: .distantFuture)
        return headerValue
    }
}

let url = "http://www.apress.com"
let headers = ["Content-Length", "Content-Encoding"]

let proxy = HttpHeaderRequestProxy()

for header in headers {
    if let val = proxy.getHeader(url: url, header: header) {
        print("\(header): \(val)")
    }
}

FileHandle.standardInput.availableData

振る舞いに関するパターン

Chain of Responsibilityパターン

呼び出し元のコンポーネントからのリクエストを処理することが可能な複数のオブジェクトを数珠つなぎにするパターンである。

struct Message {
    let from: String
    let to: String
    let subject: String
}

class Transmitter {
    var nextLink: Transmitter?

    required init() {}

    func sendMessage(message: Message) {
        guard let nextLink = nextLink else {
            print("End of chain reached. Message not send.")
            return
        }
        nextLink.sendMessage(message: message)
    }

    class func createChain() -> Transmitter? {
        let transmitterClasses: [Transmitter.Type] = [
            PriorityTransmitter.self,
            LocalTransmitter.self,
            RemoteTransmitter.self
        ]

        var link: Transmitter?

        for tClass in transmitterClasses.reversed() {
            let existingLink = link
            link = tClass.init()
            link?.nextLink = existingLink
        }

        return link
    }

    fileprivate class func matchEmailSuffix(message: Message) -> Bool {
        guard let index = message.from.range(of: "@") else {
            return false
        }
        return message.to.hasSuffix(message.from.substring(with: Range(uncheckedBounds: (lower: index.lowerBound, upper: message.from.endIndex))))
    }
}

class LocalTransmitter: Transmitter {
    override func sendMessage(message: Message) {
        if Transmitter.matchEmailSuffix(message: message) {
            print("Message to \(message.to) sent locally")
        } else {
            super.sendMessage(message: message)
        }
    }
}

class RemoteTransmitter: Transmitter {
    override func sendMessage(message: Message) {
        if !Transmitter.matchEmailSuffix(message: message) {
            print("Message to \(message.to) sent remotely")
        } else {
            super.sendMessage(message: message)
        }
    }
}

class PriorityTransmitter: Transmitter {
    override func sendMessage(message: Message) {
        if message.subject.hasPrefix("Priority") {
            print("Message to \(message.to) sent as priority")
        } else {
            super.sendMessage(message: message)
        }
    }
}

let messages = [
    Message(from: "bob@example.com", to: "joe@example.com", subject: "Free for lunch?"),
    Message(from: "joe@example.com", to: "alice@acme.com", subject: "Nex Contracts"),
    Message(from: "pete@example.com", to: "all@example.com", subject: "Priority: All-Hands Meeting")
]

if let chain = Transmitter.createChain() {
    for msg in messages {
        chain.sendMessage(message: msg)
    }
}

/*
 Message to joe@example.com sent locally
 Message to alice@acme.com sent remotely
 Message to all@example.com sent as priority
 */

Commandパターン

オブジェクトのメソッドを呼び出す方法をカプセル化し、そのメソッドを別のタイミングで呼び出せるようにするか、異なるコンポーネントで呼び出せるようにするために使用されるパターンである。

class Calculator {
    private(set) var total = 0
    private var hisotory = [Command]()

    func add(_ amount: Int) {
        addUndoCommand(method: Calculator.subtract, amount: amount)
        total += amount
    }

    func subtract(_ amount: Int) {
        addUndoCommand(method: Calculator.add, amount: amount)
        total -= amount
    }

    func multiply(_ amount: Int) {
        addUndoCommand(method: Calculator.divide, amount: amount)
        total *= amount
    }

    func divide(_ amount: Int) {
        addUndoCommand(method: Calculator.multiply, amount: amount)
        total /= amount
    }

    private func addUndoCommand(method: @escaping (Calculator) -> (Int) -> Void, amount: Int) {
        hisotory.append(GenericsCommand.createCommand(receiver: self, instructions: { calc in
            method(calc)(amount)
        }))
    }

    func undo() {
        guard !hisotory.isEmpty else {
            return
        }

        hisotory.removeLast().execute()
        hisotory.removeLast()
    }
}

protocol Command {
    func execute()
}

class GenericsCommand<T>: Command {
    private var receiver: T
    private var instructions: (T) -> Void

    init(receiver: T, instructions: @escaping (T) -> Void) {
        self.receiver = receiver
        self.instructions = instructions
    }

    func execute() {
        instructions(receiver)
    }

    class func createCommand(receiver: T, instructions: @escaping (T) -> Void) -> Command {
        return GenericsCommand(receiver: receiver, instructions: instructions)
    }
}

let calc = Calculator()
calc.add(10)
calc.multiply(4)
calc.subtract(2)

print("Total: \(calc.total)") // Total: 38

for _ in 0..<3 {
    calc.undo()
    print("Undo called. Total: \(calc.total)")
}

// Undo called: Total: 40
// Undo called: Total: 10
// Undo called: Total: 0

Mediatorパターン

オブジェクトのグループ同士のやり取りを単純かつ合理的なものにするために使用される。

struct Position {
    var distanceFromRunway: Int
    var height: Int
}

protocol Peer {
    var name: String { get }
    func otherPlaneDidChangePosition(position: Position) -> Bool
}

protocol Mediator {
    func registerPeer(peer: Peer)
    func unregisterPeer(peer: Peer)
    func changePosition(peer: Peer, pos: Position) -> Bool
}

class AirplaneMediator: Mediator {
    private var peers: [String: Peer] = [:]

    func registerPeer(peer: Peer) {
        peers[peer.name] = peer
    }

    func unregisterPeer(peer: Peer) {
        peers.removeValue(forKey: peer.name)
    }

    func changePosition(peer: Peer, pos: Position) -> Bool {
        for storedPeer in peers.values {
            if peer.name != storedPeer.name && storedPeer.otherPlaneDidChangePosition(position: pos) {
                return true
            }
        }
        return false
    }
}

class Airplane: Peer {
    var name: String
    var currentPosition: Position
    var mediator: Mediator

    init(name: String, initiailPos: Position, mediator: Mediator) {
        self.name = name
        self.currentPosition = initiailPos
        self.mediator = mediator
        mediator.registerPeer(peer: self)
    }

    func otherPlaneDidChangePosition(position: Position) -> Bool {
        return position.distanceFromRunway == self.currentPosition.distanceFromRunway &&
            abs(position.height - self.currentPosition.height) < 1000
    }

    func changePosition(newPosition: Position) {
        currentPosition = newPosition
        guard !mediator.changePosition(peer: self, pos: self.currentPosition) else {
            print("\(name): Too close! Abort!")
            return
        }
        print("\(name): Position changed")
    }

    func land() {
        currentPosition = Position(distanceFromRunway: 0, height: 0)
        mediator.unregisterPeer(peer: self)
        print("\(name): Landed")
    }
}

let mediator = AirplaneMediator()

let british = Airplane(name: "BA706", initiailPos: Position(distanceFromRunway: 11, height: 21000), mediator: mediator)
let american = Airplane(name: "AA101", initiailPos: Position(distanceFromRunway: 12, height: 22000), mediator: mediator)

british.changePosition(newPosition: Position(distanceFromRunway: 8, height: 10000))
british.changePosition(newPosition: Position(distanceFromRunway: 2, height: 5000))
british.changePosition(newPosition: Position(distanceFromRunway: 1, height: 1000))

let cathay = Airplane(name: "CX200", initiailPos: Position(distanceFromRunway: 13, height: 22000), mediator: mediator)

british.land()

cathay.changePosition(newPosition: Position(distanceFromRunway: 12, height: 22000))


// BA706: Position changed
// BA706: Position changed
// BA706: Position changed
// BA706: Landed
// CX200: Too close! Abort!

Observerパターン

Observerパターンは、オブジェクトでの変化に関する通知の受け取りを別のオブジェクトが登録できるようにするパターン

protocol Observer: class {
    func notifiy(user: String, success: Bool)
}

protocol Subject {
    func addObservers(observers: [Observer])
    func removeObserver(observer: Observer)
}

class SubjectBase: Subject {
    private var observers = [Observer]()
    private var collectionQueue = DispatchQueue(label: "colQ", attributes: .concurrent)

    func addObservers(observers: [Observer]) {
        collectionQueue.async {
            for newOb in observers {
               self.observers.append(newOb)
            }
        }
    }

    func removeObserver(observer: Observer) {
        collectionQueue.async {
            self.observers = self.observers.filter({ $0 !== observer })
        }
    }

    func sendNotification(user: String, success: Bool) {
        collectionQueue.sync {
            for ob in self.observers {
                ob.notifiy(user: user, success: success)
            }
        }
    }
}

class AuthenticationManager: SubjectBase {
    func authenticate(user: String, pass: String) -> Bool {
        var result = false
        if user == "bob" && pass == "secret" {
            result = true
            print("User \(user) is authenticated")
        } else {
            print("Failed authentication attempt")
        }
        sendNotification(user: user, success: result)
        return result
    }
}

class ActivityLog: Observer {
    func notifiy(user: String, success: Bool) {
        print("Auth request for \(user). Success: \(success)")
    }

    func logActivity(activity: String) {
        print("Log: \(activity)")
    }
}

class FileCache: Observer {
    func notifiy(user: String, success: Bool) {
        if success {
            loadFiles(user: user)
        }
    }

    func loadFiles(user: String) {
        print("Load files for \(user)")
    }
}

class AttackMonitor: Observer {
    func notifiy(user: String, success: Bool) {
        monitorSubpiciousActivity = !success
    }

    var monitorSubpiciousActivity: Bool = false {
        didSet {
            print("Monitoring for attack: \(monitorSubpiciousActivity)")
        }
    }
}

let monitor = AttackMonitor()
let log = ActivityLog()
let cache = FileCache()

let authManager = AuthenticationManager()
authManager.addObservers(observers: [log, cache, monitor])

authManager.authenticate(user: "bob", pass: "secret")
print("-----")
authManager.authenticate(user: "joe", pass: "shhh")

// User bob is authenticated
// Auth request for bob. Success: true
// Load files for bob
// Monitoring for attack: false
// -----
// Failed authentication attempt
// Auth request for joe. Success: false
// Monitoring for attack: true

Mementoパターン

オブジェクトの完全な状態をメメントに取り込み、あとからオブジェクトをリセットをするために使用できるようにするパターンである。

protocol Memento {}

protocol Originator {
    func createMemento() -> Memento
    func applyMemento(memento: Memento)
}

class LedgerEntry {
    let id: Int
    let counterParty: String
    let amount: Float

    init(id: Int, counterParty: String, amount: Float) {
        self.id = id
        self.counterParty = counterParty
        self.amount = amount
    }
}

class LedgerMemento: Memento {
    private var entries: [LedgerEntry] = []
    private let total: Float
    private let nextId: Int

    init(ledger: Ledger) {
        self.entries = ledger.entries.values.map { $0 }
        self.total = ledger.total
        self.nextId = ledger.nextId
    }

    func apply(ledger: Ledger) {
        ledger.total = self.total
        ledger.nextId = self.nextId
        ledger.entries.removeAll(keepingCapacity: true)
        for entry in self.entries {
            ledger.entries[entry.id] = entry
        }
    }
}

class Ledger: Originator {
    fileprivate var entries: [Int: LedgerEntry] = [:]
    fileprivate var nextId = 1
    var total: Float = 0

    func addEntry(counterParty: String, amount: Float) {
        let entry = LedgerEntry(id: nextId, counterParty: counterParty, amount: amount)
        nextId += 1
        entries[entry.id] = entry
        total += amount
    }

    func createMemento() -> Memento {
        return LedgerMemento(ledger: self)
    }

    func applyMemento(memento: Memento) {
        if let m = memento as? LedgerMemento {
            m.apply(ledger: self)
        }
    }

    func printEntries() {
        for id in entries.keys.sorted(by: { $0 < $1 }) {
            if let entry = entries[id] {
                print("#\(id): \(entry.counterParty) $\(entry.amount)")
            }
        }
        print("Total: $\(total)")
        print("----")
    }
}

let ledger = Ledger()

ledger.addEntry(counterParty: "Bob", amount: 100.43)
ledger.addEntry(counterParty: "Joe", amount: 200.20)

let memento = ledger.createMemento()

ledger.addEntry(counterParty: "Alice", amount: 500)
ledger.addEntry(counterParty: "Tony", amount: 20)

ledger.printEntries()

ledger.applyMemento(memento: memento)

ledger.printEntries()

// #1: Bob $100.43
// #2: Joe $200.2
// #3: Alice $500.0
// #4: Tony $20.0
// Total: $820.63
// ----
// #1: Bob $100.43
// #2: Joe $200.2
// Total: $300.63
// ----

Strategyパターン

明確に定義されたプロトコルに準拠するアルゴリズムオブジェクトを利用して、修正せずに拡張することが可能なクラスを作成するために使用されるパターンである。

protocol Strategy {
    func execute(values: [Int]) -> Int
}

class SumStrategy: Strategy {
    func execute(values: [Int]) -> Int {
        return values.reduce(0) { $0 + $1 }
    }
}

class MultiplyStrategy: Strategy {
    func execute(values: [Int]) -> Int {
        return values.reduce(1) { $0 * $1 }
    }
}

final class Sequence {
    private var numbers: [Int]

    init(_ numbers: Int...) {
        self.numbers = numbers
    }

    func addNumber(_ value: Int) {
        self.numbers.append(value)
    }

    func compute(strategy: Strategy) -> Int {
        return strategy.execute(values: self.numbers)
    }
}

let sequence = Sequence(1, 2, 3, 4)
sequence.addNumber(10)
sequence.addNumber(20)

let sumStrategy = SumStrategy()
let multiplyStrategy = MultiplyStrategy()

print("Sum: \(sequence.compute(strategy: sumStrategy))") // Sum: 40
print("Multiply: \(sequence.compute(strategy: multiplyStrategy))") // Multiply: 4800

Visitorパターン

異種のオブジェクトからなるコレクションを操作するための新しいアルゴリズムを定義できるようにするパターンである。

protocol Shape {
    func accept(visitor: Visitor)
}

protocol Visitor {
    func visit(shape: Circle)
    func visit(shape: Square)
    func visit(shape: Rectangle)
}

class AreaVisitor: Visitor {
    var totalArea: Float = 0

    func visit(shape: Circle) {
        totalArea += (3.14 * powf(shape.radius, 2))
    }

    func visit(shape: Square) {
        totalArea += powf(shape.length, 2)
    }

    func visit(shape: Rectangle) {
        totalArea += (shape.xLen * shape.yLen)
    }
}

class Circle: Shape {
    let radius: Float

    init(radius: Float) {
        self.radius = radius
    }

    func accept(visitor: Visitor) {
        visitor.visit(shape: self)
    }
}

class Square: Shape {
    let length: Float

    init(length: Float) {
        self.length = length
    }

    func accept(visitor: Visitor) {
        visitor.visit(shape: self)
    }
}

class Rectangle: Shape {
    let xLen: Float
    let yLen: Float

    init(x: Float, y: Float) {
        self.xLen = x
        self.yLen = y
    }

    func accept(visitor: Visitor) {
        visitor.visit(shape: self)
    }
}

class ShapeCollection {
    let shapes: [Shape]

    init() {
        shapes = [
            Circle(radius: 2.5),
            Square(length: 4),
            Rectangle(x: 10, y: 2)
        ]
    }
    func accept(visitor: Visitor) {
        for shape in shapes {
            shape.accept(visitor: visitor)
        }
    }
}

let shapes = ShapeCollection()
let areaVisitor = AreaVisitor()
shapes.accept(visitor: areaVisitor)
print("Area: \(areaVisitor.totalArea)") // Area: 55.625

Template Methodパターン

アルゴリズムの特定のステップをサードパーティによって提供される実装に置き換えることができるパターンである。

struct Donor {
    let title: String
    let firstName: String
    let familyName: String
    let lastDonation: Float
}

class DonorDatabase {
    private var donors: [Donor]
    var filter: (([Donor]) -> [Donor])?
    var generate: (([Donor]) -> [String])?

    init() {
        donors = [
            Donor(title: "MS", firstName: "Anne", familyName: "Jones", lastDonation: 0),
            Donor(title: "Mr", firstName: "Bob", familyName: "Smith", lastDonation: 100),
            Donor(title: "Dr", firstName: "Alice", familyName: "Doe", lastDonation: 200),
            Donor(title: "Prof", firstName: "Joe", familyName: "Davis", lastDonation: 320)
        ]
    }

    func generate(maxNumber: Int) -> [String] {
        var targetDonors = filter?(donors) ?? donors.filter { $0.lastDonation > 0 }
        targetDonors.sort { $0.0.lastDonation > $0.1.lastDonation }
        if targetDonors.count > maxNumber {
            targetDonors = Array(targetDonors[0..<maxNumber])
        }
        return generate?(targetDonors) ?? targetDonors.map { donor in
            return "Dear \(donor.title). \(donor.familyName)"
        }
    }
}

let donorDb = DonorDatabase()

let galaInvitations = donorDb.generate(maxNumber: 2)
for invite in galaInvitations {
    print(invite)
}

donorDb.filter = { $0.filter { $0.lastDonation == 0 } }
donorDb.generate = { $0.map { "Hi \($0.firstName)" } }

let newDonors = donorDb.generate(maxNumber: Int.max)
for invite in newDonors {
    print(invite)
}

// Dear Prof. Davis
// Dear Dr. Doe
// Hi Anne

読書の記録をつけていて良かったこと

2013年から初めて4年の間読んだ本の記録をつけてて、色々と良かったことがあったのでちょっと書く

どうやって記録してるか

良かったことの前にまずはどうやって記録してるか。記録が出来ればなんでもいいと思うけど、自分は読書メーターというサービスを使っている

elk.bookmeter.com

簡単に呼んだ本を記録できて感想も書ける、また簡単なSNSにもなっていてお気に入りに追加した他のユーザーの読んだ本や感想なども見ることが出来て良い

f:id:Nonchalanttan:20170106040823p:plain

記録をつけてて良かったこと

記録をつけるとはデータを蓄積することなのでその人間の傾向などが見えてきて、それはそれで面白いのだがどっちかというと副作用的な良かったことについて書く。以下、良かったこと箇条書き

  1. 自分の好みの変遷を知れる
  2. 自分が普段読まないジャンルの本を知れる
  3. 読んだ本の他の人の意見が知れる
  4. 本がきっかけになって話題になる

1. 自分の好みの変遷を知れる

これは記録をつけているので過去のデータを遡れば当たり前なので省略

2. 自分が普段読まないジャンルの本を知れる

自分はミステリばっかり読んでて他のジャンルは全然わからないが、知り合いのSFマニアが読んだ本が読書メーターのTLに流れてくるので、ちょっと興味が沸いてくる。実際に会ったときにオススメ本を教えあって感想を話すのもかなり盛り上がれて良い。どうしてもAmazonだと同じジャンルのものしか出てこないので、便利なのだが他のジャンルには手が出しづらいので他人は偉大!

3. 読んだ本の他の人の意見が知れる

ミステリばっかり読んでるので、最後まで読んだときに「これ、みんな納得してるの!?自分の頭が悪いだけ!?」と思ってよく感想とかを探すのに検索していた。ただ、そういうのも読書メーター上で完結できるので良い

4. 本がきっかけになって話題になる

読破した本をTwitterに連動させて流すことができるので、前に知り合いから「これ面白いよねー」とリプがきてそれが話題になって会話が弾んで楽しかった。個人的にその知り合いの方はその本を読むタイプだと思っていなかったので、かなりビックリした印象。ついでにオススメ本も教えてもらったのでラッキー!

まとめ

他のジャンルに手をだすキッカケになってほしいので自分も本を勧めておく。どちらもかなり読みやすいミステリ物なのでオススメ

ロートレック荘事件 空飛ぶ馬

読書は基本的に1人でするものだと思っているが、記録をつけるのとそれを公開すればそれが話題になって他人との交流が深まりかなり良かったなと感じた。ハッピー読書ライフ!!

今まで読んだ本をひたすら読書メーターに登録するのはかなりツラかった、なぜ今後読む本だけにしなかったのか...

学生と社会人1年目から見たSHIROBAKO

この記事は SHIROBAKO Advent Calendar 2016 五日目の記事です。

こんにちわ、のほほん( @nonchalant0303 )です。前の4人のクオリティが高すぎるのと、前日のネタと微妙に被ってしまい冷や汗を書いているのですが、どうかお付き合いください。

jmatsu.hatenablog.com

はじめに

SHIROBAKOの放映時は自分は大学生でした。そのときに見た感想としては漠然と面白いアニメだなーといったぐらいだったのですが、このブログを書くにあたってSHIROBAKOを見直しました。やっぱり面白くてつい24話一気に見てしまいましたのですが、1周目と2周目の違いはあれど見終わった感想がかなり違った印象を受けたのでそれについて書いていきます。

「高梨 太郎」という男

f:id:Nonchalanttan:20161023145549j:plain

高梨太郎というキャラクターを知っている人は間違っても彼が仕事できる、有能だと言う人はいないでしょう。自分も最初見たときは2年目なのに「報・連・相」が出来なくてイライラするなーと思って見てました。ただ、見返したときに高梨太郎というキャラクターの重要性をものすごく感じました。

彼によって救われたキャラクターが少なくとも武蔵野アニメーションには3人はいると自分は思いました。

f:id:Nonchalanttan:20161023170116j:plainf:id:Nonchalanttan:20161023170122j:plainf:id:Nonchalanttan:20161023170125j:plain

平岡に関してはアニメ本編でもじっくり書かれているので言うまでもないと思いますが、新人の制作進行の2人もかなり精神面で支えられているのではないかと感じました。制作進行の先輩のみゃーもりとたろうは「えくそだすっ!」を1本通して色々と体験して少し慣れてきてる感があります。ただ、新人の2人は体験したことない仕事でプレッシャーも感じてる中、ある意味でたろうのいい加減な仕事ぶりで救われてる面はあるだろうなーと思います。

というのも、自分が仕事でそれなりに大きな失敗をしたときにかなり心に来てしまった時期がありました。しかし、そこで会社の先輩がかなりリラックスしながら仕事してる(?)姿を見て、そんなに気を張ってもしょうがないなと感じてかなり救われました。

f:id:Nonchalanttan:20161204134654j:plain

経験不足というのもありますが、自分は仕事でリリースの直前などはどうしてもピリピリしてしまいます。ただ、全員がそのようなピリピリしている状態だとかなり空気が悪くなってしまいます。チームに経験豊富な人がいれば圧倒的な牽引力で引っ張っていってくれるのでしょうが、自分が配属されたチームはそういった感じではありませんでした。そのとき、横のチームの先輩達のかなり気の抜けた会話を聞いて、リラックスした記憶があります。武蔵野アニメーションのようにかなりヒリヒリしたチームで働くときにたろうのような存在なかなり重要なのだなと感じました。

f:id:Nonchalanttan:20161204134943j:plain

社会人2年目にあたり後輩も出来るので、自分もいい意味でたろうのような存在になりたいです。また、2年目になったときにSHIROBAKOを見ます!!

iOSでPDFを表示してみる メモ

iOSでPDFを表示する方法を知らなかったのでメモ書き

//: Playground - noun: a place where people can play

import PlaygroundSupport
import UIKit
import CoreGraphics

class PDFView: UIView {
    var page: CGPDFPage?

    override func draw(_ rect: CGRect) {
        guard let page = page else { return }

        guard let context = UIGraphicsGetCurrentContext() else {
            fatalError()
        }

        context.translateBy(x: 0, y: rect.size.height)
        context.scaleBy(x: 1.0, y: -1.0)

        let box = page.getBoxRect(.mediaBox)

        let xScale = rect.size.width / box.size.width
        let yScale = rect.size.height / box.size.height
        let scale = min(xScale, yScale)

        let tx = (rect.size.width - box.size.width * scale) / 2
        let ty = (rect.size.height - box.size.height * scale) / 2

        context.translateBy(x: tx, y: ty)
        context.scaleBy(x: scale, y: scale)
        context.drawPDFPage(page)
    }
}

guard let url = NSURL(string: "http://devstreaming.apple.com/videos/wwdc/2016/402h429l9d0hy98c9m6/402/402_whats_new_in_swift.pdf"), let doc = CGPDFDocument(url) else {
    fatalError()
}

let pageCount = doc.numberOfPages
let number = 1
let page = doc.page(at: number)

let view = PDFView(frame: UIScreen.main.bounds)
view.backgroundColor = UIColor.lightGray
view.page = page

PlaygroundPage.current.liveView = view

上のコードはPlaygroundで検証しました。PlaygroundPage.current.liveView = viewでPlaygroundでもViewの検証ができます。

f:id:Nonchalanttan:20161111181540p:plain

今回はシングルページの表示ですが、doc.page(at: number)でページを切り替えれるので、簡単にPDFViewerみたいなアプリも作れそうですね