آموزشگاه برنامه نویسی تحلیل داده
آموزشگاه برنامه نویسی تحلیل داده

آموزش Data Persistence در Swift

 آموزش Data Persistence در Swift ماندگار سازی یا ذخیره دائمی داده های برنامه (Data Persistence)

این مبحث به شرح چگونگی ماندگارسازی و ذخیره دائمی meal list (لیست نمایش غذاها) در تمامی session های اپلیکیشن FoodTracker می پردازد. ذخیره سازی دائمی اطلاعات یکی از معمول ترین مشکلات در برنامه سازی برای سیستم عامل IOS می باشد که بسیاری از توسعه دهندگان با آن مواجه می شوند. سیستم عامل IOS راه حل های مختلفی ویژه ی ماندگارسازی داده ها ارائه می دهد. در آموزش حاضر، شما از NSCoding به عنوان مکانیزم ذخیره سازی دائمی داده ها در برنامه ی کاربردی FoodTracker بهره خواهید گرفت. NSCoding یک protocol یا الگوی پیاده سازی است که امکان ذخیره و ماندگار سازی آبجکت ها و دیگر ساختارهای ذخیره ی اطلاعات (structure) را به صورت بهینه فراهم می آورد. آبجکت های کدگذاری شده (archived) را می توان بر روی دیسک ذخیره کرده و بعده ها از آن واکشی کرد.

آموزش Data Persistence در Swiftآنچه خواهید آموخت

  1. می توانید یک structure تعریف کنید.
  2. تفاوت میان instance property (متغیرهای متعلق به نمونه ی کلاس یا به اصطلاح non-static) و static property (متغیرهایی که به صورت static تعریف شده و متعلقق به خود کلاس هستند) را تشخیص داده و شرح دهید.
  3. . با استفاده از پروتکل NSCoding داده هایی را از دیسک خوانده و بر روی آن ذخیره (write) نمایید.

 آموزش Data Persistence در Swift پیاده سازی قابلیت ذخیره ی دائم و بارگذاری آیتم (Save/Load مقدار meal در کلاس Meal)

در این بخش، رفتار یا قابلیتی در کلاس Meal پیاده سازی خواهید کرد که اطلاعات مربوط به آیتم جدید (meal) را ذخیره کرده و بارگذاری می کند.
کلاس Meal با پیروی از الگوی پیاده سازی NSCoding (پیاده سازی متدهایی که این protocol معرفی می کند) قابلیت و وظایف مربوط به ذخیره سازی و لود هر یک از property ها را نیز به عهده می گیرد.,br> کلاس یاد شده داده های خود را با تخصیص مقدار هر یک از property ها به key مشخص، ذخیره نموده، سپس این داده ها را با جستجو و یافتن اطلاعات key مربوطه بارگذاری می کند.
Key صرفا یک مقدار رشته ای (string) است که به مثابه ی اسم یک property ایفای نقش می کند. مطلوب است اسم key را مرتبط با مقداری که قرار است نگه دارد، انتخاب نمایید. به عنوان مثال، بهتر است برای متغیری که مقدار name را نگه می دارد، key را name انتخاب کنید.
برای اینکه مشخص شود کدام قطعه داده به کدام key تعلق دارد، توصیه می شود یک structure تعریف کرده و key ها (اسم متغیرها) را در آن ذخیره نمایید. با این کار هر زمان که ملزوم به ذکر key در بخش های مختلف کد برنامه ی خود می شوید، می توانید بجای تایپ مجدد مقادیر رشته ای که احتمال رخداد خطا را بالا می برد، اسم تخصیص داده شده به ثوابت (constant ها یا همان key که اکنون در قالب structure ریخته شده) را بکار ببرید.
به منظور پیاده سازی یک structure جهت ذخیره key ها و تعریف property مراحل زیر را دنبال نمایید:
1. فایل Meal.swift را باز کنید.
2. داخل فایل ذکر شده، در زیر بخش // MARK: Properties، این structure را اضافه نمایید:

   
// MARK: Types
struct PropertyKey {
}
                 

3. داخل بدنه ی این structure (PropertyKey )، property ها (ثوابت) زیر را اضافه نمایید:

static let nameKey = "name"
static let photoKey = "photo"
static let ratingKey = "rating"


ثوابت فوق هر یک به ترتیب با یکی از سه property اعلان شده در کلاس Meal متناظر هستند. کلیدواژه ی static نشانگر این است که ثابت مورد نظر متعلق به خود structure جاری است و نه نمونه ای از آن. این مقادیر در قالب ثوابت ریخته شده اند (با کلیدواژه ی let تعریف شده اند)، به همین دلیل هیچگاه تغییر نمی کنند.
Structure نام برده هم اکنون می بایست دارای پیاده سازی زیر باشد:

struct PropertyKey {
static let nameKey = "name"
static let photoKey = "photo"
static let ratingKey = "rating"
}

برای اینکه کلاس Meal بتواند خود و property های عضوش را encode و decode کند (قابلیت رمز گذاری و رمز گشایی را داشته باشد)، بایستی متدهای پروتکل NSCoding را پیاده سازی کند (به اصطلاح از این الگوی پیاده سازی پیروی کند؛ متدها و property های آن را پیاده سازی کند). برای این منظور، متغیر Meal می بایست از کلاس NSObject ارث بری کند. NSObject یک کلاس پایه است که پل ارتباطی (interface) ساده ای به سیستم runtime می سازد.
به منظور ارث بری از کلاس NSObject (ایجاد یک کلاس فرزند از آن) و پیاده سازی متدهای پروتکل NSCoding، مراحل زیر را دنبال نمایید:
1. داخل فایل Meal.swift، خط تعریف کلاس (حاوی کلیدواژه ی class) را پیدا کنید:

    class Meal {

2. پس از واژه ی Meal، عملگر دو نقطه ":" و NSObject را جهت ارث بری از آن ذکر نمایید:

class Meal: NSObject {

3. پس از NSObject، یک ویرگول اضافه نموده و سپس واژه ی NSCoding را جهت پیاده سازی توابع این protocol، درج نمایید:

 class Meal: NSObject, NSCoding {

پروتکل NSCoding در کل دو متد ارائه می دهد. تمامی کلاس هایی که این protocol را پیاده سازی می کنند ناگزیر می بایست دو متد آن را نیز پیاده سازی نمایند تا نمونه های کلاس مورد نظر قابلیت encode و decode را داشته باشند (بتوانند رمزگذاری و رمزگشایی شوند):

func encodeWithCoder(aCoder: NSCoder)
init(coder aDecoder: NSCoder)

متد encodeWithCoder(_:) اطلاعات کلاس را برای کدگذاری و ذخیره بر روی دیسک (archive) آماده ساخته و متد سازنده (initializer) نیز این داده ها را به هنگام ایجاد کلاس از حالت archive در می آورد. شما می بایست هر دو متد را پیاده سازی کرده تا بدین وسیله داده ها بتوانند به درستی ذخیره و بارگذاری شوند. به منظور پیاده سازی متد encodeWithCoder از پروتکل NSCoding، مراحل زیر را طی نمایید:
1. در فایل Meal.swift، قبل از آخرین ({)، کد زیر را اضافه نمایید:

  // MARK: NSCoding

خط بالا یک comment است که اعلان می دارد بخش حاضر مربوط به ماندگار سازی و ذخیره ی دائمی داده ها است.
2. در زیر خط comment این متد را اضافه نمایید:

 func encodeWithCoder(aCoder: NSCoder) {
}

3. داخل بدنه ی متد encodeWithCoder(_:)، کد زیر را وارد نمایید:

aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
aCoder.encodeObject(photo, forKey: PropertyKey.photoKey)
aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)

متد encodeObject(_:forKey:) قادر است هر نوع آبجکتی را کد گذاری کند، در حالی که متد encodeInteger(_:forKey:) می تواند صرفا یک داده از نوع integer را کدگذاری نماید. این سه خط کد مقدار property های کلاس Meal را کدگذاری نموده و سپس آن ها را همراه با key متناظر (مربوطه) ذخیره می کند.
متد encodeWithCoder(_:) می بایست دارای پیاده سازی زیر باشد:

func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
aCoder.encodeObject(photo, forKey: PropertyKey.photoKey)
aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)
}

پس از تنظیم متدی که عملیات کدگذاری را انجام می دهند، نوبت به پیاده سازی متد سازنده (initializer) می رسد که داده های کدگذاری شده را از حالت encoded خارج ساخته و به اصطلاح رمز گشایی (decode) می کند.
به منظور پیاده سازی متد سازنده (initializer) جهت بارگذاری آیتم مورد نظر (meal)، مراحل زیر را دنبال نمایید:
1. در زیر متد encodeWithCoder(_:)، متد سازنده ی زیر را اضافه نمایید:

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

کلیدواژه ی required اعلان می کند که این متد سازنده بایستی در تمامی کلاس های مشتق شده از آن کلاس (کلاسی که متد سازنده به همراه این کلیدواژه در آن تعریف شده) پیاده سازی شود. کلیدواژه ی convenience نشانگر این است که متد سازنده ی حاضر یک initializer فرعی و پشتیبان بوده و در نهایت بایستی initializer اصلی یا به اصطلاح designated کلاس خود را صدا بزند. Designated initializer متد سازنده ی اولیه و اصلی کلاس است که تمامی property های آن را مقداردهی اولیه کرده، سپس initializer کلاس پدر (superclass) را صدا می زند تا پروسه ی مقداردهی اولیه ی property ها همین طور تا بالای زنجیره ی ارث بری (superclass chain) ادامه یابد. متد سازنده ی جاری را به این علت با کلیدواژه ی convenience تعریف یا علامت گذاری کردید که منحصرا در صورت وجود داده های ذخیره شده برای بارگذاری صدا خورده و اعمال می شود.
علامت "?" بیانگر این است متد سازنده ی مورد نظر از نوع failable است و ممکن است در مقداردهی اولیه با شکست مواجه شده، در خروجی مقدار nil را برگرداند.
2. خط زیر را اضافه نمایید:

  let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String

متد decodeObjectForKey(_:)، اطلاعات ذخیره شده در رابطه با آبجکت مورد نظر را از حالت archive (کد گذاری و ذخیره شده بر روی دیسک) خارج می سازد. خروجی این متد AnyObject است که شما به واسطه ی عملیات downcast در کد بالا، به یک String تبدیل نموده و بعد داخل ثابت name قرار می دهید.
با دقت در کد بالا متوجه می شوید که مقدار بازگشتی توسط عملگر as! تبدیل می شود. علت استفاده از عملگر تبدیل مزبور این است که اگر آبجکت قابل تبدیل به String نبوده یا دارای مقدار nil بود، آنگاه طبق انتظار، شما می دانید که خطایی رخ داده و برنامه باید در زمان اجرا به طور ناگهانی از کار بیافتد (runtime crash).
3. در زیر خط قبلی، کد زیر را درج نمایید:

 // Because photo is an optional property of Meal, use conditional cast.
let photo = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage               

این دستور خروجی متد decodeObjectForKey(_:) را به کلاس UIImage تبدل (downcast) کرده و متعاقبا مقدار تبدیل شده را در ثابت photo ذخیره می کند. همان طور که در کد مشاهده می کنید، این بار تبدیل با عملگر "as?" صورت پذیرفته است چرا که photo یک متغیر از نوع optional است. بدین معنی که می تواند حاوی مقدار از جنس کلاس UIImage باشد یا اصلا مقداری نداشته و nil باشد. شما می بایست هر دو سناریو را در نظر گرفته و تمهیدات لازم را برای آن ها بیاندیشید.
4. در زیر دستور قبلی، کد زیر را وارد نمایید:

 let rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey)

متد decodeIntegerForKey(_:) یک مقدار عدد صحیح (integer) را از حالت archive و رمزنگاری شده خارج می سازد. از آنجایی که خروجی تابع مذکور از نوع عدد صحیح یا int است، لزومی ندارد این خروجی را که اکنون مقدار رمزگشایی شده است، downcast نمایید.
5. در انتهای پیاده سازی، کد زیر را وارد نمایید:

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

به عنوان یک convenience initializer، این متد سازنده می بایست در انتهای پیاده سازی، یکی از initializer های اصلی کلاس (designated initializer) را صدا بزند. مقادیر ثوابتی که به هنگام archive داده های ذخیره شده (تبدیل آبجکت به فرمتی که قابل ذخیره سازی و انتقال بین اپلیکیشن ها باشد) ایجاد نمودید را اکنون به عنوان آرگومان ورودی به متد سازنده (initializer) پاس دهید. متد سازنده یinit?(coder:) اکنون می بایست دارای پیاده سازی زیر باشد:

required convenience init?(coder aDecoder: NSCoder) {
let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String
// Because photo is an optional property of Meal, use conditional cast.
let photo = aDecoder.decodeObjectForKey(PropertyKey.photoKey) as? UIImage
let rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey)
// Must call designated initializer.
self.init(name: name, photo: photo, rating: rating)
}

از آنجایی که متد سازنده ی دیگری (initializer) که در سطح کلاس Meal تعریف کردید، init?(name:photo:rating:)، یک متد سازنده ی اصلی (designated initializer) است و با کلیدواژه ی required تعریف شده، این متد می بایست داخل بدنه ی خود متد سازنده ی کلاس والد (superclass) را نیز صدا بزند.
جهت فراخوانی متد سازنده ی کلاس پدر (initializer superclass) در بدنه ی متد سازنده ی جاری، مراحل زیر را دنبال نمایید:

init?(name: String, photo: UIImage?, rating: Int) {
// Initialize stored properties.
self.name = name
self.photo = photo
self.rating = rating
// Initialization should fail if there is no name or if the rating is negative.
if name.isEmpty || rating < 0 {
return nil
}
}

1. در زیر خط self.rating = rating ، متد سازنده ی کلاس پدر را به صورت زیر فراخوانی نمایید:

 super.init()

متد init?(name:photo:rating:) اکنون می بایست دارای پیاده سازی زیر باشد:

init?(name: String, photo: UIImage?, rating: Int) {
// Initialize stored properties.
self.name = name
self.photo = photo
self.rating = rating
super.init()
// Initialization should fail if there is no name or if the rating is negative.
if name.isEmpty || rating < 0 {
return nil
}
}

در گام بعدی، شما به محلی ثابت و ماندگار (persistent path) بر روی file system نیاز دارید که داده ها در آن ذخیره شده و بعده ها از آن بارگذاری شود. بدین وسیله شما می دانید دقیقا در کجا داده های مورد نظر را جستجو کرده و واکشی نمایید.
به منظور ایجاد محل ثابت و مشخص جهت ذخیره ی داده ها (file path)، مراحل زیر را دنبال نمایید.

  • • داخل فایل Meal.swift، در زیر بخش // MARK: Properties، کد زیر را اضافه نمایید:
// MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("meals")

با دقت در مثال بالا متوجه می شوید که این ثوابت به همراه کلیدواژه ی static تعریف و علامت گذاری شده اند. کلیدواژه ی ذکر شده بیانگر این است که ثوابت فوق متعلق به خود کلاس جاری هستند، نه نمونه ای از آن کلاس. به منظور دسترسی به محل ذخیره سازی داده های Meal از بیرون این کلاس، از ساختار دستوری Meal.ArchiveURL.path! استفاده می شود (= اسم کلاس، عملگر نقطه، اسم متغیر، عملگر نقطه، خصیصه ی path).
تست کنید: با فشردن کلیدهای Command-B برنامه ی خود را کامپایل (build) نمایید. اپلیکیشن می بایست طبق انتظار و بدون هیچ مشکلی کامپایل شود.

  آموزش Data Persistence در Swift ذخیره و بارگذاری لیست نمایش غذاها (Meal list)

با افزودن قابلیت ذخیره و بارگذاری غذای جدید به اپلیکیشن FoodTracker، ناگزیر می بایست زمانی که کاربر یک آیتم جدید اضافه یا آیتم جاری را ویرایش/حذف می کند، لیست غذاها (meal list) را نیز همزمان ذخیره نمایید.
به منظور پیاده سازی متد لازم جهت ذخیره سازی لیست غذاها، مراحل زیر را دنبال نمایید:
1. فایل MealTableViewController.swift را باز نمایید.
2. داخل فایل مزبور، خط زیر را به قبل از آخرین {، اضافه نمایید:

  // MARK: NSCoding

این خط صرفا یک comment است و به کسی کد برنامه ی شما را می خواند اعلان می دارد که این بخش مربوط به ذخیره سازی دائمی و ماندگار سازی داده های اپلیکیشن (data persistence) می باشد.
3. حال در زیر comment، متد زیر را اضافه نمایید:

func saveMeals() {
}

4. داخل بدنه ی متد saveMeals()، کد زیر را اضافه نمایید:

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

متد حاضر سعی دارد آرایه ی meals را بر روی مکان مشخصی در دیسک ذخیره کرده (archive)، سپس مقدار بولی true را در صورت امکان پذیر بودن عملیات بازگرداند. این دستور آدرس یا محل دقیق ذخیره ی داده های مورد نظر بر روی دیسک را از ثابت Meal.ArchiveURL که قبلا در سطح کلاس Meal تعریف کردید، می خواند.
اکنون این سوال مطرح می شود: از کجا می توان اطمینان حاصل کرد داده ها با موفقیت ذخیره شده اند یا خیر؟ برای این منظور کافی است با استفاده از دستور print پیغامی را در console چاپ نمایید. به عنوان مثال، چنانچه عملیات ذخیره سازی داده ی مورد نظر با شکست مواجه شد، یک پیغام خطا به نشانه ی عدم موفقیت عملیات ذخیره سازی، در console چاپ می کنید.
5. در زیر خط قبلی، دستور شرطی if زیر را اضافه نمایید.

if !isSuccessfulSave {
print("Failed to save meals...")
}

پس از اعمال تغییرات فوق، اگر عملیات ذخیره با شکست مواجه شود، یک پیغام خطا در console چاپ می گردد.
در زمان حاضر متد saveMeals()، می بایست دارای پیاده سازی زیر باشد:

func saveMeals() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path!)
if !isSuccessfulSave {
print("Failed to save meals...")
}
}

حال می بایست یک متد پیاده سازی کنید که داده های ذخیره شده را از روی دیسک خوانده و در اپلیکیشن FoodTracker بارگذاری نماید.
به منظور پیاده سازی متدی جهت بارگذاری و نمایش در لیست غذاها، مراحل زیر را طی نمایید:
1. داخل فایل MealTableViewController.swift، قبل از آخرین (})، متد زیر را درج نمایید:

func loadMeals() -> [Meal]? {
}

همان طور که می بینید خروجی این متد آرایه ای از آبجکت های Meal از نوع optional است (نوع بازگشتی آن optional array است). به عبارت بهتر، متد حاضر ممکن است در خروجی آرایه ای از آّبجکت های Meal یا مقدار nil را بازگردانی نماید (= هیچ مقدار را برنگرداند).
2. داخل بدنه ی متد loadMeals()، خط زیر را اضافه نمایید:

return NSKeyedUnarchiver.unarchiveObjectWithFile(Meal.ArchiveURL.path!) as? [Meal]

این متد سعی دارد آبجکت ذخیره شده در آدرس یا محل Meal.ArchiveURL.path! را از حالت archive خارج نموده، سپس آن آبجکت را به آرایه ای از آبجکت های Meal تبدیل (downcast) نماید. همان طور که در کد مشاهده می کنید، برای انجام عملیات تبدیل نوع از عملگر as? استفاده شده است. کد جاری در حقیقت با استفاده از این عملگر می تواند در صورت امکان پذیر نبودن تبدیل، مقدار nil را بر گرداند. از آنجایی که آرایه ممکن است با موفقیت ذخیره شده/نشده باشد، فرایند downcast می تواند با شکست مواجه گردد که در آن صورت متد قاعدتا باید nil را بازگردانی نماید. متد loadMeals() هم اکنون می بایست دارای پیاده سازی زیر باشد:

func loadMeals() -> [Meal]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(Meal.ArchiveURL.path!) as? [Meal]
}

پس از پیاده سازی متدهای فوق، کدی اضافه می کنید که به مجرد اینکه کاربر یک آیتم را اضافه، حذف یا ویرایش کرد، لیست غذاها را ذخیره و بار گذاری نماید.
برای اینکه به مجرد اضافه/حذف شدن یا ویرایش یک آیتم توسط کاربر، متعاقبا تغییرات اعمال شده در لیست غذاها ذخیره و ماندگار گردد، مراحل زیر را طی نمایید:
1. داخل فایل MealTableViewController.swift، اکشن متد unwindToMealList(_:) را نیز فراخوانی نمایید:

@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
else {
// Add a new meal.
let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
meals.append(meal)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}

2. درست پس از دستور شرطی else، کد زیر را اضافه نمایید:

// Save the meals.
saveMeals()

این کد، سبب می شود زمانی که کاربر یک آیتم جدید اضافه نموده یا آیتم جاری را ویرایش می کند، آرایه ی meals به تبع آن بر روی دیسک ذخیره گردد. این دستور می بایست داخل بدنه ی دستور if اصلی ({}) که دیگر دستورات در دل آن گنجانده شده اند، درج شود.
3. داخل فایل MealTableViewController.swift، متد tableView(_:commitEditingStyle:forRowAtIndexPath:) را پیدا کنید:

// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
meals.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .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
}
}

4. بعد از خط meals.removeAtIndex(indexPath.row)، خط زیر را اضافه نمایید:

saveMeals()

این کد باعث می شود به مجرد اینکه یک آیتم از لیست غذاها حذف شد، آرایه ی meals (به همراه تغییراتی که به آن اعمال شده) بلافاصله ذخیره گردد.
بدنه ی متد unwindToMealList( :) در حال حاضر می بایست به شکل زیر باشد:

@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
else {
// Add a new meal.
let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
meals.append(meal)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
// Save the meals.
saveMeals()
}
}

پیاده سازی متد tableView(_:commitEditingStyle:forRowAtIndexPath:) نیز می بایست به صورت زیر باشد:

 // Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
meals.removeAtIndex(indexPath.row)
saveMeals()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .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
}
}

پس از نوشتن کد لازم جهت ذخیره ی آیتم های مورد نظر در زمان های مناسب، می بایست اطمینان حاصل کنید که غذاها به موقع (در زمان مناسب) بارگذاری می شوند. این اتفاق می بایست هر زمان که meal list scene (صفحه محتوای لیست نمایش غذاها) لود شده و برای کاربر در UI نمایش داده می شود، رخ دهد. به عبارت روشن تر، مکان مناسب برای بارگذاری داده های ذخیره شده بر روی دیسک، داخل بدنه ی متد viewDidLoad می باشد.
جهت بارگذاری صفحه محتوای نمایش لیست غذاها (meal list) در زمان مربوطه، مراحل زیر را طی نمایید:
1. داخل فایل 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()
}

2. در زیر خط navigationItem.leftBarButtonItem = editButtonItem()، دستور شرطی زیر را وارد نمایید:

// Load any saved meals, otherwise load sample data.
if let savedMeals = loadMeals() {
meals += savedMeals
}

چنانچه متد loadMeals()به طور موفقیت آمیز اجرا شده و در خروجی خود آرایه ای از آبجکت های Meal را بازگردانی نماید، این شرط برقرار بوده؛ به اصطلاح true می باشد و طبیعتا دستور داخل بدنه ی if اجرا می شود. حال اگر متد مذکور nil را به عنوان خروجی برگرداند، مشخص می شود که هیچ آیتمی برای بارگذاری وجود نداشته و متعاقبا دستور درج شده داخل ساختمان if اجرا نمی شود. کد حاضر در واقع تمامی آیتم هایی که به طور کامل و با موفقیت بارگذاری شدند را به آرایه ی meals اضافه می کند.
3. پس از دستور if، یک عبارت شرطی else اضافه نموده و به دنبال آن، خط فراخوانی متد loadSampleMeals() را به داخل بدنه ی else جابجا نمایید.

else {
// Load the sample data.
loadSampleMeals()
}

این کد تمامی آیتم هایی که بارگذاری شدند را به آرایه ی meals اضافه می کند.
متد 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()
}
}

تست کنید: برنامه ی خود را اجرا کنید. حال اگر تعدادی آیتم جدید اضافه نموده، از اپلیکیشن خارج شوید و سپس دوباره برنامه را اجرا نمایید، آیتم هایی که قبلا اضافه کرده بودید، باید در لیست غذاها قابل مشاهده و دسترسی باشند.

  • 1311
  •    1018
  • تاریخ ارسال :   1395/08/07

دانلود PDF دانشجویان گرامی اگر این مطلب برای شما مفید بود لطفا ما را در GooglePlus محبوب کنید
رمز عبور: tahlildadeh.com یا www.tahlildadeh.com
ارسال دیدگاه نظرات کاربران
شماره موبایل دیدگاه
عنوان پست الکترونیک

ارسال

آموزشگاه برنامه نویسی تحلیل داده
آموزشگاه برنامه نویسی تحلیل داده

تمامی حقوق این سایت متعلق به آموزشگاه تحلیل داده می باشد .