Guides and Sample Code

Developer

Start Developing iOS Apps (Swift)

On This Page

Persist Data
データ永続

In this lesson, you save a meal list across FoodTracker app sessions. Understanding and implementing data persistence is a vital part of iOS app development. iOS has many persistent data storage solutions; in this lesson, you’ll use NSCoding as the data persistence mechanism in the FoodTracker app. NSCoding is a protocol that enables a lightweight solution for archiving objects and other structures. Archived objects can be stored on disk and retrieved at a later time.
このレッスンでは、あなたはFoodTrackerアプリのセッションをまたいで食事リストを保存します。データ永続の理解と実装は、iOSアプリ開発の極めて重要な部分です。iOSは、多くの永続データ貯蔵の解決策を持ちます;このレッスンでは、あなたはNSCodingをデータ永続の仕組みとしてFoodTrackerアプリで使います。NSCodingは、あるプロトコルです、それは、オブジェクトおよび他の構造物のアーカイブ(保存)に対する軽量な解決策となります。アーカイブされたオブジェクトは、ディスクに保管され後で取り出されることができます。

Learning Objectives
学習する目標

At the end of the lesson, you’ll be able to:
このレッスンの終わりに、あなたが出来るようになるのは:

  • Create a structure to store string constants
    構造体を作成して文字列定数を格納する

  • Understand the difference between static properties and instance properties
    静的プロパティとインスタンスプロパティの間の違いを理解する

  • Use the NSCoding protocol to read and write data
    NSCodingプロトコルを使ってデータを読み書きする

Save and Load the Meal
食事の保存とロード

In this step you’ll implement the behavior in the Meal class to save and load the meal. Using the NSCoding approach, the Meal class is in charge of storing and loading each of its properties. It needs to save its data by assigning the value of each property to a particular key. It then loads the data by looking up the information associated with that key.
この段階においてあなたはMealクラスにおいて食事を保存してロードする(読み込む)挙動を実装します。NSCodingの手法を使って、Mealクラスはそれのプロパティの格納とロードの世話をします。それは、それのデータの保存を、各プロパティの値を特有のキーに割り当てることによって行う必要があります。それはそれから、データのロードを、そのキーに結びついた情報を検索することによって行います。

A key is simply a string value. You choose your own keys based on what makes the most sense in your app. For example, you might use the key name to store the value of the name property.
キーは、単なる文字列値です。あなたは、あなた独自のキーをあなたのアプリにおいて最も意味の通るものに基づき選びます。例えば、あなたはキーnameを使うことで、nameプロパティの値を格納するかもしれません。

To make it clear which coding key corresponds to each piece of data, create a structure to store the key strings. This way, when you need to use the key in multiple places throughout your code, you can use the constant instead of retyping the string (which increases the likelihood of mistakes).
どの符号化キーがデータ片のそれぞれに対応するかわかりやすくするために、ある構造体を作成してキー文字列を格納してください。こうすることで、あなたのコードにおいて複数の箇所でキーを使う必要がある時、あなたはその文字列をなんどもタイプする(間違いの可能性を増やす)ことの代わりに定数を使うことができます。

To implement a coding key structure
符号化キー構造体を実装するには

  1. Open Meal.swift.
    Meal.swiftを開いてください。

  2. In Meal.swift, below the //MARK: Properties section, add this structure:
    Meal.swiftにおいて、//MARK: Properties節の下で、この構造体を加えてください:

    1. //MARK: Types
    2. struct PropertyKey {
    3. }
  3. In the PropertyKey structure, add these properties:
    このPropertyKey構造体において、これらのプロパティを加えてください:

    1. static let name = "name"
    2. static let photo = "photo"
    3. static let rating = "rating"

    Each constant corresponds to one of the three properties of Meal. The static keyword indicates that these constants belong to the structure itself, not to instances of the structure. You access these constants using the structure’s name (for example, PropertyKey.name).
    各定数は、Mealの3つのプロパティのうちの1つに対応します。staticキーワードが示すのは、これらの定数が構造体それ自体に属しているということです、構造体のインスタンスにではなく。あなたは、これら定数に構造体の名前(例えば、PropertyKey.name)を使ってアクセスします。

Your PropertyKey structure should look like this:
あなたのPropertyKey構造体はこのように見えるはずです:

  1. struct PropertyKey {
  2. static let name = "name"
  3. static let photo = "photo"
  4. static let rating = "rating"
  5. }

To be able to encode and decode itself and its properties, the Meal class needs to conform to the NSCoding protocol. To conform to NSCoding, the Meal needs to subclass NSObject. NSObject is considered a base class that defines a basic interface to the runtime system.
それ自身とそれのプロパティを符号化および復号化するのが可能であるには、MealクラスはNSCodingプロトコル準拠する必要があります。NSCodingに準拠するために、MealNSObjectサブクラスになる必要があります。NSObjectは、基盤クラスとみなされます、それは基本的なインターフェイスを実行時システムに対して定義します。

To subclass NSObject and conform to NSCoding
NSObjectのサブクラスにして、NSCodingに準拠するには

  1. In Meal.swift, find the class line:
    Meal.swiftにおいて、class行を見つけてください:

    1. class Meal {
  2. After Meal, add a colon (:) and NSObject to subclass from the NSObject class:
    Mealの後で、コロン(:)とNSObjectを加えて、NSObjectクラスのサブクラスにしてください:

    1. class Meal: NSObject {
  3. After NSObject, add a comma (,) and NSCoding to adopt the NSCoding protocol:
    NSObjectの後で、コンマ(,)とNSCodingを加えて、NSCodingプロトコルを採用してください:

    1. class Meal: NSObject, NSCoding {

    Now that Meal is a subclass of NSObject, the Meal class’s initializer must call one of the NSObject class’s designated initializers. Because the NSObject class’s only initializer is init(), the Swift compiler adds the call for you automatically, so you don’t need to change your code; however, feel free to add a call to super.init(), if you wish.
    MealNSObjectのサブクラスである今、MealクラスのイニシャライザはNSObjectクラスの指定イニシャライザのうちの1つを呼び出さなければなりません。NSObjectクラスのただ1つのイニシャライザはinit()であることから、Swiftコンパイラはあなたの代わりに自動的に呼び出しを加えます、それであなたはあなたのコードを変更する必要はありません;しかしながら、遠慮なくsuper.init()への呼び出しを加えてください、あなたが望むなら。

The NSCoding protocol declares two methods that any class that adopts to it must implement so that instances of that class can be encoded and decoded:
NSCodingプロトコルは、2つのメソッドを宣言します、それは、それを採用するあらゆるクラスが実装しなければならないもので、それによってそのクラスのインスタンスが符号化と複合化ができるものです:

  1. encode(with aCoder: NSCoder)
  2. init?(coder aDecoder: NSCoder)

The encode(with:) method prepares the class’s information to be archived, and the initializer unarchives the data when the class is created. You need to implement both the encode(with:) method and the initializer for the data to save and load properly.
encode(with:)メソッドはクラスの情報をアーカイブされるように準備します、そしてイニシャライザはクラスが作成される時にデータをアンアーカイブします。あなたは、適切にデータを保存してロードするためにencode(with:)メソッドとイニシャライザの両方を実装する必要があります。

To implement the encodeWithCoder NSCoding method
encodeWithCoder NSCodingメソッドを実装するには

  1. In Meal.swift, before the last curly brace (}), add the following:
    Meal.swiftにおいて、最後の波括弧(})の前で、以下を加えてください:

    1. //MARK: NSCoding

    This is a comment to help you (and anybody else who reads your code) know that the code in this section is related to data persistence.
    このコメントは、あなたが(そしてあなたのコードを読む他の誰でもが)この節のコードがデータ永続に関するものだと知る助けとなります。

  2. Below the comment, add this method:
    コメントの下で、このメソッドを加えてください:

    1. func encode(with aCoder: NSCoder) {
    2. }
  3. In the encode(with:) method, add the following code:
    encode(with:)メソッドにおいて、以下のコードを加えてください:

    1. aCoder.encode(name, forKey: PropertyKey.name)
    2. aCoder.encode(photo, forKey: PropertyKey.photo)
    3. aCoder.encode(rating, forKey: PropertyKey.rating)

    The NSCoder class defines a number of encode(_:forKey:) methods, each one taking a different type for the first argument. Each method encodes data of the given type. In the code shown above, the first two lines pass a String argument, while the third line passes an Int. These lines encode the value of each property on the Meal class and store them with their corresponding key.
    NSCoderクラスは、多くのencode(_:forKey:)メソッドを定義していて、それぞれのものが最初の引数として異なる型を取ります。各メソッドは、与えられた型のデータをエンコードします。上で示したコードにおいて、最初の2つの行はString引数を渡します、一方3番目の行はIntを渡します。これらの行は、Mealクラスに関する各プロパティの値をエンコードして、それらを格納するのにそれらの対応するキーを使います。

The encode(with:) method should look like this:
encode(with:)メソッドは、このように見えるはずです:

  1. func encode(with aCoder: NSCoder) {
  2. aCoder.encode(name, forKey: PropertyKey.name)
  3. aCoder.encode(photo, forKey: PropertyKey.photo)
  4. aCoder.encode(rating, forKey: PropertyKey.rating)
  5. }

With the encoding method written, implement the initializer to decode the encoded data.
符号化メソッドが書かれたので、符号化されたデータを復号化するイニシャライザを実装します。

To implement the initializer to load the meal
食事をロードするイニシャライザを実装するには

  1. At the top of the file, import the unified logging system, just below where you import UIKit.
    ファイルの最上部で、統合ログシステムをインポートしてください、あなたがUIKitをインポートするところのすぐ下です。

    1. import os.log
  2. Below the encodeWithCoder(_:) method, add the following initializer:
    encodeWithCoder(_:)メソッドの下で、以下のイニシャライザを加えてください:

    1. required convenience init?(coder aDecoder: NSCoder) {
    2. }

    The required modifier means this initializer must be implemented on every subclass, if the subclass defines its own initializers.
    required修飾子は、このイニシャライザがすべてのサブクラス上で実装されなければならないことを意味します、もしサブクラスがそれ独自のイニシャライザを定義している場合は。

    The convenience modifier means that this is a secondary initializer, and that it must call a designated initializer from the same class.
    convenience修飾子は、これが補助のイニシャライザであること、そしてそれは指定イニシャライザを同じクラスから呼び出さなければならないことを意味します。

    The question mark (?) means that this is a failable initializer that might return nil.
    疑問符(?)は、それが失敗できるイニシャライザであるのを意味し、それはnilを返すかもしれません。

  3. Add the following code inside the initializer:
    以下のコードをイニシャライザ内部に加えてください。

    1. // The name is required. If we cannot decode a name string, the initializer should fail.
    2. guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
    3. os_log("Unable to decode the name for a Meal object.", log: OSLog.default, type: .debug)
    4. return nil
    5. }

    The decodeObject(forKey:) method decodes encoded information.
    decodeObject(forKey:)メソッドは、エンコードされた情報をデコードします。

    The return value of decodeObjectForKey(_:) is an Any? optional. The guard statement both unwraps the optional and downcasts the enclosed type to a String, before assigning it to the name constant. If either of these operations fail, the entire initializer fails.
    decodeObjectForKey(_:)の戻り値は、Any?オプショナルです。guard文は、オプショナルを、それをname定数に割り当てる前に、アンラップして封入された型をStringへダウンキャストします。これらの演算のどちらかかが失敗するならば、イニシャライザ全体が失敗します。

  4. Below the previous code, add the following:
    以前のコードの下で、以下を加えてください:

    1. // Because photo is an optional property of Meal, just use conditional cast. (写真はMealのオプショナルプロパティなので、条件キャストを使うだけです。
    2. let photo = aDecoder.decodeObjectForKey(PropertyKey.photo) as? UIImage

    You downcast the value returned by decodeObject(forKey:) as a UIImage, and assign it to the photo constant. If the downcast fails, it assigns nil to the photo property. There is no need for a guard statement here, because the photo property is itself an optional.
    あなたは、decodeObject(forKey:)によって返された値をUIImageとしてダウンキャウトします、そしてそれをphoto定数に割り当てます。ダウンキャストが失敗するならば、それはnilをphotoプロパティに割り当てます。guard文はここでは必要ありません、なぜならphotoプロパティはそれ自身オプショナルであるからです。

  5. Below the previous code, add the following:
    以前のコードの下で、以下を加えてください:

    1. let rating = aDecoder.decodeIntegerForKey(PropertyKey.rating)

    The decodeIntegerForKey(_:) method unarchives an integer. Because the return value of decodeIntegerForKey is Int, there’s no need to downcast the decoded value and there is no optional to unwrap.
    decodeIntegerForKey(_:)メソッドは、整数をアンアーカイブします。decodeIntegerForKeyの戻り値がIntであることから、復号化された値をダウンキャストする必要はありません、そしてオプショナルをアンラップすることはありません。

  6. Add the following code at the end of the implementation:
    以下のコードをこの実装の終わりに加えてください:

    1. // Must call designated initializer.
    2. self.init(name: name, photo: photo, rating: rating)

    As a convenience initializer, this initializer is required to call one of its class’s designated initializers before completing. As the initializer’s arguments, you pass in the values of the constants you created while archiving the saved data.
    便宜イニシャライザであるので、このイニシャライザは、作業を完了する前にそれのクラスの指定イニシャライザの一つを呼ぶ必要があります。このイニシャライザの引数として、あなたは、保存された値をアーカイブしている間にあなたが作成した定数の値を渡します。

The new init?(coder:) initializer should look like this:
新しいinit?(coder:)イニシャライザはこのように見えるはずです:

  1. required convenience init?(coder aDecoder: NSCoder) {
  2. // The name is required. If we cannot decode a name string, the initializer should fail.
  3. guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
  4. os_log("Unable to decode the name for a Meal object.", log: OSLog.default, type: .debug)
  5. return nil
  6. }
  7. // Because photo is an optional property of Meal, just use conditional cast. (写真はMealのオプショナルプロパティなので、条件キャストを使うだけです。
  8. let photo = aDecoder.decodeObject(forKey: PropertyKey.photo) as? UIImage
  9. let rating = aDecoder.decodeInteger(forKey: PropertyKey.rating)
  10. // Must call designated initializer.
  11. self.init(name: name, photo: photo, rating: rating)
  12. }

Next, you need a persistent path on the file system where data will be saved and loaded, so you know where to look for it.
次に、あなたはファイルシステム上の永続パス、そこにおいてデータ化保存されロードされるところを必要とします、それであなたはどこでそれを捜すかがわかります。

To create a file path to data
データへのファイルパスを作成するには

  • In Meal.swift, below the //MARK: Properties section, add this code:
    Meal.swiftにおいて、//MARK: Properties節の下で、このコードを加えてください:

    1. //MARK: Archiving Paths
    2. static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
    3. static let ArchiveURL = DocumentsDirectory.appendingPathComponent("meals")

    You mark these constants with the static keyword, which means they belong to the class instead of an instance of the class. Outside of the Meal class, you’ll access the path using the syntax Meal.ArchiveURL.path. There will only ever be one copy of these properties, no matter how many instances of the Meal class you create.
    あなたは、これらの定数をstaticキーワードで印します、それはそれらがクラスに属していることを意味します、クラスのインスタンスにではなくて。Mealクラスの外側で、あなたはパスに構文Meal.ArchiveURL.pathを使ってアクセスします。常にこれらプロパティの1つのコピーだけが存在します、あなたが作成するMealクラスのインスタンスがどれだけあるかは問題ではありません。

    The DocumentsDirectory constant uses the file manager’s urls(for:in:) method to look up the URL for your app’s documents directory. This is a directory where your app can save data for the user. This method returns an array of URLs, and the first parameter returns an optional containing the first URL in the array. However, as long as the enumerations are correct, the returned array should always contain exactly one match. Therefore, it’s safe to force unwrap the optional.
    DocumentsDirectory定数は、ファイルマネージャの持つurls(for:in:)メソッドを使って、あなたのアプリの書類ディレクトリに対するURLを検索します。これは、あなたのアプリがユーザからのデータを保存できるところのディレクトリです。このメソッドはURLからなる配列を返します、そして最初パラメータは配列中の最初のURLを含んでいるオプショナルを返します。しかしながら、列挙が正しい間は、返される配列は常にきっちりマッチする1つを含むはずです。したがって、それは安全にオプショナルを強制的にアンラップします。

    After determining the URL for the documents directory, you use this URL to create the URL for your apps data. Here, you create the file URL by appending meals to the end of the documents URL.
    書類ディレクトリに対するURLを特定後、あなたはこのURLを使ってあなたのアプリのデータに対するURLを作成します。ここで、あなたはファイルURLをmealsを書類URLの終わりに加えることによって作成します。

Checkpoint: Build your app using Command-B. It should build without issues.
確認点:あなたのアプリをコマンド-Bを使ってビルドしてください。それは問題なくビルドするはずです。

Save and Load the Meal List
食事リストの保存とロード

Now that you can save and load an individual meal, you need to save and load the meal list whenever a user adds, edits, or removes a meal.
あなたがここの食事を保存してロード(読み込み)できる今、あなたは、ユーザが食事を追加、編集、削除するたびに、食事リストを保存したりロードする必要があります。

To implement the method to save the meal list
食事リストを保存するメソッドを実装するには

  1. Open MealTableViewController.swift.
    MealTableViewController.swiftを開いてください。

  2. In the //MARK: Private Methods section, just before the last curly brace (}), add the following method:
    //MARK: Private Methods節において、最後の波括弧(})の直前に、以下のメソッドを加えてください:

    1. private func saveMeals() {
    2. }
  3. In the saveMeals() method, add the following line of code:
    このsaveMeals()メソッドにおいて、以下のコード行を加えてください:

    1. let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)

    This method attempts to archive the meals array to a specific location, and returns true if it’s successful. It uses the constant Meal.ArchiveURL that you defined in the Meal class to identify where to save the information.
    このメソッドは、meals配列を特定の場所にアーカイブして、それがうまくいくならばtrueを返します。それは、定数Meal.ArchiveURLを使います、それはあなたがMealクラスにおいて定義した情報を保存するところを識別するためのものです。

    But how do you quickly test whether the data saved successfully? Log messages to the console to indicate the result.
    しかしどうやってあなたは、データがうまく保存されたか素早くテストするのでしょうか?メッセージをコンソールに記録して結果を示してください。

  4. Add the following if statement:
    if文に続けて以下を加えてください:

    1. if isSuccessfulSave {
    2. os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
    3. } else {
    4. os_log("Failed to save meals...", log: OSLog.default, type: .error)
    5. }

    This logs a debug message to the console if the save succeeds, and an error message to the console if the save fails.
    これは、保存が成功するならばデバッグメッセージをコンソールに、そして保存が失敗するならばエラーメッセージをコンソールに記録します

Your saveMeals() method should look like this:
あなたのsaveMeals()メソッドはこのように見えるはずです:

  1. private func saveMeals() {
  2. let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)
  3. if isSuccessfulSave {
  4. os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
  5. } else {
  6. os_log("Failed to save meals...", log: OSLog.default, type: .error)
  7. }
  8. }

Now, implement a method to load meals.
次に、食事をロードするメソッドを実装してください。

To implement the method to load the meal list
食事リストをロードするメソッドを実装するには

  1. In MealTableViewController.swift, before the last curly brace (}), add the following method:
    MealTableViewController.swiftにおいて、最後の波括弧(})の前で、以下のメソッドを加えてください:

    1. private func loadMeals() -> [Meal]? {
    2. }

    This method has a return type of an optional array of Meal objects, meaning that it might return an array of Meal objects or might return nothing (nil).
    このメソッドは、Mealオブジェクトのオプショナル配列の型を返します、これは、それがMealオブジェクトの配列を返すか、または無(nil)を返すかもしれないのを意味しています。

  2. In the loadMeals() method, add the following line of code:
    loadMeals()メソッドにおいて、以下のコード行を加えてください:

    1. return NSKeyedUnarchiver.unarchiveObject(withFile: Meal.ArchiveURL.path) as? [Meal]

    This method attempts to unarchive the object stored at the path Meal.ArchiveURL.path and downcast that object to an array of Meal objects. This code uses the as? operator so that it can return nil if the downcast fails. This failure typically happens because an array has not yet been saved. In this case, the unarchiveObject(withFile:) method returns nil. The attempt to downcast nil to [Meal] also fails, itself returning nil.
    このメソッドは、パスMeal.ArchiveURL.pathに格納されるオブジェクトをアンアーカイブして、そしてそのオブジェクトをMealオブジェクトからなる配列へとダウンキャストすることを試みます。このコードは、as?演算子を使います、そのためそれはダウンキャストが失敗するならばnilを返すことができます。この失敗は、たいてい配列がまだ保存され終わっていないために起こります。この場合には、unarchiveObject(withFile:)メソッドはnilを返します。nilを[Meal]へダウンキャストする試みもまた、それ自身nilを返して失敗します。

Your loadMeals() method should look like this:
あなたのloadMeals()メソッドはこのように見えるはずです:

  1. private func loadMeals() -> [Meal]? {
  2. return NSKeyedUnarchiver.unarchiveObject(withFile: Meal.ArchiveURL.path) as? [Meal]
  3. }

With these methods implemented, you need to add code to save and load the list of meals whenever a user adds, removes, or edits a meal.
これらのメソッドが実装されたので、あなたは、ユーザが食事を追加、編集、削除するたびに、食事のリストを保存およびロードするコードを加える必要があります。

To save the meal list when a user adds, removes, or edits a meal
ユーザが食事を追加、削除、または編集したとき食事リストを保存するには

  1. In MealTableViewController.swift, find the unwindToMealList(sender:) action method:
    MealTableViewController.swiftにおいて、unwindToMealList(sender:)アクションメソッドを見つけてください:

    1. @IBAction func unwindToMealList(sender: UIStoryboardSegue) {
    2. if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
    3. if let selectedIndexPath = tableView.indexPathForSelectedRow {
    4. // Update an existing meal. // (既存の食事を更新する。)
    5. meals[selectedIndexPath.row] = meal
    6. tableView.reloadRows(at: [selectedIndexPath], with: .none)
    7. }
    8. else {
    9. // Add a new meal. // (新しい食事を加える。)
    10. let newIndexPath = IndexPath(row: meals.count, section: 0)
    11. meals.append(meal)
    12. tableView.insertRows(at: [newIndexPath], with: .automatic)
    13. }
    14. }
    15. }
  2. Right after the else clause, add the following code:
    else節のすぐ後で、以下のコードを加えてください:

    1. // Save the meals. // (食事を保存する。)
    2. saveMeals()

    This code saves the meals array whenever a new one is added or an existing one is updated. Make sure this line of code is inside of the outer if statement.
    このコードは、meals配列を、新しいものが加えられるか既存のものが更新される時はいつでも保存します。このコード行がその外側のif文の内側にあるようにしてください。

  3. In MealTableViewController.swift, find the tableView(_:commit:forRowAt:) method:
    MealTableViewController.swiftにおいて、tableView(_:commit:forRowAt:)メソッドを見つけてください:

    1. // Override to support editing the table view. (テーブルビューの編集をサポートするためにオーバーライドする。)
    2. override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    3. if editingStyle == .delete {
    4. // Delete the row from the data source // (行をデータソースから削除する)
    5. meals.remove(at: indexPath.row)
    6. tableView.deleteRows(at: [indexPath], with: .fade)
    7. } else if editingStyle == .insert {
    8. // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view // (適切なクラスの新規インスタンスを作成して、それを配列に挿入し、そして新しい行をテーブルビューに加える)
    9. }
    10. }
  4. After the meals.removeAtIndex(indexPath.row) line, add the following line of code:
    meals.removeAtIndex(indexPath.row)行の後で、以下のコードを加える:

    1. saveMeals()

    This code saves the meals array whenever a meal is deleted.
    このコードは、食事が削除される時はいつでも、meals配列を保存します。

Your unwindToMealList(_:) action method should look like this:
あなたのunwindToMealList(_:)アクションメソッドはこのように見えるはずです:

  1. @IBAction func unwindToMealList(sender: UIStoryboardSegue) {
  2. if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
  3. if let selectedIndexPath = tableView.indexPathForSelectedRow {
  4. // Update an existing meal. // (既存の食事を更新する。)
  5. meals[selectedIndexPath.row] = meal
  6. tableView.reloadRows(at: [selectedIndexPath], with: .none)
  7. }
  8. else {
  9. // Add a new meal. // (新しい食事を加える。)
  10. let newIndexPath = IndexPath(row: meals.count, section: 0)
  11. meals.append(meal)
  12. tableView.insertRows(at: [newIndexPath], with: .automatic)
  13. }
  14. // Save the meals. // (食事を保存する。)
  15. saveMeals()
  16. }
  17. }

And your tableView(_:commit:forRowAt:) method should look like this:
そしてあなたのtableView(_:commit:forRowAt:)メソッドはこのように見えるはずです:

  1. // Override to support editing the table view. (テーブルビューの編集をサポートするためにオーバーライドする。)
  2. override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
  3. if editingStyle == .delete {
  4. // Delete the row from the data source // (行をデータソースから削除する)
  5. meals.remove(at: indexPath.row)
  6. saveMeals()
  7. tableView.deleteRows(at: [indexPath], with: .fade)
  8. } else if editingStyle == .insert {
  9. // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view // (適切なクラスの新規インスタンスを作成して、それを配列に挿入し、そして新しい行をテーブルビューに加える)
  10. }
  11. }

Now that meals are saved at the appropriate times, make sure that meals get loaded at the appropriate time. The appropriate place to load the stored data is in the table view’s viewDidLoad method.
今や食事は適切な時に保存されるので、食事が適切な時にロードされるのを確実なものにしてください。格納されたデータをロードするのに適切な場所は、テーブルビューの持つviewDidLoadメソッドの中になります。

To load the meal list at the appropriate time
食事リストを適切な時にロードするには

  1. In MealTableViewController.swift, find the viewDidLoad() method:
    MealTableViewController.swiftにおいて、viewDidLoad()メソッドを見つけてください:

    1. override func viewDidLoad() {
    2. super.viewDidLoad()
    3. // Use the edit button item provided by the table view controller. // (テーブルビューコントローラによって提供される編集ボタン項目を使う)
    4. navigationItem.leftBarButtonItem = editButtonItem
    5. // Load the sample data. // (見本データをロードする)
    6. loadSampleMeals()
    7. }
  2. After setting up the edit button (navigationItem.leftBarButtonItem = editButtonItem), add the following if statement:
    編集ボタンを準備した後(navigationItem.leftBarButtonItem = editButtonItem)、if文の下に以下を加えてください:

    1. // Load any saved meals, otherwise load sample data. // (食事があればロードする、そうでなければ見本データをロードする)
    2. if let savedMeals = loadMeals() {
    3. meals += savedMeals
    4. }

    If loadMeals() successfully returns an array of Meal objects, this condition is true and the if statement gets executed. If loadMeals() returns nil, there were no meals to load and the if statement doesn’t get executed. This code adds any meals that were successfully loaded to the meals array.
    loadMeals()が成功裏にMealオブジェクトの配列を返したならば、この条件はtrueになり、if文が実行されます。loadMeals()nilを返すならば、ロードする食事はなかったので、if文は実行されません。このコードは、うまくロードされた食事があればmeals配列に加えます。

  3. After the if statement, add an else clause and move the call to loadSampleMeals() inside of it:
    if文の後で、else節を加えてloadSampleMeals()への呼び出しをそれの内側に移動してください:

    1. else {
    2. // Load the sample data. // (見本データをロードする)
    3. loadSampleMeals()
    4. }

    This code adds any meals that were loaded to the meals array.
    このコードは何らかの食事を加えます、それはmeals配列にロードされたものです。

Your viewDidLoad() method should look like this:
あなたのviewDidLoad()メソッドはこのように見えるはずです:

  1. override func viewDidLoad() {
  2. super.viewDidLoad()
  3. // Use the edit button item provided by the table view controller. // (テーブルビューコントローラによって提供される編集ボタン項目を使う)
  4. navigationItem.leftBarButtonItem = editButtonItem
  5. // Load any saved meals, otherwise load sample data. // (食事があればロードする、そうでなければ見本データをロードする)
  6. if let savedMeals = loadMeals() {
  7. meals += savedMeals
  8. }
  9. else {
  10. // Load the sample data. // (見本データをロードする)
  11. loadSampleMeals()
  12. }
  13. }

Checkpoint: Run your app. If you add a few new meals and quit the app, the meals you added will be there next time you open the app.
確認点:あなたのアプリを実行します。あなたがいくつかの新しい食事を加えてアプリを終了したならば、あなたが加えた食事は次回あなたがアプリを開いた時にそこにあるでしょう。

Wrapping Up
まとめ

In this lesson, you added the ability to save and load the app’s data. This lets the data persist across multiple runs. Whenever the app launches, it loads the existing data. When the data is modified, the app saves it. The app can terminate safely without losing any data.
このレッスンでは、あなたはアプリのデータを保存してロードする能力を加えました。これは、そのデータを複数の実行をまたいで永続させます。アプリが起動するたびごとに、それは既存のデータをロードします。データが修正される時は、アプリはそれを保存します。アプリは、何らデータを消失することなく安全に終了できます。

This also completes the app. Congratulations! You now have a fully functional app.
これはまたアプリの完成でもあります。おめでとう!あなたは今完全に機能するアプリを持ちます。