Swift4.2 読込と保存 ? RTFとXMLの話 ?

- 目的はiPhoneとかでそれらのファイルを読み込んで表示すること、次回は保存することです(追加: 2019/1/4、本日XMLの保存も解決しました)

> 拡張子のRTFとかXMLとか ? 例えばソースコードをコピーしてMacのテキストエディットでファイル名をsampleRTFとして普通に保存するとsampleRTF.rtfになりますね ? XMLのファイルは作成が面倒でここでは説明しませんが ? XMLファイル形式とは私の場合、年賀状アプリの「筆ぐるめ」の体験版をインストールして住所録を作成して書き出したものとか ? Contact-XMLの解説を参考にしました

[「ContactXML 仕様書」](http://xmlconsortium.org/wg/contact/data/ContactXML_01_01a.pdf)

- 簡単なコードの解説

- Instance Method
url(forResource:withExtension:subdirectory:localization:)でバンドルのsampleRTF.rtfを読み込みます

[「参考サイト・Developer・url(forResource:withExtension:subdirectory:localization:)」](https://developer.apple.com/documentation/foundation/bundle/1417378-url)

```Declaration.swift
func url(forResource name: String?, 
withExtension ext: String?, 
subdirectory subpath: String?, 
localization localizationName: String?) -> URL?
以下コード
let rtf = Bundle.main.url(forResource: "sampleRTF", withExtension: "rtf", subdirectory: nil, localization: nil)

```

- NSAttributedStringのType Property
defaultAttributes

[「参考サイト・Developer・defaultAttributes」](https://developer.apple.com/documentation/foundation/nsattributedstring/documentattributekey/1524806-defaultattributes)

```Declaration.swift
static let defaultAttributes: NSAttributedString.DocumentAttributeKey
以下コード
let attributedString: [NSAttributedString.DocumentReadingOptionKey : Any] = [
                .documentType : NSAttributedString.DocumentType.rtf]

```

- Initializer
init(url:options:documentAttributes:)

[「参考サイト・Developer・init(url:options:documentAttributes:)」](https://developer.apple.com/documentation/foundation/nsattributedstring/1530490-init)

```Declaration.swift
init(url: URL, options: [NSAttributedString.DocumentReadingOptionKey : Any] = [:], documentAttributes dict: AutoreleasingUnsafeMutablePointer<NSDictionary?>?) throws
以下コード
let attributedText = try! NSAttributedString(url: rtf, options: attributedString, documentAttributes: nil)
                testUiText.attributedText = attributedText

```

- 12/30日追加します、ご存知でしょうが ? これのがコードが少ない ?

```コードが少ない.swift

if let rtf = Bundle.main.url(forResource: "sampleRTF", withExtension: "rtf", subdirectory: nil, localization: nil) {
            //let attributedString: [NSAttributedString.DocumentReadingOptionKey : Any] = [.documentType : NSAttributedString.DocumentType.rtf]
            let attributedText = try! NSAttributedString(url: rtf, options: [.documentType : NSAttributedString.DocumentType.rtf], documentAttributes: nil)
            //let attributedText = try! NSAttributedString(url: rtf, options: attributedString, documentAttributes: nil)
                testUiText.attributedText = attributedText
        }

```

> 拡張子のRTFの全コードです

```ViewController.swift

//
//  ViewController.swift
//  ViewController
//
//  Created by 福田敏一 on 2018/12/29.
//  Copyright © 2018 株式会社パパスサン. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var uiTextView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        if let rtf = Bundle.main.url(forResource: "sampleRTF", withExtension: "rtf", subdirectory: nil, localization: nil) {
            
            let attributedString: [NSAttributedString.DocumentReadingOptionKey : Any] = [
                .documentType : NSAttributedString.DocumentType.rtf]
            
            let attributedText = try! NSAttributedString(url: rtf, options: attributedString, documentAttributes: nil)
            uiTextView.attributedText = attributedText
        } else {
            //エラーの場合
        }
    }

}

```

> 拡張子のXMLの全コードです

```ViewController.swift

//
//  ViewController.swift
//  ViewController
//
//  Created by 福田敏一 on 2018/12/29.
//  Copyright © 2018 株式会社パパスサン. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var uiTextView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        if let xml = Bundle.main.url(forResource: "sampleXML", withExtension: "xml", subdirectory: nil, localization: nil) {
            
            let attributedString: [NSAttributedString.DocumentReadingOptionKey : Any] = [
                .documentType : NSAttributedString.DocumentType.html]
            
            let attributedText = try! NSAttributedString(url: xml, options: attributedString, documentAttributes: nil)
            uiTextView.attributedText = attributedText
        } else {
            //エラーの場合
        }
    }

}

```

> RTFの実行結果の縦表示です

![ビデオが開けなかった場合に表示されるテキスト](https://scontent-nrt1-1.xx.fbcdn.net/v/t1.0-9/48413675_1962537230478088_5221403406878375936_o.jpg?_nc_cat=107&_nc_ht=scontent-nrt1-1.xx&oh=40965470c0036a6caa01d54337bba4b5&oe=5CC841E3)

> RTFの実行結果の横表示です

![ビデオが開けなかった場合に表示されるテキスト](https://scontent-nrt1-1.xx.fbcdn.net/v/t1.0-9/49183621_1962537560478055_6146357084024733696_n.jpg?_nc_cat=100&_nc_ht=scontent-nrt1-1.xx&oh=85061d7e06aaae67720d64c6a523768e&oe=5C9F9F83)


- 感想

> 実行結果が変ですよね ? storyboardのシミレーターはiPad Pro 9.7"でデキストView画面サイズは(x:20, y:20, widht: 728, height: 984)にしていますが、縦横共に表示が全面になってませんよね ? 私はテキストViewサイズ全体に表示すると思っていましたが ? 縦画面画像では文字数の分しか作成されません ? また、横画面でも右方向の文字が改行された場所までしか作成されてませんね ? 例えば縦方向に沢山の文字がある場合と横方向に沢山文字がある場合もテストしましたが ? その場合は指定したテキストViewのサイズまで作成されました、そういうもんなんですかね ?

# Swift4.2 RTFとXMLの保存ができましたの話 ? 2019/1/3 追加1/4: XMLの保存を追加

> 平成31年の新年ですね ? 関東は天気も良く気持ちの良い新年でした
しかし、RTFの保存が課題でしたね ? で、今、解決しましたのでコードを表示します、明日にでもコードを再度確認してみて修正します

```RTF読込と書込.swift

//
//  ViewController.swift
//  RTFsave
//
//  Created by 福田敏一 on 2018/12/28.
//  Copyright © 2018 福田敏一. All rights reserved.
//

import UIKit

class RTFViewController: UIViewController {
    
    @IBOutlet weak var testUiText: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        if let rtf = Bundle.main.url(forResource: "sampleRTF", withExtension: "rtf", subdirectory: nil, localization: nil) {
            let attributedString: [NSAttributedString.DocumentReadingOptionKey : Any] = [.documentType : NSAttributedString.DocumentType.rtf]
            //let attributedText = try! NSAttributedString(url: rtf, options: [.documentType : NSAttributedString.DocumentType.rtf], documentAttributes: nil)
            let attributedText = try! NSAttributedString(url: rtf, options: attributedString, documentAttributes: nil)
            
                testUiText.attributedText = attributedText
        }
        
//RTF書込み
        let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        let documentsPath = paths[0] as String
        let documentsURL = URL(fileURLWithPath: documentsPath)
        let rtfURL = documentsURL.appendingPathComponent("sampleRTF").appendingPathExtension("rtf")
print("RTF URL: \(rtfURL.path)")
        let opts = [NSAttributedString.DocumentAttributeKey.documentType : NSAttributedString.DocumentType.rtf]
        
        do {
            let data = try testUiText.attributedText.data(from: NSRange(location: 0, length: testUiText.attributedText.length), documentAttributes: opts)
print("通過・data -> \(data)")
//2019/1/3・NSdata -> NSString変更で書込み可能になりました
            let dataNS: String = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue)! as String
print("通過・dataNS -> \(dataNS)")
            try dataNS.write(to: rtfURL, atomically: true, encoding: .utf8)
            
        } catch let error as NSError {
            print("Failed to write to URL")
            print(error)
        }
    }
    
}
//実行結果
RTF URL: /Users/papassan/Library/Developer/CoreSimulator/Devices/93D9DF90-D9A9-413D-B046-60689B9B265D/data/Containers/Data/Application/D8415F6B-F701-46FB-A776-B17A8BBF7B1C/Documents/sampleRTF.rtf
通過・data -> 5751 bytes
通過・dataNS -> {\rtf1\ansi\ansicpg1252
{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;\f1\fswiss\fcharset0 Helvetica;\f2\fnil\fcharset128 HiraginoSans-W3;
}
{\colortbl;\red255\green255\blue255;\red127\green140\blue152;\red41\green42\blue48;\red255\green122\blue178;
\red255\green255\blue255;\red138\green209\blue195;\red167\green235\blue221;\red255\green129\blue112;\red167\green157\blue247;
\red160\green217\blue117;\red186\green242\blue143;}
{\*\expandedcolortbl;;\csgenericrgb\c42394\c47462\c52518;\csgenericrgb\c12054\c12284\c14131;\csgenericrgb\c98839\c37355\c63833;
\csgenericrgb\c100000\c100000\c100000;\csgenericrgb\c47716\c78607\c71403;\csgenericrgb\c59926\c90967\c83488;\csgenericrgb\c98912\c41558\c36568;\csgenericrgb\c58752\c52717\c95948;
\csgenericrgb\c56799\c83212\c38450;\csgenericrgb\c68215\c95290\c48909;}
\deftab890
\pard\tx890\pardeftab890\pardirnatural\partightenfactor0

\f0\fs36 \cf2 \cb3 //
\f1\fs24 \cf0 \

\f0\fs36 \cf2 //  ViewController.swift
\f1\fs24 \cf0 \

\f0\fs36 \cf2 //  RTFsave
\f1\fs24 \cf0 \

\f0\fs36 \cf2 //
\f1\fs24 \cf0 \

\f0\fs36 \cf2 //  Created by 
\f2 \'95\'9f\'93\'63\'95\'71\'88\'ea
\f0  on 2018/12/28.
\f1\fs24 \cf0 \

\f0\fs36 \cf2 //  Copyright \'a9 2018 
\f2 \'95\'9f\'93\'63\'95\'71\'88\'ea
\f0 . All rights reserved.
\f1\fs24 \cf0 \

\f0\fs36 \cf2 //
\f1\fs24 \cf0 \
\

\f0\fs36 \cf4 import\cf5  UIKit
\f1\fs24 \cf0 \
\

\f0\fs36 \cf4 class\cf5  ViewController: \cf6 UIViewController\cf5  \{
\f1\fs24 \cf0 \

\f0\fs36 \cf5     
\f1\fs24 \cf0 \

\f0\fs36 \cf5     \cf4 @IBOutlet\cf5  \cf4 weak\cf5  \cf4 var\cf5  testUiText: \cf6 UITextView\cf5 !
\f1\fs24 \cf0 \

\f0\fs36 \cf5     
\f1\fs24 \cf0 \

\f0\fs36 \cf5     \cf4 override\cf5  \cf4 func\cf5  viewDidLoad() \{
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 super\cf5 .\cf7 viewDidLoad\cf5 ()
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 // Do any additional setup after loading the view, typically from a nib.
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 if\cf5  \cf4 let\cf5  url = \cf6 Bundle\cf5 .\cf6 main\cf5 .\cf7 url\cf5 (forResource: \cf8 "testRichText"\cf5 , withExtension: \cf8 "rtf"\cf5 ) \{
\f1\fs24 \cf0 \

\f0\fs36 \cf5             \cf4 let\cf5  opts = [\cf6 NSAttributedString\cf5 .\cf6 DocumentAttributeKey\cf5 .\cf6 documentType\cf5 :\cf6 NSAttributedString\cf5 .\cf6 DocumentType\cf5 .\cf6 rtf\cf5 ]
\f1\fs24 \cf0 \

\f0\fs36 \cf5             \cf4 if\cf5  \cf4 let\cf5  s = \cf6 NSAttributedString\cf5 (fileURL: url, options: opts, documentAttributes: \cf4 nil\cf5 , error: \cf4 nil\cf5 ) \{
\f1\fs24 \cf0 \

\f0\fs36 \cf5                 \cf4 self\cf5 .testUiText.attributedText = s
\f1\fs24 \cf0 \

\f0\fs36 \cf5             \}
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \}
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  opts = [\cf6 NSAttributedString\cf5 .\cf6 DocumentAttributeKey\cf5 .\cf6 documentType\cf5 :\cf6 NSAttributedString\cf5 .\cf6 DocumentType\cf5 .\cf6 rtf\cf5 ]
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  input = \cf8 "Hello, world"
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  range = \cf7 NSMakeRange\cf5 (\cf9 0\cf5 , \cf10 testUiText\cf5 .\cf6 attributedText\cf5 .\cf6 length\cf5 )
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  swiftRange = \cf6 Range\cf5 (range, in: input)
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //NSData
\f2 \'82\'c9\'95\'cf\'8a\'b7
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //let data = testUiText.attributedText.dataFromRange(NSRange(0, testUiText.attributedText.length), documentAttributes: opts, error: nil)
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  data = \cf10 testUiText\cf5 .\cf6 attributedText\cf5 .dataFromRange(range, documentAttributes: opts, error: \cf4 nil\cf5 )
\f1\fs24 \cf0 \

\f0\fs36 \cf5         
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //
\f2 \'83\'74\'83\'40\'83\'43\'83\'8b\'96\'bc
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  fileName = \cf8 "test.rtf"
\f1\fs24 \cf0 \

\f0\fs36 \cf5         
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //Documents
\f2 \'82\'dc\'82\'c5\'82\'cc\'83\'70\'83\'58\'8e\'e6\'93\'be
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  paths1 = \cf7 NSSearchPathForDirectoriesInDomains\cf5 (\cf6 FileManager\cf5 .\cf6 SearchPathDirectory\cf5 .\cf7 documentDirectory\cf5 , \cf6 FileManager\cf5 .\cf6 SearchPathDomainMask\cf5 .\cf6 userDomainMask\cf5 , \cf4 true\cf5 )
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  documentsPath = paths1[\cf9 0\cf5 ] \cf4 as\cf5  \cf6 String
\f1\fs24 \cf0 \

\f0\fs36 \cf5         
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //Documents
\f2 \'82\'dc\'82\'c5\'82\'cc\'83\'70\'83\'58\'82\'c9\'83\'74\'83\'40\'83\'43\'83\'8b\'96\'bc\'82\'f0\'8c\'8b\'8d\'87
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //let filePath = documentsPath.stringByAppendingPathComponent(fileName)
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  filePath = documentsPath.\cf11 stringByAppendingPathComponent\cf5 (path: fileName)
\f1\fs24 \cf0 \

\f0\fs36 \cf7 print\cf5 (filePath)
\f1\fs24 \cf0 \

\f0\fs36 \cf5         
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf2 //RTF
\f2 \'83\'74\'83\'40\'83\'43\'83\'8b\'8f\'6f\'97\'cd
\f1\fs24 \cf0 \

\f0\fs36 \cf5         data?.write(to: filePath, options: \cf4 true\cf5 )
\f1\fs24 \cf0 \

\f0\fs36 \cf5     \}
\f1\fs24 \cf0 \
\
\

\f0\fs36 \cf5 \}
\f1\fs24 \cf0 \
\

\f0\fs36 \cf4 extension\cf5  \cf6 String\cf5  \{
\f1\fs24 \cf0 \

\f0\fs36 \cf5     \cf4 func\cf5  stringByAppendingPathComponent(path: \cf6 String\cf5 ) -> \cf6 String\cf5  \{
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 let\cf5  nsSt = \cf4 self\cf5  \cf4 as\cf5  \cf6 NSString
\f1\fs24 \cf0 \

\f0\fs36 \cf5         \cf4 return\cf5  nsSt.\cf7 appendingPathComponent\cf5 (path)
\f1\fs24 \cf0 \

\f0\fs36 \cf5     \}
\f1\fs24 \cf0 \

\f0\fs36 \cf5 \}
\f1\fs24 \cf0 \
\
}

```

> 使うのはパスかURLか ? 一番短縮の形は以下です、皆さんは個々に研究してください、パスは/Users/省略でURLはfile:///Users/省略みたいなヤツですよね ? 私は何も考えないで作成してるのでコードが長くなりますが短縮形を考えてみました、私はこれでOKです

```保存URLの短縮.swift

let FileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
        let rtfURL1 = FileURL.appendingPathComponent("sampleRTF").appendingPathExtension("rtf")
print("rtfURL1 \(rtfURL1)")

//実行結果
rtfURL1 file:///Users/papassan/Library/Developer/CoreSimulator/Devices/93D9DF90-D9A9-413D-B046-60689B9B265D/data/Containers/Data/Application/D4C9B4F5-7C60-4575-A00D-DD5378386E97/Documents/sampleRTF.rtf

```

- いやっ ? XMLでは保存されていません、研究しますが修正しました

- 修正後

```XML読込と保存.swift

//
//  ViewController.swift
//  Xml
//
//  Created by 福田敏一 on 2019/01/04.
//  Copyright © 2019 福田敏一. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var testUIText: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let xml = Bundle.main.path(forResource: "address", ofType: "xml")
        
            do {
               let readXML = try String(contentsOfFile: xml!, encoding: String.Encoding.utf8)
                testUIText.text = readXML
            } catch let error as NSError {
                print("Failed to write to URL")
                print(error)
            }
        
//保存
        let DocumentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        let fileURL = DocumentDirURL.appendingPathComponent("sampleXML").appendingPathExtension("xml")
         let readXML = testUIText.text
print("通過・fileURL -> \(fileURL)")
        do {
            try readXML!.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
        } catch let error as NSError {
            print("Failed to write to URL")
            print(error)
        }
    }

}

実行結果
通過・fileURL -> file:///Users/papassan/Library/Developer/CoreSimulator/Devices/93D9DF90-D9A9-413D-B046-60689B9B265D/data/Containers/Data/Application/DAA045F6-C5B8-4C9F-A81C-4BD703B8E939/Documents/sampleXML.xml

```

- 修正前

```XML読込と保存.swift

//
//  ViewController.swift
//  RTFsave
//
//  Created by 福田敏一 on 2018/12/28.
//  Copyright © 2018 福田敏一. All rights reserved.
//

import UIKit

class XMLViewController: UIViewController {
    
    @IBOutlet weak var testUiText: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        
        if let xml = Bundle.main.url(forResource: "address", withExtension: "xml", subdirectory: nil, localization: nil) {
            let attributedString: [NSAttributedString.DocumentReadingOptionKey : Any] = [
                .documentType : NSAttributedString.DocumentType.html,]
            
            let attributedTxt = try! NSAttributedString(url: xml, options: attributedString, documentAttributes: nil)
                testUiText.attributedText = attributedTxt
        }

//2019/1/4・XML保存
        let FileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
        let xmlURL = FileURL.appendingPathComponent("sampleXML").appendingPathExtension("xml")
//2019/1/4・DocumentTypeはhtmlにするとOKですがXMLではありません
        let opts = [NSAttributedString.DocumentAttributeKey.documentType : NSAttributedString.DocumentType.html]
        
        do {
            let data = try testUiText.attributedText.data(from: NSRange(location: 0, length: testUiText.attributedText.length), documentAttributes: opts)

            let dataNS: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
            
            //XMLファイル出力
            try dataNS.write(to: xmlURL, atomically: true, encoding: .utf8)
            
        } catch let error as NSError {
            print("Failed to write to URL")
            print(error)
        }
    }

}


```

- 感想

> 昨年からRTF保存を研究しました、三が日は子供達の来客で開発は中止でした

- できるときは無心でコードが進みますね ?

> どうしたら保存できるか ? なんで保存できたのでしょうかね ? 考えます

ここまで