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
符号化キー構造体を実装するには
Open
Meal.swift
.
Meal.swift
を開いてください。In
Meal.swift
, below the//MARK: Properties
section, add this structure:
Meal.swift
において、//MARK: Properties
節の下で、この構造体を加えてください://MARK: Types
struct PropertyKey {
}
In the
PropertyKey
structure, add these properties:
このPropertyKey
構造体において、これらのプロパティを加えてください:static let name = "name"
static let photo = "photo"
static let rating = "rating"
Each constant corresponds to one of the three properties of
Meal
. Thestatic
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
構造体はこのように見えるはずです:
struct PropertyKey {
static let name = "name"
static let photo = "photo"
static let rating = "rating"
}
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
に準拠するために、Meal
はNSObject
のサブクラスになる必要があります。NSObject
は、基盤クラスとみなされます、それは基本的なインターフェイスを実行時システムに対して定義します。
To subclass NSObject and conform to NSCoding
NSObjectのサブクラスにして、NSCodingに準拠するには
In
Meal.swift
, find theclass
line:
Meal.swift
において、class
行を見つけてください:class Meal {
After
Meal
, add a colon (:
) andNSObject
to subclass from theNSObject
class:
Meal
の後で、コロン(:
)とNSObject
を加えて、NSObject
クラスのサブクラスにしてください:class Meal: NSObject {
After
NSObject
, add a comma (,
) andNSCoding
to adopt theNSCoding
protocol:
NSObject
の後で、コンマ(,
)とNSCoding
を加えて、NSCoding
プロトコルを採用してください:class Meal: NSObject, NSCoding {
Now that
Meal
is a subclass ofNSObject
, theMeal
class’s initializer must call one of theNSObject
class’s designated initializers. Because theNSObject
class’s only initializer isinit()
, 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 tosuper.init()
, if you wish.
Meal
がNSObject
のサブクラスである今、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つのメソッドを宣言します、それは、それを採用するあらゆるクラスが実装しなければならないもので、それによってそのクラスのインスタンスが符号化と複合化ができるものです:
encode(with aCoder: NSCoder)
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メソッドを実装するには
In
Meal.swift
, before the last curly brace (}
), add the following:
Meal.swift
において、最後の波括弧(}
)の前で、以下を加えてください://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.
このコメントは、あなたが(そしてあなたのコードを読む他の誰でもが)この節のコードがデータ永続に関するものだと知る助けとなります。Below the comment, add this method:
コメントの下で、このメソッドを加えてください:func encode(with aCoder: NSCoder) {
}
In the
encode(with:)
method, add the following code:
encode(with:)
メソッドにおいて、以下のコードを加えてください:aCoder.encode(name, forKey: PropertyKey.name)
aCoder.encode(photo, forKey: PropertyKey.photo)
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 theMeal
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:)
メソッドは、このように見えるはずです:
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: PropertyKey.name)
aCoder.encode(photo, forKey: PropertyKey.photo)
aCoder.encode(rating, forKey: PropertyKey.rating)
}
With the encoding method written, implement the initializer to decode the encoded data.
符号化メソッドが書かれたので、符号化されたデータを復号化するイニシャライザを実装します。
To implement the initializer to load the meal
食事をロードするイニシャライザを実装するには
At the top of the file, import the unified logging system, just below where you import UIKit.
ファイルの最上部で、統合ログシステムをインポートしてください、あなたがUIKitをインポートするところのすぐ下です。import os.log
Below the
encodeWithCoder(_:)
method, add the following initializer:
encodeWithCoder(_:)
メソッドの下で、以下のイニシャライザを加えてください:required convenience init?(coder aDecoder: NSCoder) {
}
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を返すかもしれません。Add the following code inside the initializer:
以下のコードをイニシャライザ内部に加えてください。// The name is required. If we cannot decode a name string, the initializer should fail.
guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
os_log("Unable to decode the name for a Meal object.", log: OSLog.default, type: .debug)
return nil
}
The
decodeObject(forKey:)
method decodes encoded information.
decodeObject(forKey:)
メソッドは、エンコードされた情報をデコードします。The return value of
decodeObjectForKey(_:)
is anAny?
optional. The guard statement both unwraps the optional and downcasts the enclosed type to aString
, before assigning it to thename
constant. If either of these operations fail, the entire initializer fails.
decodeObjectForKey(_:)
の戻り値は、Any?
オプショナルです。guard文は、オプショナルを、それをname
定数に割り当てる前に、アンラップして封入された型をString
へダウンキャストします。これらの演算のどちらかかが失敗するならば、イニシャライザ全体が失敗します。Below the previous code, add the following:
以前のコードの下で、以下を加えてください:// Because photo is an optional property of Meal, just use conditional cast. (写真はMealのオプショナルプロパティなので、条件キャストを使うだけです。
let photo = aDecoder.decodeObjectForKey(PropertyKey.photo) as? UIImage
You downcast the value returned by
decodeObject(forKey:)
as aUIImage
, and assign it to thephoto
constant. If the downcast fails, it assignsnil
to the photo property. There is no need for aguard
statement here, because thephoto
property is itself an optional.
あなたは、decodeObject(forKey:)
によって返された値をUIImage
としてダウンキャウトします、そしてそれをphoto
定数に割り当てます。ダウンキャストが失敗するならば、それはnil
をphotoプロパティに割り当てます。guard
文はここでは必要ありません、なぜならphoto
プロパティはそれ自身オプショナルであるからです。Below the previous code, add the following:
以前のコードの下で、以下を加えてください:let rating = aDecoder.decodeIntegerForKey(PropertyKey.rating)
The
decodeIntegerForKey(_:)
method unarchives an integer. Because the return value ofdecodeIntegerForKey
isInt
, there’s no need to downcast the decoded value and there is no optional to unwrap.
decodeIntegerForKey(_:)
メソッドは、整数をアンアーカイブします。decodeIntegerForKey
の戻り値がInt
であることから、復号化された値をダウンキャストする必要はありません、そしてオプショナルをアンラップすることはありません。Add the following code at the end of the implementation:
以下のコードをこの実装の終わりに加えてください:// Must call designated initializer.
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:)
イニシャライザはこのように見えるはずです:
required convenience init?(coder aDecoder: NSCoder) {
// The name is required. If we cannot decode a name string, the initializer should fail.
guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
os_log("Unable to decode the name for a Meal object.", log: OSLog.default, type: .debug)
return nil
}
// Because photo is an optional property of Meal, just use conditional cast. (写真はMealのオプショナルプロパティなので、条件キャストを使うだけです。
let photo = aDecoder.decodeObject(forKey: PropertyKey.photo) as? UIImage
let rating = aDecoder.decodeInteger(forKey: PropertyKey.rating)
// Must call designated initializer.
self.init(name: name, photo: photo, rating: rating)
}
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
節の下で、このコードを加えてください://MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
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 theMeal
class, you’ll access the path using the syntaxMeal.ArchiveURL.path
. There will only ever be one copy of these properties, no matter how many instances of theMeal
class you create.
あなたは、これらの定数をstatic
キーワードで印します、それはそれらがクラスに属していることを意味します、クラスのインスタンスにではなくて。Meal
クラスの外側で、あなたはパスに構文Meal.ArchiveURL.path
を使ってアクセスします。常にこれらプロパティの1つのコピーだけが存在します、あなたが作成するMeal
クラスのインスタンスがどれだけあるかは問題ではありません。The
DocumentsDirectory
constant uses the file manager’surls(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
食事リストを保存するメソッドを実装するには
Open
MealTableViewController.swift
.
MealTableViewController.swift
を開いてください。In the
//MARK: Private Methods
section, just before the last curly brace (}
), add the following method:
//MARK: Private Methods
節において、最後の波括弧(}
)の直前に、以下のメソッドを加えてください:private func saveMeals() {
}
In the
saveMeals()
method, add the following line of code:
このsaveMeals()
メソッドにおいて、以下のコード行を加えてください:let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)
This method attempts to archive the
meals
array to a specific location, and returnstrue
if it’s successful. It uses the constantMeal.ArchiveURL
that you defined in theMeal
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.
しかしどうやってあなたは、データがうまく保存されたか素早くテストするのでしょうか?メッセージをコンソールに記録して結果を示してください。Add the following
if
statement:
if
文に続けて以下を加えてください:if isSuccessfulSave {
os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
} else {
os_log("Failed to save meals...", log: OSLog.default, type: .error)
}
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()
メソッドはこのように見えるはずです:
private func saveMeals() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)
if isSuccessfulSave {
os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
} else {
os_log("Failed to save meals...", log: OSLog.default, type: .error)
}
}
Now, implement a method to load meals.
次に、食事をロードするメソッドを実装してください。
To implement the method to load the meal list
食事リストをロードするメソッドを実装するには
In
MealTableViewController.swift
, before the last curly brace (}
), add the following method:
MealTableViewController.swift
において、最後の波括弧(}
)の前で、以下のメソッドを加えてください:private func loadMeals() -> [Meal]? {
}
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
)を返すかもしれないのを意味しています。In the
loadMeals()
method, add the following line of code:
loadMeals()
メソッドにおいて、以下のコード行を加えてください: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 ofMeal
objects. This code uses theas?
operator so that it can returnnil
if the downcast fails. This failure typically happens because an array has not yet been saved. In this case, theunarchiveObject(withFile:)
method returnsnil
. The attempt to downcastnil
to [Meal] also fails, itself returningnil
.
このメソッドは、パスMeal.ArchiveURL.path
に格納されるオブジェクトをアンアーカイブして、そしてそのオブジェクトをMeal
オブジェクトからなる配列へとダウンキャストすることを試みます。このコードは、as?
演算子を使います、そのためそれはダウンキャストが失敗するならばnil
を返すことができます。この失敗は、たいてい配列がまだ保存され終わっていないために起こります。この場合には、unarchiveObject(withFile:)
メソッドはnil
を返します。nil
を[Meal]へダウンキャストする試みもまた、それ自身nil
を返して失敗します。
Your loadMeals()
method should look like this:
あなたのloadMeals()
メソッドはこのように見えるはずです:
private func loadMeals() -> [Meal]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: Meal.ArchiveURL.path) as? [Meal]
}
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
ユーザが食事を追加、削除、または編集したとき食事リストを保存するには
In
MealTableViewController.swift
, find theunwindToMealList(sender:)
action method:
MealTableViewController.swift
において、unwindToMealList(sender:)
アクションメソッドを見つけてください:@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal. // (既存の食事を更新する。)
meals[selectedIndexPath.row] = meal
tableView.reloadRows(at: [selectedIndexPath], with: .none)
}
else {
// Add a new meal. // (新しい食事を加える。)
let newIndexPath = IndexPath(row: meals.count, section: 0)
meals.append(meal)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
}
Right after the
else
clause, add the following code:
else
節のすぐ後で、以下のコードを加えてください:// Save the meals. // (食事を保存する。)
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 outerif
statement.
このコードは、meals
配列を、新しいものが加えられるか既存のものが更新される時はいつでも保存します。このコード行がその外側のif
文の内側にあるようにしてください。In
MealTableViewController.swift
, find thetableView(_:commit:forRowAt:)
method:
MealTableViewController.swift
において、tableView(_:commit:forRowAt:)
メソッドを見つけてください:// Override to support editing the table view. (テーブルビューの編集をサポートするためにオーバーライドする。)
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source // (行をデータソースから削除する)
meals.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view // (適切なクラスの新規インスタンスを作成して、それを配列に挿入し、そして新しい行をテーブルビューに加える)
}
}
After the
meals.removeAtIndex(indexPath.row)
line, add the following line of code:
meals.removeAtIndex(indexPath.row)
行の後で、以下のコードを加える:saveMeals()
This code saves the
meals
array whenever a meal is deleted.
このコードは、食事が削除される時はいつでも、meals
配列を保存します。
Your unwindToMealList(_:)
action method should look like this:
あなたのunwindToMealList(_:)
アクションメソッドはこのように見えるはずです:
@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal. // (既存の食事を更新する。)
meals[selectedIndexPath.row] = meal
tableView.reloadRows(at: [selectedIndexPath], with: .none)
}
else {
// Add a new meal. // (新しい食事を加える。)
let newIndexPath = IndexPath(row: meals.count, section: 0)
meals.append(meal)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
// Save the meals. // (食事を保存する。)
saveMeals()
}
}
And your tableView(_:commit:forRowAt:)
method should look like this:
そしてあなたのtableView(_:commit:forRowAt:)
メソッドはこのように見えるはずです:
// Override to support editing the table view. (テーブルビューの編集をサポートするためにオーバーライドする。)
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source // (行をデータソースから削除する)
meals.remove(at: indexPath.row)
saveMeals()
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view // (適切なクラスの新規インスタンスを作成して、それを配列に挿入し、そして新しい行をテーブルビューに加える)
}
}
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
食事リストを適切な時にロードするには
In
MealTableViewController.swift
, find theviewDidLoad()
method:
MealTableViewController.swift
において、viewDidLoad()
メソッドを見つけてください:override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button item provided by the table view controller. // (テーブルビューコントローラによって提供される編集ボタン項目を使う)
navigationItem.leftBarButtonItem = editButtonItem
// Load the sample data. // (見本データをロードする)
loadSampleMeals()
}
After setting up the edit button (
navigationItem.leftBarButtonItem = editButtonItem
), add the followingif
statement:
編集ボタンを準備した後(navigationItem.leftBarButtonItem = editButtonItem
)、if
文の下に以下を加えてください:// Load any saved meals, otherwise load sample data. // (食事があればロードする、そうでなければ見本データをロードする)
if let savedMeals = loadMeals() {
meals += savedMeals
}
If
loadMeals()
successfully returns an array ofMeal
objects, this condition istrue
and theif
statement gets executed. IfloadMeals()
returnsnil
, there were no meals to load and theif
statement doesn’t get executed. This code adds any meals that were successfully loaded to themeals
array.
loadMeals()
が成功裏にMeal
オブジェクトの配列を返したならば、この条件はtrue
になり、if
文が実行されます。loadMeals()
がnil
を返すならば、ロードする食事はなかったので、if
文は実行されません。このコードは、うまくロードされた食事があればmeals
配列に加えます。After the if statement, add an
else
clause and move the call toloadSampleMeals()
inside of it:
if文の後で、else
節を加えてloadSampleMeals()
への呼び出しをそれの内側に移動してください:else {
// Load the sample data. // (見本データをロードする)
loadSampleMeals()
}
This code adds any meals that were loaded to the
meals
array.
このコードは何らかの食事を加えます、それはmeals
配列にロードされたものです。
Your viewDidLoad()
method should look like this:
あなたのviewDidLoad()
メソッドはこのように見えるはずです:
override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button item provided by the table view controller. // (テーブルビューコントローラによって提供される編集ボタン項目を使う)
navigationItem.leftBarButtonItem = editButtonItem
// Load any saved meals, otherwise load sample data. // (食事があればロードする、そうでなければ見本データをロードする)
if let savedMeals = loadMeals() {
meals += savedMeals
}
else {
// Load the sample data. // (見本データをロードする)
loadSampleMeals()
}
}
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.
これはまたアプリの完成でもあります。おめでとう!あなたは今完全に機能するアプリを持ちます。
Implement Edit and Delete Behavior
編集および削除挙動の実装
Copyright © 2016 Apple Inc. All rights reserved. Terms of Use | Privacy Policy | Updated: 2016-12-08