Indonesian (Bahasa Indonesia) translation by Yudha Zubair (you can also view the original English article)
Pada artikel sebelumnya tentang iOS 8 dan Core Data, kami membahas pembaruan batch. Pembaruan Batch bukan satu-satunya API baru di kota. Pada iOS 8 dan OS X Yosemite, dimungkinkan untuk mengambil data secara asinkron. Dalam tutorial ini, kita akan melihat lebih dekat bagaimana menerapkan pengambilan asinkron dan dalam situasi apa aplikasi Anda dapat mengambil manfaat dari API baru ini.
1. Masalah
Seperti pembaruan batch, pengambilan asinkron telah ada dalam daftar keinginan banyak pengembang selama beberapa waktu. Mengambil permintaan bisa jadi rumit, membutuhkan waktu yang tidak sepele untuk diselesaikan. Selama waktu itu, permintaan pengambilan memblokir thread yang sedang berjalan dan, sebagai hasilnya, memblokir akses ke konteks objek yang dikelola yang mengeksekusi permintaan pengambilan. Masalahnya sederhana untuk dipahami, tetapi seperti apa solusi Apple.
2. Solusinya
Jawaban Apple untuk masalah ini adalah pengambilan asinkron. Permintaan pengambilan asinkron berjalan di latar belakang. Ini berarti bahwa itu tidak memblokir tugas-tugas lain saat sedang dieksekusi, seperti memperbarui antarmuka pengguna pada thread utama.
Pengambilan asinkron juga menampilkan dua fitur nyaman lainnya, pelaporan kemajuan dan pembatalan. Permintaan pengambilan asinkron dapat dibatalkan kapan saja, misalnya, ketika pengguna memutuskan permintaan pengambilan terlalu lama untuk diselesaikan. Pelaporan kemajuan adalah tambahan yang berguna untuk menunjukkan kepada pengguna kondisi permintaan pengambilan saat ini.
Pengambilan asinkron adalah API yang fleksibel. Tidak hanya memungkinkan untuk membatalkan permintaan pengambilan asinkron, tetapi juga memungkinkan untuk membuat perubahan pada konteks objek yang dikelola saat permintaan pengambilan asinkron dijalankan. Dengan kata lain, pengguna dapat terus menggunakan aplikasi Anda saat aplikasi mengeksekusi permintaan pengambilan asinkron di latar belakang.
3. Bagaimana Cara Kerjanya?
Seperti pembaruan batch, permintaan pengambilan asinkron diserahkan ke konteks objek terkelola sebagai objek NSPersistentStoreRequest
, turunan dari kelas NSAsynchronousFetchRequest
tepatnya.
Instance NSAsynchronousFetchRequest
diinisialisasi dengan objek NSFetchRequest
dan blok completion. Blok completion dijalankan ketika permintaan pengambilan asinkron telah menyelesaikan permintaan pengambilannya.
Mari kita kembali ke aplikasi to-do yang kita buat sebelumnya dalam seri ini dan menggantikan implementasi saat ini dari kelas NSFetchedResultsController
dengan permintaan pengambilan asinkron.
Langkah 1: Pengaturan Proyek
Unduh atau klon proyek dari GitHub dan buka di Xcode 6. Sebelum kita dapat mulai bekerja dengan kelas NSAsynchronousFetchRequest
, kita perlu membuat beberapa perubahan. Kami tidak akan dapat menggunakan kelas NSFetchedResultsController
untuk mengelola data tampilan tabel karena kelas NSFetchedResultsController
dirancang untuk berjalan di thread utama.
Langkah 2: Memasang Kembali Results Controller yang Diambil
Mulailah dengan memperbarui ekstensi kelas private dari kelas TSPViewController
seperti yang ditunjukkan di bawah ini. Kami menghapus properti fetchedResultsController
dan membuat properti baru, items
, dari tipe NSArray
untuk menyimpan item to-do. Ini juga berarti bahwa kelas TSPViewController
tidak perlu lagi mematuhi protokol NSFetchedResultsControllerDelegate
.
1 |
@interface TSPViewController () |
2 |
|
3 |
@property (strong, nonatomic) NSArray *items; |
4 |
|
5 |
@property (strong, nonatomic) NSIndexPath *selection; |
6 |
|
7 |
@end
|
Sebelum kita memperbaiki metode viewDidLoad
, saya pertama-tama ingin memperbarui implementasi protokol UITableViewDataSource
. Lihatlah perubahan yang saya buat di blok kode berikut.
1 |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { |
2 |
return self.items ? 1 : 0; |
3 |
}
|
1 |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
2 |
return self.items ? self.items.count : 0; |
3 |
}
|
1 |
- (void)configureCell:(TSPToDoCell *)cell atIndexPath:(NSIndexPath *)indexPath { |
2 |
// Fetch Record
|
3 |
NSManagedObject *record = [self.items objectAtIndex:indexPath.row]; |
4 |
|
5 |
// Update Cell
|
6 |
[cell.nameLabel setText:[record valueForKey:@"name"]]; |
7 |
[cell.doneButton setSelected:[[record valueForKey:@"done"] boolValue]]; |
8 |
|
9 |
[cell setDidTapButtonBlock:^{ |
10 |
BOOL isDone = [[record valueForKey:@"done"] boolValue]; |
11 |
|
12 |
// Update Record
|
13 |
[record setValue:@(!isDone) forKey:@"done"]; |
14 |
}];
|
15 |
}
|
1 |
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { |
2 |
if (editingStyle == UITableViewCellEditingStyleDelete) { |
3 |
NSManagedObject *record = [self.items objectAtIndex:indexPath.row]; |
4 |
|
5 |
if (record) { |
6 |
[self.managedObjectContext deleteObject:record]; |
7 |
}
|
8 |
}
|
9 |
}
|
Kita juga perlu mengubah satu baris kode dalam metode prepForSegue:sender:
seperti yang ditunjukkan di bawah ini.
1 |
// Fetch Record
|
2 |
NSManagedObject *record = [self.items objectAtIndex:self.selection.row]; |
Terakhir tapi bukan yang akhir, hapus implementasi protokol NSFetchedResultsControllerDelegate
karena kita tidak lagi membutuhkannya.
Langkah 3: Membuat Permintaan Ambil Asinkron
Seperti yang Anda lihat di bawah, kami membuat permintaan pengambilan asinkron dalam metode viewDidLoad
view controller. Mari kita luangkan waktu untuk melihat apa yang terjadi.
1 |
- (void)viewDidLoad { |
2 |
[super viewDidLoad]; |
3 |
|
4 |
// Helpers
|
5 |
__weak TSPViewController *weakSelf = self; |
6 |
|
7 |
// Initialize Fetch Request
|
8 |
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"TSPItem"]; |
9 |
|
10 |
// Add Sort Descriptors
|
11 |
[fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:YES]]]; |
12 |
|
13 |
// Initialize Asynchronous Fetch Request
|
14 |
NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) { |
15 |
dispatch_async(dispatch_get_main_queue(), ^{ |
16 |
// Process Asynchronous Fetch Result
|
17 |
[weakSelf processAsynchronousFetchResult:result]; |
18 |
});
|
19 |
}];
|
20 |
|
21 |
// Execute Asynchronous Fetch Request
|
22 |
[self.managedObjectContext performBlock:^{ |
23 |
// Execute Asynchronous Fetch Request
|
24 |
NSError *asynchronousFetchRequestError = nil; |
25 |
NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[weakSelf.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError]; |
26 |
|
27 |
if (asynchronousFetchRequestError) { |
28 |
NSLog(@"Unable to execute asynchronous fetch result."); |
29 |
NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); |
30 |
}
|
31 |
}];
|
32 |
}
|
Kami mulai dengan membuat dan mengonfigurasi instance NSFetchRequest
untuk menginisialisasi permintaan pengambilan asinkron. Permintaan pengambilan inilah yang akan dijalankan permintaan asinkron di latar belakang.
1 |
// Initialize Fetch Request
|
2 |
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"TSPItem"]; |
3 |
|
4 |
// Add Sort Descriptors
|
5 |
[fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:YES]]]; |
Untuk menginisialisasi instance NSAsynchronousFetchRequest
, kami memanggil initWithFetchRequest:completionBlock:
, dimasukan di fetchRequest
dan blok completion.
1 |
// Initialize Asynchronous Fetch Request
|
2 |
NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) { |
3 |
dispatch_async(dispatch_get_main_queue(), ^{ |
4 |
// Process Asynchronous Fetch Result
|
5 |
[weakSelf processAsynchronousFetchResult:result]; |
6 |
});
|
7 |
}];
|
Blok completion dipanggil ketika permintaan pengambilan asinkron telah selesai menjalankan permintaan pengambilannya. Blok completion mengambil satu argumen tipe NSAsynchronousFetchResult
, yang berisi hasil kueri serta referensi ke permintaan pengambilan asinkron asli.
Di blok completion, kami memanggil processAsynchronousFetchResult
:, dimasukan di objek NSAsynchronousFetchResult
. Kami akan melihat metode helper ini dalam beberapa saat.
Menjalankan permintaan pengambilan asinkron hampir identik dengan cara kami menjalankan NSBatchUpdateRequest
. Kami memanggil executeRequest:error:
pada konteks objek terkelola, dimasukan di permintaan pengambilan asinkron dan pointer ke objek NSError
.
1 |
[self.managedObjectContext performBlock:^{ |
2 |
// Execute Asynchronous Fetch Request
|
3 |
NSError *asynchronousFetchRequestError = nil; |
4 |
NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[weakSelf.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError]; |
5 |
|
6 |
if (asynchronousFetchRequestError) { |
7 |
NSLog(@"Unable to execute asynchronous fetch result."); |
8 |
NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); |
9 |
}
|
10 |
}];
|
Perhatikan bahwa kami menjalankan permintaan pengambilan asinkron dengan memanggil performBlock:
pada konteks objek terkelola. Meskipun ini tidak sepenuhnya diperlukan karena metode viewDidLoad
, di mana kami membuat dan mengeksekusi permintaan pengambilan asinkron, dipanggil pada thread utama, itu kebiasaan yang baik dan praktik terbaik untuk melakukannya.
Meskipun permintaan pengambilan asinkron dijalankan di latar belakang, perhatikan bahwa metode executeRequest:error:
segera kembalikan, menyerahkan kami objek NSAsynchronousFetchResult
. Setelah permintaan pengambilan asinkron selesai, objek NSAsynchronousFetchResult
yang sama diisi dengan hasil permintaan pengambilan.
Akhirnya, kami memeriksa apakah permintaan pengambilan asinkron dijalankan tanpa masalah dengan memeriksa apakah objek NSError
sama dengan nil
.
Langkah 4: Memproses Hasil Ambil Asinkron
Metode processAsynchronousFetchResult:
tidak lebih dari metode helper di mana kami memproses hasil permintaan pengambilan asinkron. Kami menetapkan properti items
view controller dengan konten properti finalResult
hasil dan memuat kembali tampilan tabel.
1 |
- (void)processAsynchronousFetchResult:(NSAsynchronousFetchResult *)asynchronousFetchResult { |
2 |
if (asynchronousFetchResult.finalResult) { |
3 |
// Update Items
|
4 |
[self setItems:asynchronousFetchResult.finalResult]; |
5 |
|
6 |
// Reload Table View
|
7 |
[self.tableView reloadData]; |
8 |
}
|
9 |
}
|
Langkah 5: Bangun dan Jalankan
Bangun proyek dan jalankan aplikasi di Simulator iOS. Anda mungkin terkejut melihat aplikasi Anda berhenti ketika mencoba mengeksekusi permintaan pengambilan asinkron. Untungnya, output di konsol memberi tahu kami apa yang salah.
1 |
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSConfinementConcurrencyType context <NSManagedObjectContext: 0x7fce3a731e60> cannot support asynchronous fetch request <NSAsynchronousFetchRequest: 0x7fce3a414300> with fetch request <NSFetchRequest: 0x7fce3a460860> (entity: TSPItem; predicate: ((null)); sortDescriptors: (( |
2 |
"(createdAt, ascending, compare:)"
|
3 |
)); type: NSManagedObjectResultType; ).'
|
Jika Anda belum membaca artikel tentang Core Data dan konkurensi, Anda mungkin bingung dengan apa yang Anda baca. Ingat bahwa Core Data mendeklarasikan tiga tipe konkurensi, NSConfinementConcurrencyType
, NSPrivateQueueConcurrencyType
, dan NSMainQueueConcurrencyType
. Setiap kali Anda membuat konteks objek terkelola dengan memanggil metode kelas init
, tipe konkurensi konteks objek terkelola yang dihasilkan sama dengan NSConfinementConcurrencyType
. Ini adalah tipe konkurensi default.
Masalahnya, bagaimanapun, adalah pengambilan asinkron tidak sesuai dengan tipe NSConfinementConcurrencyType
. Tanpa terlalu banyak detail, penting untuk mengetahui bahwa permintaan pengambilan asinkron perlu menggabungkan hasil permintaan pengambilannya dengan konteks objek terkelola yang menjalankan permintaan pengambilan asinkron. Perlu mengetahui antrian pengiriman mana yang dapat melakukan ini dan itu sebabnya hanya NSPrivateQueueConcurrencyType
dan NSMainQueueConcurrencyType
mendukung pengambilan asinkron. Solusinya sangat sederhana.
Langkah 6: Mengkonfigurasi Konteks Objek yang Dikelola
Buka TSPAppDelegate.m dan perbarui metode managedObjectContext
seperti yang ditunjukkan di bawah ini.
1 |
- (NSManagedObjectContext *)managedObjectContext { |
2 |
if (_managedObjectContext) { |
3 |
return _managedObjectContext; |
4 |
}
|
5 |
|
6 |
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; |
7 |
|
8 |
if (coordinator) { |
9 |
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; |
10 |
[_managedObjectContext setPersistentStoreCoordinator:coordinator]; |
11 |
}
|
12 |
|
13 |
return _managedObjectContext; |
14 |
}
|
Satu-satunya perubahan yang kami buat adalah mengganti metode init
dengan initWithConcurrencyType:
, dengan memasukan NSMainQueueConcurrencyType
sebagai argumen. Ini berarti bahwa konteks objek yang dikelola hanya dapat diakses dari thread utama. Ini berfungsi baik selama kita menggunakan metode performBlock:
atau performBlockAndWait:
untuk mengakses konteks objek yang dikelola.
Jalankan proyek sekali lagi untuk memastikan bahwa perubahan kami memang telah menyelesaikan masalah.
4. Menampilkan Kemajuan
Kelas NSAsynchronousFetchRequest
menambahkan dukungan untuk memantau kemajuan permintaan pengambilan dan bahkan dimungkinkan untuk membatalkan permintaan pengambilan asinkron, misalnya, jika pengguna memutuskan bahwa terlalu lama untuk menyelesaikannya.
Kelas NSAsynchronousFetchRequest
memanfaatkan kelas NSProgress
untuk pelaporan kemajuan serta membatalkan permintaan pengambilan asinkron. Kelas NSProgress
, tersedia sejak iOS 7 dan OS X 10.9, adalah cara cerdas untuk memantau kemajuan suatu tugas tanpa harus secara ketat memasangkan tugas ke antarmuka pengguna.
Kelas NSProgress
juga mendukung pembatalan, yang merupakan bagaimana permintaan pengambilan asinkron dapat dibatalkan. Mari kita cari tahu apa yang perlu kita lakukan untuk mengimplementasikan pelaporan kemajuan untuk permintaan pengambilan asinkron.
Langkah 1: Menambahkan SVProgressHUD
Kami akan menunjukkan kepada pengguna kemajuan permintaan pengambilan asinkron menggunakan pustaka SVProgressHUD milik Sam Vermette. Unduh perpustakaan dari GitHub dan tambahkan folder SVProgressHUD ke proyek Xcode Anda.
Langkah 2: Menyiapkan NSProgress
Dalam artikel ini, kami tidak akan mengeksplorasi kelas NSProgress
secara mendetail, tetapi jangan ragu untuk membaca lebih lanjut tentang hal itu dalam dokumentasi. Kami membuat instance NSProgress
di blok yang kami serahkan ke metode performBlock:
dalam metode viewDidLoad
view controller.
1 |
// Create Progress
|
2 |
NSProgress *progress = [NSProgress progressWithTotalUnitCount:1]; |
3 |
|
4 |
// Become Current
|
5 |
[progress becomeCurrentWithPendingUnitCount:1]; |
Anda mungkin terkejut bahwa kami menetapkan jumlah unit menjadi 1
. Alasannya sederhana. Ketika Core Data mengeksekusi permintaan pengambilan asinkron, ia tidak tahu berapa banyak record yang akan ditemukan di store persisten. Ini juga berarti bahwa kami tidak akan dapat menunjukkan kemajuan relatif kepada pengguna — persentase. Sebagai gantinya, kami akan menunjukkan kepada pengguna kemajuan absolut — jumlah record yang telah ditemukan.
Anda bisa memperbaiki masalah ini dengan melakukan permintaan pengambilan untuk mengambil jumlah record sebelum Anda menjalankan permintaan pengambilan yang asinkron. Saya lebih suka untuk tidak melakukan ini, karena ini juga berarti bahwa mengambil record dari store persisten membutuhkan waktu lebih lama untuk diselesaikan karena permintaan pengambilan ekstra di awal.
Langkah 3: Menambahkan Observer
Saat kami menjalankan permintaan pengambilan asinkron, kami segera menyerahkan objek NSAsynchronousFetchResult
. Objek ini memiliki properti progress
, yang bertipe NSProgress
. Properti progress
inilah yang perlu kita amati jika kita ingin menerima pembaruan kemajuan.
1 |
// Execute Asynchronous Fetch Request
|
2 |
[self.managedObjectContext performBlock:^{ |
3 |
// Create Progress
|
4 |
NSProgress *progress = [NSProgress progressWithTotalUnitCount:1]; |
5 |
|
6 |
// Become Current
|
7 |
[progress becomeCurrentWithPendingUnitCount:1]; |
8 |
|
9 |
// Execute Asynchronous Fetch Request
|
10 |
NSError *asynchronousFetchRequestError = nil; |
11 |
NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[self.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError]; |
12 |
|
13 |
if (asynchronousFetchRequestError) { |
14 |
NSLog(@"Unable to execute asynchronous fetch result."); |
15 |
NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); |
16 |
}
|
17 |
|
18 |
// Add Observer
|
19 |
[asynchronousFetchResult.progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:ProgressContext]; |
20 |
|
21 |
// Resign Current
|
22 |
[progress resignCurrent]; |
23 |
}];
|
Perhatikan bahwa kami memanggil resignCurrent
pada objek progress
untuk menyeimbangkan yang sebelumnya panggilan becomeCurrentWithPendingUnitCount:
. Perlu diingat bahwa kedua metode ini perlu dijalankan pada thread yang sama.
Langkah 4: Menghapus Observer
Di blok completion permintaan pengambilan asinkron, kami menghapus observer dan mengabaikan progres HUD.
1 |
// Initialize Asynchronous Fetch Request
|
2 |
NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) { |
3 |
dispatch_async(dispatch_get_main_queue(), ^{ |
4 |
// Dismiss Progress HUD
|
5 |
[SVProgressHUD dismiss]; |
6 |
|
7 |
// Process Asynchronous Fetch Result
|
8 |
[weakSelf processAsynchronousFetchResult:result]; |
9 |
|
10 |
// Remove Observer
|
11 |
[result.progress removeObserver:weakSelf forKeyPath:@"completedUnitCount" context:ProgressContext]; |
12 |
});
|
13 |
}];
|
Sebelum kita menerapkan observeValueForKeyPath:ofObject:change:context
, kita perlu menambahkan pernyataan impor untuk pustaka SVProgressHUD, mendeklarasikan variabel statis ProgressContext
yang kita berikan sebagai konteks saat menambahkan dan menghapus pengamat, dan menunjukkan kemajuan HUD sebelum membuat permintaan pengambilan asinkron.
1 |
#import "SVProgressHUD/SVProgressHUD.h"
|
1 |
static void *ProgressContext = &ProgressContext; |
1 |
- (void)viewDidLoad { |
2 |
[super viewDidLoad]; |
3 |
|
4 |
// Helpers
|
5 |
__weak TSPViewController *weakSelf = self; |
6 |
|
7 |
// Show Progress HUD
|
8 |
[SVProgressHUD showWithStatus:@"Fetching Data" maskType:SVProgressHUDMaskTypeGradient]; |
9 |
|
10 |
// Initialize Fetch Request
|
11 |
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"TSPItem"]; |
12 |
|
13 |
// Add Sort Descriptors
|
14 |
[fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:YES]]]; |
15 |
|
16 |
// Initialize Asynchronous Fetch Request
|
17 |
NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) { |
18 |
dispatch_async(dispatch_get_main_queue(), ^{ |
19 |
// Dismiss Progress HUD
|
20 |
[SVProgressHUD dismiss]; |
21 |
|
22 |
// Process Asynchronous Fetch Result
|
23 |
[weakSelf processAsynchronousFetchResult:result]; |
24 |
|
25 |
// Remove Observer
|
26 |
[result.progress removeObserver:weakSelf forKeyPath:@"completedUnitCount" context:ProgressContext]; |
27 |
});
|
28 |
}];
|
29 |
|
30 |
// Execute Asynchronous Fetch Request
|
31 |
[self.managedObjectContext performBlock:^{ |
32 |
// Create Progress
|
33 |
NSProgress *progress = [NSProgress progressWithTotalUnitCount:1]; |
34 |
|
35 |
// Become Current
|
36 |
[progress becomeCurrentWithPendingUnitCount:1]; |
37 |
|
38 |
// Execute Asynchronous Fetch Request
|
39 |
NSError *asynchronousFetchRequestError = nil; |
40 |
NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[weakSelf.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError]; |
41 |
|
42 |
if (asynchronousFetchRequestError) { |
43 |
NSLog(@"Unable to execute asynchronous fetch result."); |
44 |
NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); |
45 |
}
|
46 |
|
47 |
// Add Observer
|
48 |
[asynchronousFetchResult.progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:ProgressContext]; |
49 |
|
50 |
// Resign Current
|
51 |
[progress resignCurrent]; |
52 |
}];
|
53 |
}
|
Langkah 5: Pelaporan Kemajuan
Yang tersisa untuk kita lakukan adalah mengimplementasikan metode observeValueForKeyPath:ofObject:change:context:
. Kami memeriksa apakah konteksnya
sama dengan ProgressContext
, membuat objek status
dengan mengekstraksi jumlah record yang diselesaikan dari dictionary change
, dan memperbarui progres HUD. Perhatikan bahwa kami memperbarui antarmuka pengguna pada thread utama.
1 |
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { |
2 |
if (context == ProgressContext) { |
3 |
dispatch_async(dispatch_get_main_queue(), ^{ |
4 |
// Create Status
|
5 |
NSString *status = [NSString stringWithFormat:@"Fetched %li Records", (long)[[change objectForKey:@"new"] integerValue]]; |
6 |
|
7 |
// Show Progress HUD
|
8 |
[SVProgressHUD setStatus:status]; |
9 |
});
|
10 |
}
|
11 |
}
|
5. Data Dummy
Jika kita ingin menguji aplikasi kita dengan benar, kita membutuhkan lebih banyak data. Meskipun saya tidak merekomendasikan menggunakan pendekatan berikut dalam aplikasi produksi, ini adalah cara cepat dan mudah untuk mengisi database dengan data.
Buka TSPAppDelegate.m dan perbarui metode application:didFinishLaunchingWithOptions
seperti yang ditunjukkan di bawah ini. Metode populateDatabase
adalah metode helper sederhana di mana kami menambahkan data dummy ke database.
1 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
2 |
// Populate Database
|
3 |
[self populateDatabase]; |
4 |
|
5 |
...
|
6 |
|
7 |
return YES; |
8 |
}
|
Implementasinya sangat mudah. Karena kami hanya ingin memasukkan data dummy sekaligus, kami memeriksa database default pengguna untuk kunci @"didPopulateDatabase"
. Jika kunci tidak disetel, kami memasukkan data dummy.
1 |
- (void)populateDatabase { |
2 |
// Helpers
|
3 |
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; |
4 |
if ([ud objectForKey:@"didPopulateDatabase"]) return; |
5 |
|
6 |
for (NSInteger i = 0; i < 1000000; i++) { |
7 |
// Create Entity
|
8 |
NSEntityDescription *entity = [NSEntityDescription entityForName:@"TSPItem" inManagedObjectContext:self.managedObjectContext]; |
9 |
|
10 |
// Initialize Record
|
11 |
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext]; |
12 |
|
13 |
// Populate Record
|
14 |
[record setValue:[NSString stringWithFormat:@"Item %li", (long)i] forKey:@"name"]; |
15 |
[record setValue:[NSDate date] forKey:@"createdAt"]; |
16 |
}
|
17 |
|
18 |
// Save Managed Object Context
|
19 |
[self saveManagedObjectContext]; |
20 |
|
21 |
// Update User Defaults
|
22 |
[ud setBool:YES forKey:@"didPopulateDatabase"]; |
23 |
}
|
Jumlah record penting. Jika Anda berencana untuk menjalankan aplikasi pada Simulator iOS, maka boleh saja untuk memasukkan 100.000 atau 1.000.000 record. Ini tidak akan berfungsi dengan baik pada perangkat fisik dan akan terlalu lama untuk diselesaikan.
Di dalam for
loop, kami membuat objek yang dikelola dan mengisinya dengan data. Perhatikan bahwa kami tidak menyimpan perubahan konteks objek terkelola selama setiap iterasi dari perulangan for
.
Terakhir, kami memperbarui database default pengguna untuk memastikan database tidak terisi saat aplikasi diluncurkan berikutnya.
Hebat. Jalankan aplikasi di iOS Simulator untuk melihat hasilnya. Anda akan melihat bahwa dibutuhkan beberapa saat untuk permintaan pengambilan asinkron untuk mulai mengambil record dan memperbarui kemajuan HUD.



6. Melanggar Perubahan
Dengan mengganti kelas results controller yang diambil dengan permintaan pengambilan asinkron, kami telah merusak beberapa bagian aplikasi. Misalnya, mengetuk tanda centang item yang harus dilakukan sepertinya tidak berfungsi lagi. Saat basis data sedang diperbarui, antarmuka pengguna tidak mencerminkan perubahan. Solusinya cukup mudah untuk diperbaiki dan saya akan menyerahkannya kepada Anda untuk mengimplementasikan solusinya. Anda sekarang harus memiliki pengetahuan yang cukup untuk memahami masalah dan menemukan solusi yang sesuai.
Kesimpulan
Saya yakin Anda setuju bahwa pengambilan asinkron secara mengejutkan mudah digunakan. Pengangkatan berat dilakukan oleh Core Data, yang berarti bahwa tidak perlu secara manual menggabungkan hasil permintaan pengambilan asinkron dengan konteks objek yang dikelola. Satu-satunya tugas Anda adalah memperbarui antarmuka pengguna ketika asynchronous mengambil permintaan dari Anda. Bersama dengan pembaruan batch, ini merupakan tambahan yang bagus untuk kerangka kerja Data Core.
Artikel ini juga menyimpulkan seri ini tentang Core Data. Anda telah belajar banyak tentang kerangka kerja Core Data dan Anda tahu semua hal penting untuk menggunakan Core Data dalam aplikasi nyata. Core Data adalah kerangka kerja yang kuat dan, dengan rilis iOS 8, Apple telah menunjukkan kepada kami bahwa itu menjadi lebih baik setiap tahun.