33784


サーバーにあるJsonファイルを読み込み、オフライン環境下でも起動できるように、以下のコードでLibrary/cachesに保存します。

[code]

    let dataName = "test.json"
    
    if let jsonUrl = url {
                let jsonData = NSData(contentsOf: jsonUrl)
                let cashesPath = NSHomeDirectory() + "/Library/caches"
                if let localData = jsonData {
                    localData.write(toFile: "\(cashesPath)/\(dataName)", atomically: true)
                    print("\(cashesPath)/\(dataName)")
                }
            }
    
[/code]

そしてこの保存したtest.jsonのファイルを読み込むために

[code]

     let path = localPath + "/" + dataName
            let jsonData : NSData = NSData(contentsOfFile: path)
            let data = try! JSONSerialization.jsonObject(with: jsonData as! Data, options: []) as! Array<[String: AnyObject]>
    
[/code]

としてるのですが、

[code]

    fatal error: unexpectedly found nil while unwrapping an Optional value
    
[/code]

というエラーが出ます。  
これはdataにtest.jsonが読み込めてないのか、もしくはそもそもPathの指定が間違ってるのかと思うのですが、どう訂正したらうまくいくのでしょうか？  
swiftを学び始めてまだ1週間足らずなので見当違いなところももしかしたらあるとは思いますが、教えて頂けると嬉しいです。


CC BY-SA 3.0
2017-04-04T08:38:15.407
33764
2017-04-05T08:49:03.683
2017-04-04T08:44:23.927
5793
21359
question
swift json
Jsonで読み込んだファイルをローカルに保存して、再び読み込みたい


> これはdataにtest.jsonが読み込めてないのか、もしくはそもそもPathの指定が間違ってるのかと思うのですが、
> **どう訂正したらうまくいくのでしょうか？**

訂正箇所を指摘してもらうより、訂正箇所を自力で探し出す方法を尋ねたほうが、汎用性、応用性で優れています。ここでは、その方法を考えてみましょう。  
プログラムでは、成功しないかもしれない処理、期待しない結果になる可能性のある処理を、条件文を使って、分岐する方法がよく使われます。今回のプログラムでも、多用されています。このうまくいっていない方の分岐に、`print()`関数で、その旨を出力するようにすると、どこで期待しない結果になったのか、知ることができます。  
また、例外処理（`do - try -
catch`ブロック）で、エラーメッセージを`print()`関数で出力するようにすると、どんな例外が、どこで起きたのか、知ることができます。`NSError`オブジェクトは、`localizedDescription`プロパティで、エラーを人間が読める文字列にしてくれます。

以上を踏まえて、さらに二つの方針を加えて、コードを書いてみました。  
二つの方針のひとつは、レガシーな項目は、できるだけ使わず、新しいものに置き換えることです。`NSData`クラスは、Objective-
Cのコードとブリッジするために使われるもので、`Data`構造体に置き換えます。  
もうひとつは、「`!`」を使わないということです。この記号は、「変数や式の評価の値が`nil`だったら、実行をクラッシュして教えてね」という意味を持っていて、開発中のデバッグでは、有効ですが、完成したプログラムには、できるだけなくしておきたいものです。プログラムの実行がクラッシュして、`fatal
error: unexpectedly found nil while unwrapping an Optional
value`というエラーメッセージがコンソールに出たなら、「`!`」のある箇所を探すのが、有効なデバッグの方法です。プログラムが完成に近づいたら、「`!`」は、Optional
Binding文（`if let 〜`）に置き換えるようにしましょう。  
Xcodeで、macOSの新規アプリケーションを作成し、`ViewController.swift`ファイルに、以下のコードを記述します。Storyboardにおいて、ボタンをひとつ貼り付け、アクションメソッド`getJSON(_:)`と接続します。

[code]

    import Cocoa
    
    class ViewController: NSViewController {
    
        let fileName = "test.json"
        let urlString = "http://....../test.json" // JSONファイルのURL
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let url = URL(string: urlString) {
                if let desktopURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first {
                    do {
                        let data = try Data(contentsOf: url)
                        let fileURL = desktopURL.appendingPathComponent(fileName)
                        try data.write(to: fileURL)
                    } catch let error {
                        print("Read or Write Error: \(error.localizedDescription)")
                    }
                } else {
                    print("ローカルディスク上のパス取得失敗")
                }
            } else {
                print("不正なURL")
            }
    
        }
    
        @IBAction func getJSON(_ sender: Any) {
            if let url = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first {
                let fileURL = url.appendingPathComponent(fileName)
                do {
                    if FileManager.default.fileExists(atPath: fileURL.path) {
                        let data = try Data(contentsOf: fileURL)
                        if let dataArray = try JSONSerialization.jsonObject(with: data) as? [[String : Any]] {
                            for element in dataArray {
                                print("\(element)")
                            }
                        } else {
                            print("JSONファイル作成失敗")
                        }
                    } else {
                        print("ファイル保存失敗")
                    }
                } catch let error {
                    print("Error \(error.localizedDescription)")
                }
            } else {
                print("ローカルディスク上のパス取得失敗")
            }
        }
    
    }
    
[/code]

JSON書類ファイルを、「Library/cachesに保存」する意味がわからない（危険な香りがする）ので、上のコードでは、デスクトップに保存するようにしました。また、各種ディレクトリパスの取得は、`FileManager`クラスを使うようにしてください。パスを直書きしてもいいのですが、そのほうが移植性が高まります。  
ちなみに、~/Library/Cachesディレクトリの取得は、こう書きます。

[code]

    FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first
    
[/code]

このメソッドは配列を返すので、最初の要素を`first`で取り出します。また、ローカルディスク上のファイルにアクセスするときは、ファイルの有無を事前に確認するようにしましょう。これも`FileManager`クラスを使います。


CC BY-SA 3.0
2017-04-05T06:03:03.390
33784
2017-04-05T08:49:03.683
2017-04-05T08:49:03.683
18540
18540
33764
answer