【swift】ライブラリを使ってhtmlをpdfデータとして保存しwebViewで表示＆動的に書き換える

[ディップAdventCalendar2017](http://qiita.com/advent-calendar/2017/dip-dev)、3日目🌟٩( 'ω' )و

****

htmlで書いたデータをpdfで保存・作成し複数ページで表示する、
そして中身のデータを動的に書き換える方法です💪

最初は[libHaru](http://libharu.org)という昔からあるpdf作成ライブラリから派生しました[swiftyHaru](https://github.com/WeirdMath/SwiftyHaru)を使おうとしたのですが、
[PDFGenerator](https://github.com/sgr-ksmt/PDFGenerator)という(自分的に)もっと使いやすいライブラリを見つけました♪( ´▽｀)

# 1.環境
* Xcode9.1
* swift3.2

* podfileに以下を入力

```
pod 'PDFGenerator'
``` 

# 2.準備
* .htmlを2つと.css1つを用意
* .storyboard：viewControllerの上にwebViewを配置
* webViewをIBOutlet接続

<img width="600" alt="img1.png" src="https://qiita-image-store.s3.amazonaws.com/0/146629/d2506172-1847-9ce9-3705-0bdbf977d5a3.png">

* htmlの中身を動的に差し替えるということで、htmlの中身はこのような形にしております☺️
* `#Data1#`、`#Data2#`の部分を書き換えるようにします💪
    * なので最初に入れておくhtmlはテンプレデータの役目として使うイメージ

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="pdfSample.css">
  </head>
  <body>
    <p class="color">#Data1##Data2#</p>
  </body>
</html>
```

# 3.実装
## ■html表示用のwebViewを読み込む
pdfを作るにあたり先にhtmlを読み込んでおく必要があるので表示用のwebViewを作成しhtmlを読み込みますo(｀ω´ )o

```swift
class pdfSampleViewController: UIViewController, UIWebViewDelegate {
    
    // webViewの親view
    @IBOutlet weak var sampleView: UIView!
    // webView
    @IBOutlet weak var webView: UIWebView!
    
    // pdfページ用(html読み込み用)のwebView
    var pdfPage1WebView = UIWebView()
    var pdfPage2WebView = UIWebView()
    // pdfページ用のwebView格納配列
    var webViewArray: [UIWebView] = []

    // MARK: - Life Cycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frameSize = CGRect(x: 0.0, y: 0, width: sampleView.bounds.width, height: sampleView.bounds.height / 2)
        // viewのサイズの初期化
        pdfPage1WebView = UIWebView(frame: frameSize)
        pdfPage2WebView = UIWebView(frame: frameSize)
        
        pdfPage1WebView.delegate = self
        pdfPage2WebView.delegate = self
    }
}
```

## ■htmlのデータを新たに作り保存する
上記で書いたように、テンプレデータに値を入れるため新たにhtmlデータを作成します。
書き換えたい値を別クラス内で辞書として持ちfor文で回すようにしています。
辞書のvalueの方をDBからデータを取ってくるというように変えると良いと思います☺️

``` swift
// pdfのデータを表示する
func callingPDFData() {
    
    // テンプレートデータのパスを取得する
    if let pdfPage1Path = Bundle.main.path(forResource: "pdfPage1", ofType: "html"),
        let pdfPage2Path = Bundle.main.path(forResource: "pdfPage2", ofType: "html"),
        let cssFormatPath = Bundle.main.path(forResource: "pdfSample", ofType: "css") {
        
        // 編集後のデータを格納する
        var newPdfPage1Data = ""
        var newPdfPage2Data = ""
        var newPdfPage1FilePath = URL(fileURLWithPath: "")
        var newPdfPage2FilePath = URL(fileURLWithPath: "")
        
        do {
            // テンプレートデータの中身を取得する
            let pdfPage1Data = try NSString(contentsOf: URL(fileURLWithPath: pdfPage1Path), encoding: String.Encoding.utf8.rawValue)
            let pdfPage2Data = try NSString(contentsOf: URL(fileURLWithPath: pdfPage2Path), encoding: String.Encoding.utf8.rawValue)
            
            // 取得の中身を置換する
            newPdfPage1Data = pdfPage1Data.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
            newPdfPage2Data = pdfPage2Data.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
            for (key, value) in PdfDataModel().pdfPage1DataDictionary {
                newPdfPage1Data = newPdfPage1Data.replacingOccurrences(of: key, with: value)
            }
            for (key, value) in PdfDataModel().pdfPage2DataDictionary {
                newPdfPage2Data = newPdfPage2Data.replacingOccurrences(of: key, with: value)
            }
            
            // 置換後のデータをドキュメントディレクトリに保存する
            if let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first {
                newPdfPage1FilePath = dir.appendingPathComponent("editPdfPage1.html")
                newPdfPage2FilePath = dir.appendingPathComponent("editPdfPage2.html")
                do {
                    try newPdfPage1Data.write(to: newPdfPage1FilePath, atomically: false, encoding: String.Encoding.utf8)
                    try newPdfPage2Data.write(to: newPdfPage2FilePath, atomically: false, encoding: String.Encoding.utf8)
                } catch {
                }
            }
        } catch {
        }
        // 新しく作ったデータでリクエストを投げる
        let req = NSMutableURLRequest(url: newPdfPage1FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
        let req2 = NSMutableURLRequest(url: newPdfPage2FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
        self.pdfPage1WebView.loadRequest(req as URLRequest)
        self.pdfPage2WebView.loadRequest(req2 as URLRequest)
    }
}
```

* データ用クラス

```swift
class PdfDataModel {
    
    // 各データ変数を格納
    var pdfPage1DataDictionary: [String : String] = ["#Data1#": "HELLO",
                                                    "#Data2#": " WORLD"
    ]
    // 各データ変数を格納
    var pdfPage2DataDictionary: [String : String] = ["#Data1#": "hello",
                                                    "#Data2#": " world"
    ]
}
```

## ■htmlのデータをpdf内に保存する
html用のwebViewが読み込み完了したらpdfで保存する処理を行います💪

* 読み込みが完了したか判別

```swift
// webViewカウント用配列
var webViewCountArray: [UIWebView] = []

// MARK: - UIWebViewDelegate

func webViewDidStartLoad(_ webView: UIWebView) {
        
    webViewCountArray.append(webView)
}
    
func webViewDidFinishLoad(_ webView: UIWebView) {
        
    webViewArray.append(webView)
        
    // htmlファイルが２ページ分とも読み込み完了したらpdfで表示する処理を呼ぶ
    if webViewArray.count == webViewCountArray.count {
        self.getPDFData()
        sampleView.backgroundColor = UIColor.lightGray
    }
}
```

* webViewで読み込んだデータをpdfに格納していく
* ライブラリ側のメソッドにpdfデータとして保存したいviewを継承したオブジェクトを渡すとそれを元にデータを作成してくれます💪💪💪便利！

```swift
import PDFGenerator

// pdfファイルの作成、保存
func getPDFData() {
        
    // ドキュメントディレクトリに保存する
    let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last

    if let pdfData = dir?.appendingPathComponent("pdfData.pdf", isDirectory: true) {
        // pdfファイルの作成、保存
        do {
            let data = try PDFGenerator.generated(by: webViewArray)
            try data.write(to: pdfData, options: .atomic)
        } catch {
        }
    }
}
```

## ■pdfを呼ぶ
* pdfを呼び出す処理も同じくライブラリの処理を叩く💪

```swift
// 呼び出し
do {
    try PDFGenerator.generate(webViewArray, to: pdfData)
   } catch {
   }
            
   webView.scalesPageToFit = true
   webView.loadRequest(NSMutableURLRequest(url: pdfData) as URLRequest)
```

# 3.確認
* デバックで見てみます。

<img width="546" alt="スクリーンショット 2017-12-03 7.35.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/146629/e69cd56f-9adc-0351-87c0-bb64c01f220d.png">

出来た！☺️

コンテナーからDocmentフォルダを見てみます。
<img width="793" alt="スクリーンショット 2018-01-18 10.44.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/146629/a1b31efe-2872-a329-eaf5-6ab00e0b282d.png">

pdfデータとして保存されているのが確認できました💪✨✨

*********************

以下コード全体です↓

```swift
import UIKit
import PDFGenerator

class pdfSampleViewController: UIViewController, UIWebViewDelegate {
    
    // webViewの親view
    @IBOutlet weak var sampleView: UIView!
    // webView
    @IBOutlet weak var webView: UIWebView!
    
    // pdfページ用(html読み込み用)のwebView
    var pdfPage1WebView = UIWebView()
    var pdfPage2WebView = UIWebView()
    // webViewカウント用配列
    var webViewCountArray: [UIWebView] = []
    // pdfページ用のwebView格納配列
    var webViewArray: [UIWebView] = []
    
    // MARK: - Life Cycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frameSize = CGRect(x: 0.0, y: 0, width: sampleView.bounds.width, height: sampleView.bounds.height / 2)
        // viewのサイズの初期化
        pdfPage1WebView = UIWebView(frame: frameSize)
        pdfPage2WebView = UIWebView(frame: frameSize)
        
        pdfPage1WebView.delegate = self
        pdfPage2WebView.delegate = self
        
        self.callingPDFData()
    }
    
    // MARK: - UIWebViewDelegate
    
    func webViewDidStartLoad(_ webView: UIWebView) {
        
        webViewCountArray.append(webView)
    }
    
    func webViewDidFinishLoad(_ webView: UIWebView) {
        
        webViewArray.append(webView)
        
        // htmlファイルが２ページ分とも読み込み完了したらpdfで表示する処理を呼ぶ
        if webViewArray.count == webViewCountArray.count {
            self.getPDFData()
            sampleView.backgroundColor = UIColor.lightGray
        }
    }
    
    // MARK: - Private Method
    
    // pdfファイルの作成、保存
    func getPDFData() {
        
        let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
        if let pdfData = dir?.appendingPathComponent("pdfData.pdf", isDirectory: true) {
            // pdfファイルの作成、保存
            do {
                let data = try PDFGenerator.generated(by: webViewArray)
                try data.write(to: pdfData, options: .atomic)
            } catch {
            }
            // 呼び出し
            do {
                try PDFGenerator.generate(webViewArray, to: pdfData)
            } catch {
            }
            
            webView.scalesPageToFit = true
            webView.loadRequest(NSMutableURLRequest(url: pdfData) as URLRequest)
        }
    }
    
    // pdfのデータを表示する
    func callingPDFData() {
        
        // デフォルトフォーマットのパスを取得する
        if let pdfPage1Path = Bundle.main.path(forResource: "pdfSample1", ofType: "html"),
            let pdfPage2Path = Bundle.main.path(forResource: "pdfSample2", ofType: "html"),
            let cssFormatPath = Bundle.main.path(forResource: "pdfSample", ofType: "css") {
            
            // 編集後のデータを格納する
            var newPdfPage1Data = ""
            var newPdfPage2Data = ""
            var newPdfPage1FilePath = URL(fileURLWithPath: "")
            var newPdfPage2FilePath = URL(fileURLWithPath: "")
            
            do {
                // デフォルトフォーマットの中身を取得する
                let pdfPage1TemplateData = try NSString(contentsOf: URL(fileURLWithPath: pdfPage1Path), encoding: String.Encoding.utf8.rawValue)
                let pdfPage2TemplateData = try NSString(contentsOf: URL(fileURLWithPath: pdfPage2Path), encoding: String.Encoding.utf8.rawValue)
                
                // 取得の中身を置換する
                newPdfPage1Data = pdfPage1TemplateData.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
                newPdfPage2Data = pdfPage2TemplateData.replacingOccurrences(of: "pdfSample.css", with: cssFormatPath)
                for (key, value) in pdfDataModel().pdfPage1DataDictionary {
                    newPdfPage1Data = newPdfPage1Data.replacingOccurrences(of: key, with: value)
                }
                for (key, value) in pdfDataModel().pdfPage2DataDictionary {
                    newPdfPage2Data = newPdfPage2Data.replacingOccurrences(of: key, with: value)
                }
                
                // 置換後のデータをドキュメントディレクトリに保存する
                if let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first {
                    newPdfPage1FilePath = dir.appendingPathComponent("editPdfPage1.html")
                    newPdfPage2FilePath = dir.appendingPathComponent("editPdfPage2.html")
                    // htmlファイルの作成、保存
                    do {
                        try newPdfPage1Data.write(to: newPdfPage1FilePath, atomically: false, encoding: String.Encoding.utf8)
                        try newPdfPage2Data.write(to: newPdfPage2FilePath, atomically: false, encoding: String.Encoding.utf8)
                    } catch {
                    }
                }
            } catch {
            }
            // 新しく作ったデータでリクエストを投げる
            let req = NSMutableURLRequest(url: newPdfPage1FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
            let req2 = NSMutableURLRequest(url: newPdfPage2FilePath, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 3)
            self.pdfPage1WebView.loadRequest(req as URLRequest)
            self.pdfPage2WebView.loadRequest(req2 as URLRequest)
        }
    }
}

class pdfDataModel {
    
    // 各データ変数を格納
    var pdfPage1DataDictionary: [String : String] = ["#Data1#": "HELLO",
                                                    "#Data2#": " WORLD"
    ]
    // 各データ変数を格納
    var pdfPage2DataDictionary: [String : String] = ["#Data1#": "hello",
                                                    "#Data2#": " world"
    ]
}
```