ลดความซับซ้อนของการใช้ RESTful API และการคงอยู่ของข้อมูลบน iOS ด้วย Mantle และ Realm
เผยแพร่แล้ว: 2022-03-11นักพัฒนา iOS ทุกคนคุ้นเคยกับ Core Data, อ็อบเจ็กต์กราฟ และเฟรมเวิร์กการคงอยู่ของ Apple นอกเหนือจากการคงข้อมูลไว้ภายในแล้ว เฟรมเวิร์กยังมาพร้อมกับคุณสมบัติขั้นสูงมากมาย เช่น การติดตามการเปลี่ยนแปลงอ็อบเจ็กต์และการเลิกทำ คุณลักษณะเหล่านี้แม้ว่าจะมีประโยชน์ในหลายกรณี แต่ก็ไม่ได้มาฟรีๆ ต้องใช้รหัสสำเร็จรูปจำนวนมาก และกรอบงานโดยรวมมีช่วงการเรียนรู้ที่สูงชัน
ในปี 2014 Realm ซึ่งเป็นฐานข้อมูลมือถือได้รับการเผยแพร่และเข้าครอบงำโลกของการพัฒนาโดยพายุ หากเราต้องการเพียงเก็บข้อมูลไว้ในเครื่อง Realm ก็เป็นทางเลือกที่ดี ท้ายที่สุดแล้ว ไม่ใช่ทุกกรณีการใช้งานที่จำเป็นต้องมีคุณลักษณะขั้นสูงของ Core Data Realm นั้นใช้งานง่ายมาก และต่างจาก Core Data ที่ต้องการโค้ดสำเร็จรูปเพียงเล็กน้อย นอกจากนี้ยังปลอดภัยสำหรับเธรดและได้รับการกล่าวขานว่าเร็วกว่าเฟรมเวิร์กการคงอยู่ของ Apple
ในแอปพลิเคชันมือถือสมัยใหม่ส่วนใหญ่ ข้อมูลที่คงอยู่ช่วยแก้ปัญหาได้ครึ่งหนึ่ง เรามักจะต้องดึงข้อมูลจากบริการระยะไกล ซึ่งมักจะผ่าน RESTful API นี่คือที่มาของ Mantle เป็นเฟรมเวิร์กโมเดลโอเพนซอร์สสำหรับ Cocoa และ Cocoa Touch Mantle ช่วยลดความซับซ้อนในการเขียนโมเดลข้อมูลสำหรับการโต้ตอบกับ API ที่ใช้ JSON เป็นรูปแบบการแลกเปลี่ยนข้อมูล
ในบทความนี้ เราจะสร้างแอปพลิเคชัน iOS ที่ดึงรายชื่อบทความพร้อมกับลิงก์จาก New York Times Article Search API v2 รายการจะถูกดึงข้อมูลโดยใช้คำขอ HTTP GET มาตรฐาน โดยมีรูปแบบคำขอและการตอบสนองที่สร้างโดยใช้ Mantle เราจะมาดูกันว่า Mantle จัดการกับการแปลงค่าได้ง่ายเพียงใด (เช่น จาก NSDate เป็นสตริง) เมื่อดึงข้อมูลแล้ว เราจะยืนยันข้อมูลดังกล่าวในเครื่องโดยใช้ Realm ทั้งหมดนี้ด้วยรหัสต้นแบบขั้นต่ำ
RESTful API - เริ่มต้นใช้งาน
เริ่มต้นด้วยการสร้างโปรเจ็กต์ Xcode “Master-Detail Application” ใหม่สำหรับ iOS ชื่อ “RealmMantleTutorial” เราจะเพิ่มเฟรมเวิร์กให้กับมันโดยใช้ CocoaPods Podfile ควรมีลักษณะดังต่อไปนี้:
pod 'Mantle' pod 'Realm' pod 'AFNetworking'
เมื่อติดตั้งพ็อดแล้ว เราสามารถเปิดพื้นที่ทำงาน MantleRealmTutorial ที่สร้างขึ้นใหม่ได้ ดังที่คุณสังเกตเห็น เฟรมเวิร์ก AFNetworking ที่มีชื่อเสียงก็ได้รับการติดตั้งเช่นกัน เราจะใช้มันเพื่อส่งคำขอไปยัง API
ตามที่กล่าวไว้ในบทนำ New York Times มี API การค้นหาบทความที่ยอดเยี่ยม ในการใช้งาน คุณต้องลงทะเบียนเพื่อรับคีย์การเข้าถึง API ซึ่งสามารถทำได้ที่ http://developer.nytimes.com ด้วยคีย์ API เราพร้อมที่จะเริ่มต้นเขียนโค้ดแล้ว
ก่อนที่เราจะเจาะลึกถึงการสร้างแบบจำลองข้อมูล Mantle เราจำเป็นต้องทำให้เลเยอร์เครือข่ายของเราเริ่มทำงาน มาสร้างกลุ่มใหม่ใน Xcode แล้วเรียกมันว่าเครือข่าย ในกลุ่มนี้ เราจะสร้างสองคลาส เรามาเรียก SessionManager ตัวแรกกันและให้แน่ใจว่าได้มาจาก AFHTTPSessionManager ซึ่งเป็นคลาสตัวจัดการเซสชันจาก AFNetworking ซึ่งเป็นเฟรมเวิร์กเครือข่ายที่น่ารื่นรมย์ คลาส SessionManager ของเราจะเป็นอ็อบเจ็กต์ซิงเกิลตันที่เราจะใช้เพื่อดำเนินการรับคำขอไปยัง API เมื่อสร้างคลาสแล้ว โปรดคัดลอกโค้ดด้านล่างลงในไฟล์อินเทอร์เฟซและการใช้งานตามลำดับ
#import "AFHTTPSessionManager.h" @interface SessionManager : AFHTTPSessionManager + (id)sharedManager; @end
#import "SessionManager.h" static NSString *const kBaseURL = @"http://api.nytimes.com"; @implementation SessionManager - (id)init { self = [super initWithBaseURL:[NSURL URLWithString:kBaseURL]]; if(!self) return nil; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.requestSerializer = [AFJSONRequestSerializer serializer]; return self; } + (id)sharedManager { static SessionManager *_sessionManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sessionManager = [[self alloc] init]; }); return _sessionManager; } @end
ตัวจัดการเซสชันเริ่มต้นด้วย URL พื้นฐานที่กำหนดไว้ในตัวแปร kBaseURL แบบคงที่ นอกจากนี้ยังจะใช้คำขอ JSON และตัวสร้างอนุกรมการตอบกลับ
ตอนนี้คลาสที่สองที่เราจะสร้างในกลุ่ม Network จะเรียกว่า APIManager มันจะได้มาจากคลาส SessionManager ที่สร้างขึ้นใหม่ของเรา เมื่อสร้างโมเดลข้อมูลที่จำเป็นแล้ว เราจะเพิ่มวิธีการใน ApiManager ซึ่งจะใช้เพื่อขอรายชื่อบทความจาก API
ภาพรวม API ค้นหาบทความของ New York Times
เอกสารอย่างเป็นทางการสำหรับ API ที่ยอดเยี่ยมนี้มีอยู่ที่ http://developer.nytimes.com/…/article_search_api_v2 สิ่งที่เราจะทำคือใช้ปลายทางต่อไปนี้:
http://api.nytimes.com/svc/search/v2/articlesearch
… เพื่อดึงบทความที่พบโดยใช้คำค้นหาที่เราเลือกซึ่งล้อมรอบด้วยช่วงวันที่ ตัวอย่างเช่น สิ่งที่เราทำได้คือขอให้ API ส่งคืนรายการบทความทั้งหมดที่ปรากฏใน New York Times ซึ่งเกี่ยวข้องกับบาสเก็ตบอลในเจ็ดวันแรกของเดือนกรกฎาคม 2015 ตามเอกสารของ API ให้ทำอย่างนั้น เราจำเป็นต้องตั้งค่าพารามิเตอร์ต่อไปนี้ในคำขอรับไปยังปลายทางนั้น:
พารามิเตอร์ | ค่า |
q | "บาสเกตบอล" |
start_date | “20150701” |
end_date | “20150707” |
การตอบสนองจาก API ค่อนข้างซับซ้อน ด้านล่างนี้คือคำตอบสำหรับคำขอที่มีพารามิเตอร์ด้านบนจำกัดไว้เพียงหนึ่งบทความ (หนึ่งรายการในอาร์เรย์เอกสาร) โดยละเว้นฟิลด์จำนวนมากเพื่อความชัดเจน
{ "response": { "docs": [ { "web_url": "http://www.nytimes.com/2015/07/04/sports/basketball/robin-lopez-and-knicks-are-close-to-a-deal.html", "lead_paragraph": "Lopez, a 7-foot center, joined Arron Afflalo, a 6-foot-5 guard, as the Knicks' key acquisitions in free agency. He is expected to solidify the Knicks' interior defense.", "abstract": null, "print_page": "1", "source": "The New York Times", "pub_date": "2015-07-04T00:00:00Z", "document_type": "article", "news_desk": "Sports", "section_name": "Sports", "subsection_name": "Pro Basketball", "type_of_material": "News", "_id": "5596e7ac38f0d84c0655cb28", "word_count": "879" } ] }, "status": "OK", "copyright": "Copyright (c) 2013 The New York Times Company. All Rights Reserved." }
สิ่งที่เราได้รับในการตอบสนองโดยทั่วไปคือสามฟิลด์ คำตอบแรกที่เรียกว่า การตอบกลับ มีอาร์เรย์ docs ซึ่งจะมีรายการที่เป็นตัวแทนของบทความ อีกสองช่องคือ สถานะ และ ลิขสิทธิ์ ตอนนี้เรารู้แล้วว่า API ทำงานอย่างไร ก็ถึงเวลาสร้างโมเดลข้อมูลโดยใช้ Mantle
บทนำสู่เสื้อคลุม
ดังที่ได้กล่าวไว้ก่อนหน้านี้ Mantle เป็นเฟรมเวิร์กโอเพนซอร์ซที่ทำให้การเขียนแบบจำลองข้อมูลง่ายขึ้นอย่างมาก เริ่มต้นด้วยการสร้างแบบจำลองคำขอรายการบทความ ให้เรียกคลาสนี้ ArticleListRequestModel และตรวจสอบให้แน่ใจว่าได้มาจาก MTLModel ซึ่งเป็นคลาสที่โมเดล Mantle ทั้งหมดควรได้รับมาจาก นอกจากนี้ มาทำให้มันเป็นไปตามโปรโตคอล MTLJSONSerializing โมเดลคำขอของเราควรมีคุณสมบัติสามประเภทที่เหมาะสม: query, ArticlesFromDate และ ArticleToDate เพียงเพื่อให้แน่ใจว่าโครงการของเรามีการจัดการที่ดี ฉันขอแนะนำให้จัดชั้นเรียนนี้ในกลุ่ม แบบจำลอง
ไฟล์อินเทอร์เฟซของ ArticleListRequestModel ควรมีลักษณะดังนี้:
#import "MTLModel.h" #import "Mantle.h" @interface ArticleListRequestModel : MTLModel <MTLJSONSerializing> @property (nonatomic, copy) NSString *query; @property (nonatomic, copy) NSDate *articlesFromDate; @property (nonatomic, copy) NSDate *articlesToDate; @end
ตอนนี้ หากเราค้นหาเอกสารสำหรับจุดสิ้นสุดการค้นหาบทความของเรา หรือดูตารางที่มีพารามิเตอร์คำขอด้านบน เราจะสังเกตเห็นว่าชื่อของตัวแปรในคำขอ API นั้นแตกต่างจากในแบบจำลองคำขอของเรา เสื้อคลุมจัดการสิ่งนี้อย่างมีประสิทธิภาพโดยใช้วิธีการ:
+ (NSDictionary *)JSONKeyPathsByPropertyKey.
ต่อไปนี้คือวิธีการนำวิธีการนี้ไปใช้ในการดำเนินการตามรูปแบบคำขอของเรา:
#import "ArticleListRequestModel.h" @implementation ArticleListRequestModel #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"query": @"q", @"articlesFromDate": @"begin_date", @"articlesToDate": @"end_date" }; } @end
การนำเมธอดนี้ไปใช้ระบุวิธีการแมปคุณสมบัติของโมเดลในการแทนค่า JSON เมื่อวิธีการ JSONKeyPathsByPropertyKey ถูกนำไปใช้ เราสามารถรับการแทนพจนานุกรม JSON ของโมเดลด้วยวิธีคลาส +[MTLJSONAdapter JSONArrayForModels:]
สิ่งหนึ่งที่ยังคงหลงเหลืออยู่ ดังที่เราทราบจากรายการพารามิเตอร์ คือ พารามิเตอร์วันที่ทั้งสองจะต้องอยู่ในรูปแบบ “YYYYMMDD” นี่คือจุดที่ Mantle มีประโยชน์มาก เราสามารถเพิ่มการแปลงค่าที่กำหนดเองสำหรับพร็อพเพอร์ตี้ใดก็ได้โดยใช้เมธอดที่ไม่บังคับ +<propertyName>JSONTransformer
โดยการใช้งานเราจะบอก Mantle ว่าควรแปลงค่าของฟิลด์ JSON เฉพาะอย่างไรในระหว่างการดีซีเรียลไลเซชัน JSON นอกจากนี้เรายังสามารถใช้หม้อแปลงไฟฟ้าแบบย้อนกลับที่จะใช้ในการสร้าง JSON จากแบบจำลองได้ เนื่องจากเราจำเป็นต้องแปลงวัตถุ NSDate เป็นสตริง เราจะใช้คลาส NSDataFormatter ด้วยเช่นกัน นี่คือการใช้งานคลาส ArticleListRequestModel ที่สมบูรณ์:
#import "ArticleListRequestModel.h" @implementation ArticleListRequestModel + (NSDateFormatter *)dateFormatter { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyyMMdd"; return dateFormatter; } #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"query": @"q", @"articlesFromDate": @"begin_date", @"articlesToDate": @"end_date" }; } #pragma mark - JSON Transformers + (NSValueTransformer *)articlesToDateJSONTransformer { return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *dateString, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter dateFromString:dateString]; } reverseBlock:^id(NSDate *date, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter stringFromDate:date]; }]; } + (NSValueTransformer *)articlesFromDateJSONTransformer { return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *dateString, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter dateFromString:dateString]; } reverseBlock:^id(NSDate *date, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter stringFromDate:date]; }]; } @end
คุณสมบัติที่ยอดเยี่ยมอีกประการของ Mantle คือโมเดลทั้งหมดเหล่านี้สอดคล้องกับโปรโตคอล NSCoding รวมถึงใช้วิธี isEqual และ hash
ดังที่เราได้เห็นแล้ว ผลลัพธ์ JSON จากการเรียก API มีอาร์เรย์ของอ็อบเจ็กต์ที่แสดงบทความ หากเราต้องการสร้างแบบจำลองการตอบสนองนี้โดยใช้ Mantle เราจะต้องสร้างแบบจำลองข้อมูลสองแบบแยกกัน หนึ่งจะสร้างแบบจำลองวัตถุที่เป็นตัวแทนของบทความ (องค์ประกอบอาร์เรย์ เอกสาร ) และอีกส่วนหนึ่งจะสร้างแบบจำลองการตอบสนอง JSON ทั้งหมดยกเว้นองค์ประกอบของอาร์เรย์เอกสาร ตอนนี้ เราไม่ต้องแมปคุณสมบัติแต่ละรายการจาก JSON ขาเข้ากับโมเดลข้อมูลของเรา สมมติว่าเราสนใจเฉพาะวัตถุบทความสองฟิลด์เท่านั้น และเหล่านั้นจะเป็น lead_paragraph และ web_url คลาส ArticleModel นั้นค่อนข้างตรงไปตรงมาในการใช้งาน ดังที่เราเห็นด้านล่าง
#import "MTLModel.h" #import <Mantle/Mantle.h> @interface ArticleModel : MTLModel <MTLJSONSerializing> @property (nonatomic, copy) NSString *leadParagraph; @property (nonatomic, copy) NSString *url; @end
#import "ArticleModel.h" @implementation ArticleModel #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"leadParagraph": @"lead_paragraph", @"url": @"web_url" }; } @end
เมื่อกำหนดแบบจำลองบทความแล้ว เราสามารถดำเนินการนิยามแบบจำลองการตอบสนองให้เสร็จสิ้นได้โดยการสร้างแบบจำลองสำหรับรายการบทความ โมเดลการตอบสนองของคลาส ArticleList จะเป็นอย่างไร

#import "MTLModel.h" #import <Mantle/Mantle.h> #import "ArticleModel.h" @interface ArticleListResponseModel : MTLModel <MTLJSONSerializing> @property (nonatomic, copy) NSArray *articles; @property (nonatomic, copy) NSString *status; @end
#import "ArticleListResponseModel.h" @class ArticleModel; @implementation ArticleListResponseModel #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"articles" : @"response.docs", @"status" : @"status" }; } #pragma mark - JSON Transformer + (NSValueTransformer *)articlesJSONTransformer { return [MTLJSONAdapter arrayTransformerWithModelClass:ArticleModel.class]; } @end
คลาสนี้มีเพียงสองคุณสมบัติ: สถานะ และ บทความ หากเราเปรียบเทียบกับการตอบสนองจากปลายทาง เราจะเห็นว่าลิขสิทธิ์แอตทริบิวต์ JSON ที่สามจะไม่ถูกแมปเข้ากับโมเดลการตอบสนอง หากเราดูวิธี ArticleJSONTransformer เราจะเห็นว่ามันคืนค่า Transformer สำหรับอาร์เรย์ที่มีอ็อบเจ็กต์ของคลาส ArticleModel
นอกจากนี้ยังเป็นที่น่าสังเกตว่าในวิธีการ JSONKeyPathsByPropertyKey บทความ คุณสมบัติของแบบจำลองจะสอดคล้องกับเอกสารอาร์เรย์ที่ซ้อนอยู่ภายใน การตอบสนอง แอตทริบิวต์ JSON
ถึงตอนนี้ เราควรจะมีคลาสโมเดลสามคลาสที่ใช้งาน: ArticleListRequestModel, ArticleModel และ ArticleListResponseModel
คำขอ API แรก
ตอนนี้เราได้ใช้โมเดลข้อมูลทั้งหมดแล้ว ก็ถึงเวลากลับไปที่คลาส APIManager เพื่อนำเมธอดที่เราจะใช้เพื่อดำเนินการร้องขอ GET ไปยัง API วิธีการ:
- (NSURLSessionDataTask *) getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure
ใช้โมเดลคำขอ ArticleListRequestModel เป็นพารามิเตอร์และส่งคืน ArticleListResponseModel ในกรณีที่ประสบความสำเร็จหรือ NSError เป็นอย่างอื่น การใช้วิธีนี้ใช้ AFNetworking เพื่อดำเนินการร้องขอ GET ไปยัง API โปรดทราบว่าเพื่อให้คำขอ API สำเร็จ เราจำเป็นต้องจัดเตรียมรหัสที่สามารถรับได้ดังที่กล่าวไว้ก่อนหน้านี้ โดยการลงทะเบียนที่ http://developer.nytimes.com
#import "SessionManager.h" #import "ArticleListRequestModel.h" #import "ArticleListResponseModel.h" @interface APIManager : SessionManager - (NSURLSessionDataTask *)getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure; @end
#import "APIManager.h" #import "Mantle.h" static NSString *const kArticlesListPath = @"/svc/search/v2/articlesearch.json"; static NSString *const kApiKey = @"replace this with your own key"; @implementation APIManager - (NSURLSessionDataTask *)getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure{ NSDictionary *parameters = [MTLJSONAdapter JSONDictionaryFromModel:requestModel error:nil]; NSMutableDictionary *parametersWithKey = [[NSMutableDictionary alloc] initWithDictionary:parameters]; [parametersWithKey setObject:kApiKey forKey:@"api-key"]; return [self GET:kArticlesListPath parameters:parametersWithKey success:^(NSURLSessionDataTask *task, id responseObject) { NSDictionary *responseDictionary = (NSDictionary *)responseObject; NSError *error; ArticleListResponseModel *list = [MTLJSONAdapter modelOfClass:ArticleListResponseModel.class fromJSONDictionary:responseDictionary error:&error]; success(list); } failure:^(NSURLSessionDataTask *task, NSError *error) { failure(error); }]; }
มีสองสิ่งที่สำคัญมากที่เกิดขึ้นในการใช้วิธีนี้ ก่อนอื่นมาดูบรรทัดนี้กัน:
NSDictionary *parameters = [MTLJSONAdapter JSONDictionaryFromModel:requestModel error:nil];
สิ่งที่เกิดขึ้นที่นี่คือการใช้วิธีการที่จัดเตรียมโดยคลาส MTLJSONAdapter เราได้รับการแสดง NSDictionary ของโมเดลข้อมูลของเรา การเป็นตัวแทนนั้นสะท้อน JSON ที่จะถูกส่งไปยัง API นี่คือความงดงามของเสื้อคลุม หลังจากใช้งาน เมธอด JSONKeyPathsByPropertyKey และ +<propertyName>JSONTransformer
ในคลาส ArticleListRequestModel แล้ว เราสามารถรับการแสดง JSON ที่ถูกต้องของโมเดลข้อมูลของเราได้ในเวลาไม่นานด้วยโค้ดเพียงบรรทัดเดียว
เสื้อคลุมยังช่วยให้เราสามารถทำการเปลี่ยนแปลงในทิศทางอื่นได้เช่นกัน และนั่นคือสิ่งที่เกิดขึ้นกับข้อมูลที่ได้รับจาก API NSDictionary ที่เราได้รับถูกแมปในวัตถุของคลาส ArticleListResponseModel โดยใช้วิธีคลาสต่อไปนี้:
ArticleListResponseModel *list = [MTLJSONAdapter modelOfClass:ArticleListResponseModel.class fromJSONDictionary:responseDictionary error:&error];
ข้อมูลที่คงอยู่ด้วย Realm
เมื่อเราสามารถดึงข้อมูลจาก API ระยะไกลได้แล้ว ก็ถึงเวลาที่ต้องยืนยัน ตามที่กล่าวไว้ในบทนำ เราจะทำโดยใช้ Realm Realm เป็นฐานข้อมูลมือถือและแทนที่ Core Data และ SQLite ดังที่เราจะเห็นด้านล่าง มันใช้งานง่ายมาก
ในการบันทึกข้อมูลใน Realm ก่อนอื่นเราต้องแค็ปซูลออบเจกต์ที่ได้มาจากคลาส RLMObject สิ่งที่เราต้องทำตอนนี้คือการสร้างคลาสโมเดลที่จะเก็บข้อมูลสำหรับบทความเดี่ยว นี่เป็นวิธีที่ง่ายในการสร้างคลาสดังกล่าว
#import "RLMObject.h" @interface ArticleRealm : RLMObject @property NSString *leadParagraph; @property NSString *url; @end
และนี่อาจเป็นโดยพื้นฐานแล้ว การใช้งานคลาสนี้ยังคงว่างเปล่า โปรดทราบว่าคุณสมบัติในคลาสโมเดลไม่มีแอตทริบิวต์เช่น nonatomic, strong หรือ copy อาณาจักรดูแลสิ่งเหล่านั้นและเราไม่จำเป็นต้องกังวลเกี่ยวกับพวกเขา
เนื่องจากบทความที่เราหามาได้นั้นถูกจำลองด้วย บทความ แบบจำลอง Mante จึงสะดวกที่จะเริ่มต้นวัตถุ ArticleRealm ด้วยวัตถุของ Article ของคลาส ในการทำเช่นนั้น เราจะเพิ่มเมธอด initWithMantleModel ให้กับโมเดล Realm ของเรา นี่คือการใช้งานคลาส ArticleRealm อย่างสมบูรณ์
#import "RLMObject.h" #import "ArticleModel.h" @interface ArticleRealm : RLMObject @property NSString *leadParagraph; @property NSString *url; - (id)initWithMantleModel:(ArticleModel *)articleModel; @end
#import "ArticleRealm.h" @implementation ArticleRealm - (id)initWithMantleModel:(ArticleModel *)articleModel{ self = [super init]; if(!self) return nil; self.leadParagraph = articleModel.leadParagraph; self.url = articleModel.url; return self; } @end
เราโต้ตอบกับฐานข้อมูลโดยใช้วัตถุของคลาส RLMRealm เราสามารถรับวัตถุ RLMRealm ได้อย่างง่ายดายโดยเรียกใช้เมธอด "[RLMRealm defaultRealm]" สิ่งสำคัญคือต้องจำไว้ว่าอ็อบเจกต์ดังกล่าวใช้ได้เฉพาะภายในเธรดที่สร้างขึ้นและไม่สามารถแชร์ข้ามเธรดได้ การเขียนข้อมูลไปยัง Realm ค่อนข้างตรงไปตรงมา การเขียนครั้งเดียวหรือหลายชุดจะต้องทำภายในรายการเขียน นี่คือตัวอย่างการเขียนไปยังฐานข้อมูล:
RLMRealm *realm = [RLMRealm defaultRealm]; ArticleRealm *articleRealm = [ArticleRealm new]; articleRealm.leadParagraph = @"abc"; articleRealm.url = @"sampleUrl"; [realm beginWriteTransaction]; [realm addObject:articleRealm]; [realm commitWriteTransaction];
สิ่งที่เกิดขึ้นที่นี่มีดังต่อไปนี้ ขั้นแรก เราสร้างวัตถุ RLMRealm เพื่อโต้ตอบกับฐานข้อมูล จากนั้นวัตถุโมเดล ArticleRealm จะถูกสร้างขึ้น (โปรดจำไว้ว่ามาจากคลาส RLMRealm ) ในที่สุดเพื่อบันทึก ธุรกรรมการเขียนจะเริ่มต้นขึ้น อ็อบเจ็กต์จะถูกเพิ่มลงในฐานข้อมูล และเมื่อบันทึกแล้ว ธุรกรรมการเขียนก็จะถูกคอมมิต ดังที่เราเห็น ธุรกรรมการเขียนบล็อกเธรดที่เรียกใช้ แม้ว่า Realm จะกล่าวกันว่าเร็วมาก แต่ถ้าเราต้องเพิ่มหลายอ็อบเจ็กต์ลงในฐานข้อมูลภายในธุรกรรมเดียวบนเธรดหลัก อาจทำให้ UI ไม่ตอบสนองจนกว่าธุรกรรมจะเสร็จสิ้น วิธีแก้ปัญหาตามธรรมชาติคือทำธุรกรรมการเขียนบนเธรดพื้นหลัง
คำขอ API และการตอบสนองอย่างต่อเนื่องใน Realm
นี่คือข้อมูลทั้งหมดที่เราต้องการเพื่อยืนยันบทความโดยใช้ Realm มาลองทำคำขอ API โดยใช้เมธอด
- (NSURLSessionDataTask *) getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure
และแบบจำลองคำขอและการตอบสนองของ Mantle เพื่อให้ได้บทความของ New York Times ที่ไม่มีอะไรจะทำ (เช่นในตัวอย่างก่อนหน้า) เกี่ยวกับบาสเก็ตบอลและได้รับการตีพิมพ์ในเจ็ดวันแรกของเดือนมิถุนายน 2015 เมื่อมีรายชื่อบทความดังกล่าวแล้ว เรา จะคงอยู่ในอาณาจักร ด้านล่างเป็นรหัสที่ทำอย่างนั้น มันถูกวางไว้ในวิธี viewDidLoad ของตัวควบคุมมุมมองตารางในแอพของเรา
ArticleListRequestModel *requestModel = [ArticleListRequestModel new]; // (1) requestModel.query = @"Basketball"; requestModel.articlesToDate = [[ArticleListRequestModel dateFormatter] dateFromString:@"20150706"]; requestModel.articlesFromDate = [[ArticleListRequestModel dateFormatter] dateFromString:@"20150701"]; [[APIManager sharedManager] getArticlesWithRequestModel:requestModel // (2) success:^(ArticleListResponseModel *responseModel){ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // (3) @autoreleasepool { RLMRealm *realm = [RLMRealm defaultRealm]; [realm beginWriteTransaction]; [realm deleteAllObjects]; [realm commitWriteTransaction]; [realm beginWriteTransaction]; for(ArticleModel *article in responseModel.articles){ ArticleRealm *articleRealm = [[ArticleRealm alloc] initWithMantleModel:article]; // (4) [realm addObject:articleRealm]; } [realm commitWriteTransaction]; dispatch_async(dispatch_get_main_queue(), ^{ // (5) RLMRealm *realmMainThread = [RLMRealm defaultRealm]; // (6) RLMResults *articles = [ArticleRealm allObjectsInRealm:realmMainThread]; self.articles = articles; // (7) [self.tableView reloadData]; }); } }); } failure:^(NSError *error) { self.articles = [ArticleRealm allObjects]; [self.tableView reloadData]; }];
ขั้นแรก มีการเรียก API (2) ด้วยโมเดลคำขอ (1) ซึ่งส่งคืนโมเดลการตอบกลับที่มีรายการบทความ เพื่อคงบทความเหล่านั้นโดยใช้ Realm เราจำเป็นต้องสร้างวัตถุโมเดล Realm ซึ่งเกิดขึ้นใน for loop (4) สิ่งสำคัญคือต้องสังเกตว่า เนื่องจากหลายอ็อบเจ็กต์ยังคงอยู่ภายในธุรกรรมการเขียนเดียว ธุรกรรมการเขียนนั้นจึงถูกดำเนินการบนเธรดพื้นหลัง (3) ตอนนี้ เมื่อบทความทั้งหมดถูกบันทึกใน Realm เราจะมอบหมายบทความเหล่านั้นให้กับคุณสมบัติ class self.articles (7) เนื่องจากจะเข้าถึงได้ในภายหลังบนเธรดหลักในวิธีแหล่งข้อมูล TableView จึงปลอดภัยที่จะดึงข้อมูลจากฐานข้อมูล Realm บนเธรดหลักด้วย (5) อีกครั้ง ในการเข้าถึงฐานข้อมูลจากเธรดใหม่ ต้องสร้างวัตถุ RLMRealm ใหม่ (6) บนเธรดนั้น
หากการรับบทความใหม่จาก API ล้มเหลวด้วยเหตุผลใดก็ตาม บทความที่มีอยู่จะถูกดึงมาจากที่จัดเก็บในตัวเครื่องในบล็อกความล้มเหลว
ห่อ
ในบทช่วยสอนนี้ เราได้เรียนรู้วิธีกำหนดค่า Mantle ซึ่งเป็นกรอบงานแบบจำลองสำหรับ Cocoa และ Cocoa Touch เพื่อโต้ตอบกับ API ระยะไกล เรายังได้เรียนรู้วิธีคงข้อมูลไว้ภายในเครื่องที่ดึงมาในรูปแบบของวัตถุแบบจำลอง Mantle โดยใช้ฐานข้อมูลมือถือ Realm
ในกรณีที่คุณต้องการลองใช้แอปพลิเคชันนี้ คุณสามารถดึงซอร์สโค้ดจากที่เก็บ GitHub คุณจะต้องสร้างและระบุคีย์ API ของคุณเองก่อนเรียกใช้แอปพลิเคชัน