เครือข่ายแบบรวมศูนย์และแบบแยกส่วน iOS: บทช่วยสอนเกี่ยวกับเครือข่าย AF พร้อมคลาสซิงเกิลตัน
เผยแพร่แล้ว: 2022-03-11เมื่อพูดถึงรูปแบบสถาปัตยกรรม iOS รูปแบบการออกแบบ Model-View-Controller (MVC) นั้นยอดเยี่ยมสำหรับการมีอายุยืนยาวและความสามารถในการบำรุงรักษาของ codebase ของแอปพลิเคชัน อนุญาตให้ใช้ซ้ำหรือเปลี่ยนคลาสได้ง่ายเพื่อรองรับความต้องการที่หลากหลายโดยแยกส่วนออกจากกัน ซึ่งจะช่วยเพิ่มข้อได้เปรียบของ Object Oriented Programming (OOP) ให้ได้มากที่สุด
แม้ว่าสถาปัตยกรรมแอปพลิเคชัน iOS นี้จะทำงานได้ดีในระดับไมโคร (แต่ละหน้าจอ/ส่วนต่างๆ ของแอป) คุณอาจพบว่าตัวเองเพิ่มฟังก์ชันที่คล้ายคลึงกันในหลายรุ่นเมื่อแอปของคุณเติบโตขึ้น ในกรณีต่างๆ เช่น การสร้างเครือข่าย การย้ายลอจิกทั่วไปออกจากคลาสโมเดลของคุณไปยังคลาสตัวช่วย singleton อาจเป็นวิธีที่ดีกว่า ในบทช่วยสอน AFNetworking iOS นี้ ฉันจะสอนวิธีตั้งค่าออบเจ็กต์เครือข่ายซิงเกิลตันแบบรวมศูนย์ที่แยกจากส่วนประกอบ MVC ระดับไมโคร นำกลับมาใช้ใหม่ได้ตลอดแอปพลิเคชันสถาปัตยกรรมแบบแยกส่วน
ปัญหาเกี่ยวกับระบบเครือข่าย iOS
Apple ทำได้ดีมากในการแยกแยะความซับซ้อนมากมายในการจัดการฮาร์ดแวร์มือถือใน iOS SDK ที่ใช้งานง่าย แต่ในบางกรณี เช่น ระบบเครือข่าย บลูทูธ OpenGL และการประมวลผลมัลติมีเดีย คลาสอาจยุ่งยากเนื่องจากเป้าหมายที่จะรักษาไว้ SDK ที่มีความยืดหยุ่น โชคดีที่ชุมชนนักพัฒนา iOS จำนวนมากได้สร้างเฟรมเวิร์กระดับสูงเพื่อลดความซับซ้อนของกรณีการใช้งานทั่วไป เพื่อลดความซับซ้อนของการออกแบบและโครงสร้างแอปพลิเคชัน โปรแกรมเมอร์ที่ดี ใช้แนวทางปฏิบัติที่ดีที่สุดสำหรับสถาปัตยกรรมแอป ios รู้ว่าควรใช้เครื่องมือใด เหตุใดจึงควรใช้ และเมื่อใดควรเขียนเครื่องมือและชั้นเรียนของคุณเองตั้งแต่เริ่มต้น
AFNetworking เป็นตัวอย่างเครือข่ายที่ยอดเยี่ยม และเป็นหนึ่งในเฟรมเวิร์กโอเพ่นซอร์สที่ใช้กันมากที่สุด ซึ่งทำให้งานประจำวันของนักพัฒนาง่ายขึ้น ช่วยลดความซับซ้อนของเครือข่าย RESTful API และสร้างรูปแบบคำขอ/การตอบสนองแบบแยกส่วนพร้อมบล็อกความสำเร็จ ความคืบหน้า และความล้มเหลว ซึ่งช่วยขจัดความจำเป็นในการใช้วิธีการมอบสิทธิ์ของนักพัฒนาและการตั้งค่าคำขอ/การเชื่อมต่อที่กำหนดเอง และสามารถรวมไว้ในชั้นเรียนใดๆ ได้อย่างรวดเร็ว
ปัญหาเกี่ยวกับ AFNetworking
AFNetworking นั้นยอดเยี่ยม แต่โมดูลาร์ของมันยังสามารถนำไปสู่การใช้งานในลักษณะที่กระจัดกระจาย การใช้งานที่ไม่มีประสิทธิภาพทั่วไปอาจรวมถึง:
คำขอเครือข่ายหลายรายการโดยใช้วิธีการและคุณสมบัติที่คล้ายคลึงกันในตัวควบคุมมุมมองเดียว
คำขอที่เกือบจะเหมือนกันในตัวควบคุมมุมมองหลายตัวซึ่งนำไปสู่ตัวแปรทั่วไปแบบกระจายที่สามารถออกจากการซิงโครไนซ์ได้
คำขอเครือข่ายในคลาสสำหรับข้อมูลที่ไม่เกี่ยวข้องกับคลาสนั้น
สำหรับแอปพลิเคชันที่มีจำนวนการดูจำกัด การเรียกใช้ API เพียงไม่กี่ครั้งเพื่อนำไปใช้ และแอปพลิเคชันที่ไม่น่าจะเปลี่ยนแปลงบ่อย สิ่งนี้อาจไม่เป็นปัญหามากนัก อย่างไรก็ตาม มีแนวโน้มมากกว่าที่คุณกำลังคิดใหญ่และมีแผนอัปเดตอีกหลายปี หากกรณีของคุณเป็นกรณีหลัง คุณอาจจะต้องจัดการ:
การกำหนดเวอร์ชัน API เพื่อรองรับแอปพลิเคชันหลายรุ่น
การเพิ่มพารามิเตอร์ใหม่หรือการเปลี่ยนแปลงพารามิเตอร์ที่มีอยู่เมื่อเวลาผ่านไปเพื่อขยายความสามารถ
การใช้งาน API ใหม่ทั้งหมด
หากรหัสเครือข่ายของคุณกระจัดกระจายไปทั่วฐานรหัส ตอนนี้อาจเป็นฝันร้าย หวังว่าคุณจะมีพารามิเตอร์อย่างน้อยบางตัวที่กำหนดไว้แบบคงที่ในส่วนหัวทั่วไป แต่ถึงกระนั้นคุณก็อาจสัมผัสหลายสิบคลาสสำหรับการเปลี่ยนแปลงเล็กน้อยที่สุด
เราจะจัดการกับข้อจำกัดของ AFNetworking ได้อย่างไร?
สร้างเครือข่ายซิงเกิลตันเพื่อรวมศูนย์การจัดการคำขอ การตอบสนอง และพารามิเตอร์
ออบเจ็กต์ซิงเกิลตันให้จุดเข้าถึงทรัพยากรของคลาสเดียวกันทั่วโลก Singletons ใช้ในสถานการณ์ที่ต้องการจุดควบคุมเพียงจุดเดียว เช่น กับคลาสที่ให้บริการหรือทรัพยากรทั่วไป คุณได้รับอินสแตนซ์ส่วนกลางจากคลาสซิงเกิลตันผ่านวิธีการจากโรงงาน - แอปเปิ้ล
ดังนั้น singleton คือคลาสที่คุณจะมีเพียงหนึ่งอินสแตนซ์ในแอปพลิเคชันที่มีอยู่ตลอดอายุของแอปพลิเคชัน นอกจากนี้ เนื่องจากเราทราบดีว่ามีเพียงอินสแตนซ์เดียวเท่านั้น จึงสามารถเข้าถึงได้ง่ายโดยคลาสอื่นๆ ที่ต้องการเข้าถึงวิธีการหรือคุณสมบัติของคลาส
นี่คือเหตุผลที่เราควรใช้ซิงเกิลตันสำหรับการสร้างเครือข่าย:
มันถูกเตรียมใช้งานแบบสแตติก ดังนั้น เมื่อสร้างแล้ว จะมีเมธอดและคุณสมบัติเดียวกันกับคลาสใดๆ ที่พยายามเข้าถึง ไม่มีโอกาสเกิดปัญหาการซิงโครไนซ์แบบแปลกๆ หรือการร้องขอข้อมูลจากอินสแตนซ์ที่ไม่ถูกต้องของคลาส
คุณสามารถจำกัดการเรียก API ของคุณให้อยู่ภายใต้ขีดจำกัดอัตราได้ (เช่น เมื่อคุณต้องเก็บคำขอ API ของคุณให้ต่ำกว่า 5 ต่อวินาที)
คุณสมบัติคงที่ เช่น ชื่อโฮสต์ หมายเลขพอร์ต ปลายทาง เวอร์ชัน API ประเภทอุปกรณ์ รหัสถาวร ขนาดหน้าจอ ฯลฯ สามารถอยู่ร่วมกันได้ ดังนั้นการเปลี่ยนแปลงเพียงครั้งเดียวจะส่งผลต่อคำขอเครือข่ายทั้งหมด
คุณสมบัติทั่วไปสามารถนำมาใช้ซ้ำได้ระหว่างคำขอเครือข่ายจำนวนมาก
วัตถุซิงเกิลตันไม่ใช้หน่วยความจำจนกว่าจะสร้างอินสแตนซ์ สิ่งนี้มีประโยชน์สำหรับซิงเกิลตันที่มีกรณีการใช้งานที่เฉพาะเจาะจงซึ่งผู้ใช้บางคนอาจไม่ต้องการ เช่น การจัดการการแคสต์วิดีโอไปยัง Chromecast หากไม่มีอุปกรณ์
คำขอเครือข่ายสามารถแยกออกจากมุมมองและตัวควบคุมได้อย่างสมบูรณ์ เพื่อให้สามารถดำเนินการต่อได้แม้ว่ามุมมองและตัวควบคุมจะถูกทำลาย
การบันทึกเครือข่ายสามารถรวมศูนย์และทำให้ง่ายขึ้นได้
เหตุการณ์ทั่วไปสำหรับความล้มเหลว เช่น การแจ้งเตือน สามารถใช้ซ้ำได้สำหรับคำขอทั้งหมด
โครงสร้างหลักของซิงเกิลตันดังกล่าวสามารถนำมาใช้ซ้ำได้ในหลายโปรเจ็กต์โดยมีการเปลี่ยนแปลงคุณสมบัติสแตติกระดับบนสุดอย่างง่าย
เหตุผลบางประการที่จะไม่ใช้ซิงเกิลตัน:
สามารถใช้มากเกินไปเพื่อให้มีความรับผิดชอบหลายอย่างในชั้นเรียนเดียว ตัวอย่างเช่น วิธีการประมวลผลวิดีโออาจผสมกับวิธีเครือข่ายหรือวิธีสถานะผู้ใช้ นี่อาจเป็นแนวทางในการออกแบบที่ไม่ดีและนำไปสู่โค้ดที่เข้าใจยาก แต่ควรสร้างซิงเกิลตันหลายชุดที่มีความรับผิดชอบเฉพาะ
Singletons ไม่สามารถจัดคลาสย่อยได้
Singletons สามารถซ่อนการพึ่งพาและทำให้เป็นแบบแยกส่วนน้อยลง ตัวอย่างเช่น ถ้าซิงเกิลตันถูกลบออกและคลาสไม่มีการนำเข้าที่ซิงเกิลตันนำเข้า อาจนำไปสู่ปัญหาในอนาคต (โดยเฉพาะอย่างยิ่งหากมีการพึ่งพาไลบรารีภายนอก)
คลาสสามารถแก้ไขคุณสมบัติที่ใช้ร่วมกันใน singletons ระหว่างการดำเนินการที่ยาวนานซึ่งไม่คาดคิดในคลาสอื่น หากไม่มีความคิดที่ถูกต้อง ผลลัพธ์อาจแตกต่างกันไป
หน่วยความจำรั่วในซิงเกิลตันอาจกลายเป็นปัญหาสำคัญได้ เนื่องจากซิงเกิลตันเองไม่เคยยกเลิกการจัดสรร
อย่างไรก็ตาม การใช้แนวทางปฏิบัติที่ดีที่สุดสำหรับสถาปัตยกรรมแอป iOS เชิงลบเหล่านี้สามารถบรรเทาได้ แนวทางปฏิบัติที่ดีที่สุดบางประการ ได้แก่:
ซิงเกิลตันแต่ละคนควรรับผิดชอบเดียว
อย่าใช้ซิงเกิลตันเพื่อจัดเก็บข้อมูลที่จะมีการเปลี่ยนแปลงอย่างรวดเร็วโดยหลายคลาสหรือหลายเธรด หากคุณต้องการความแม่นยำสูง
สร้างซิงเกิลตันเพื่อเปิด/ปิดคุณลักษณะตามการขึ้นต่อกันที่มี
อย่าจัดเก็บข้อมูลจำนวนมากในคุณสมบัติซิงเกิลตัน เนื่องจากข้อมูลดังกล่าวจะคงอยู่ตลอดอายุของแอปพลิเคชันของคุณ (เว้นแต่จะมีการจัดการด้วยตนเอง)
ตัวอย่างซิงเกิลตันอย่างง่ายด้วย AFNetworking
ขั้นแรก ให้เพิ่ม AFNetworking ให้กับโปรเจ็กต์ของคุณ เป็นข้อกำหนดเบื้องต้น วิธีที่ง่ายที่สุดคือผ่าน Cocoapods และพบคำแนะนำในหน้า GitHub
ในขณะที่คุณทำอยู่ ฉันขอแนะนำให้เพิ่ม UIAlertController+Blocks
และ MBProgressHUD
(เพิ่มได้อย่างง่ายดายอีกครั้งด้วย CocoaPods) สิ่งเหล่านี้เป็นทางเลือกที่ชัดเจน แต่จะทำให้ความคืบหน้าและการแจ้งเตือนง่ายขึ้นอย่างมาก หากคุณต้องการนำไปใช้ในซิงเกิลตันในหน้าต่าง AppDelegate
เมื่อเพิ่ม AFNetworking
แล้ว ให้เริ่มต้นด้วยการสร้าง Cocoa Touch Class ใหม่ที่เรียกว่า NetworkManager
เป็นคลาสย่อยของ NSObject
เพิ่มวิธีการเรียนสำหรับการเข้าถึงผู้จัดการ ไฟล์ NetworkManager.h
ของคุณควรมีลักษณะเหมือนโค้ดด้านล่าง:
#import <Foundation/Foundation.h> #import “AFNetworking.h” @interface NetworkManager : NSObject + (id)sharedManager; @end
ถัดไป ใช้วิธีการเริ่มต้นพื้นฐานสำหรับซิงเกิลตันและนำเข้าส่วนหัว AFNetworking การใช้งานคลาสของคุณควรมีลักษณะดังนี้ (หมายเหตุ: นี่ถือว่าคุณกำลังใช้การนับการอ้างอิงอัตโนมัติ):
#import "NetworkManager.h" @interface NetworkManager() @end @implementation NetworkManager #pragma mark - #pragma mark Constructors static NetworkManager *sharedManager = nil; + (NetworkManager*)sharedManager { static dispatch_once_t once; dispatch_once(&once, ^ { sharedManager = [[NetworkManager alloc] init]; }); return sharedManager; } - (id)init { if ((self = [super init])) { } return self; } @end
ยอดเยี่ยม! ตอนนี้เรากำลังทำอาหารและพร้อมที่จะเพิ่มคุณสมบัติและวิธีการ เพื่อเป็นการทดสอบอย่างรวดเร็วเพื่อทำความเข้าใจวิธีเข้าถึงซิงเกิลตัน ให้เพิ่มสิ่งต่อไปนี้ใน NetworkManager.h
:
@property NSString *appID; - (void)test;
และต่อไปนี้ไปยัง NetworkManager.m
:
#define HOST @”http://www.apitesting.dev/” static const in port = 80; … @implementation NetworkManager … //Set an initial property to init: - (id)init { if ((self = [super init])) { self.appID = @”1”; } return self; } - (void)test { NSLog(@”Testing out the networking singleton for appID: %@, HOST: %@, and PORT: %d”, self.appID, HOST, port); }
จากนั้นในไฟล์ ViewController.m
หลักของเรา (หรืออะไรก็ตามที่คุณมี) นำเข้า NetworkManager.h
จากนั้นใน viewDidLoad
เพิ่ม:
[[NetworkManager sharedManager] test];
เปิดแอพและคุณควรเห็นสิ่งต่อไปนี้ในผลลัพธ์:
Testing our the networking singleton for appID: 1, HOST: http://www.apitesting.dev/, and PORT: 80
ตกลง ดังนั้นคุณจะไม่ผสม #define
, static const และ @property
พร้อมกันเช่นนี้ แต่เพียงแสดงตัวเลือกของคุณให้ชัดเจน “const คงที่” เป็นการประกาศที่ดีกว่าสำหรับความปลอดภัยประเภท แต่ #define
อาจมีประโยชน์ในการสร้างสตริง เนื่องจากอนุญาตให้ใช้มาโคร สำหรับสิ่งที่คุ้มค่า ฉันกำลังใช้ #define
เพื่อความกระชับในสถานการณ์นี้ เว้นแต่ว่าคุณกำลังใช้พอยน์เตอร์ ไม่มีความแตกต่างในทางปฏิบัติระหว่างแนวทางการประกาศเหล่านี้มากนัก
เมื่อคุณเข้าใจ #defines
, ค่าคงที่, คุณสมบัติ และวิธีการ เราสามารถลบสิ่งเหล่านั้นออกและไปยังตัวอย่างที่เกี่ยวข้องมากขึ้นได้
ตัวอย่างเครือข่าย
ลองนึกภาพแอปพลิเคชันที่ผู้ใช้ต้องลงชื่อเข้าใช้เพื่อเข้าถึงอะไรก็ได้ เมื่อเปิดแอป เราจะตรวจสอบว่าเราได้บันทึกโทเค็นการตรวจสอบสิทธิ์หรือไม่ และหากเป็นเช่นนั้น ให้ดำเนินการขอ GET ไปยัง API ของเราเพื่อดูว่าโทเค็นหมดอายุหรือไม่
ใน AppDelegate.m
ให้ลงทะเบียนค่าเริ่มต้นสำหรับโทเค็นของเรา:
+ (void)initialize { NSDictionary *defaults = [NSDictionary dictionaryWithObjectsAndKeys:@"", @"token", nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; }
เราจะเพิ่มการตรวจสอบโทเค็นใน NetworkManager และรับข้อเสนอแนะเกี่ยวกับการตรวจสอบผ่านการบล็อกการเสร็จสิ้น คุณสามารถออกแบบบล็อกความสำเร็จเหล่านี้ได้ตามต้องการ ในตัวอย่างนี้ ฉันใช้ความสำเร็จกับข้อมูลออบเจ็กต์การตอบกลับและความล้มเหลวกับสตริงการตอบกลับข้อผิดพลาดและรหัสสถานะ หมายเหตุ: ความล้มเหลวอาจถูกละเว้นหากไม่มีความสำคัญกับฝ่ายรับ เช่น การเพิ่มมูลค่าในการวิเคราะห์

NetworkManager.h
ด้านบน @interface
:
typedef void (^NetworkManagerSuccess)(id responseObject); typedef void (^NetworkManagerFailure)(NSString *failureReason, NSInteger statusCode);
ใน @อินเทอร์เฟซ:
@property (ไม่ใช่อะตอมที่แข็งแกร่ง) AFHTTPSessionManager *networkingManager;
- (void)tokenCheckWithSuccess:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure;
NetworkManager.m:
กำหนด BASE_URL ของเรา:
#define ENABLE_SSL 1 #define HOST @"http://www.apitesting.dev/" #define PROTOCOL (ENABLE_SSL ? @"https://" : @"http://") #define PORT @"80" #define BASE_URL [NSString stringWithFormat:@"%@%@:%@", PROTOCOL, HOST, PORT]
เราจะเพิ่มวิธีการช่วยเหลือสองสามวิธีเพื่อทำให้คำขอตรวจสอบสิทธิ์ง่ายขึ้นรวมถึงข้อผิดพลาดในการแยกวิเคราะห์ (ตัวอย่างนี้ใช้โทเค็นเว็บ JSON):
- (AFHTTPSessionManager*)getNetworkingManagerWithToken:(NSString*)token { if (self.networkingManager == nil) { self.networkingManager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:BASE_URL]]; if (token != nil && [token length] > 0) { NSString *headerToken = [NSString stringWithFormat:@"%@ %@", @"JWT", token]; [self.networkingManager.requestSerializer setValue:headerToken forHTTPHeaderField:@"Authorization"]; // Example - [networkingManager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; } self.networkingManager.requestSerializer = [AFJSONRequestSerializer serializer]; self.networkingManager.responseSerializer.acceptableContentTypes = [self.networkingManager.responseSerializer.acceptableContentTypes setByAddingObjectsFromArray:@[@"text/html", @"application/json", @"text/json"]]; self.networkingManager.securityPolicy = [self getSecurityPolicy]; } return self.networkingManager; } - (id)getSecurityPolicy { return [AFSecurityPolicy defaultPolicy]; /* Example - AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; [policy setAllowInvalidCertificates:YES]; [policy setValidatesDomainName:NO]; return policy; */ } - (NSString*)getError:(NSError*)error { if (error != nil) { NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil]; if (responseObject != nil && [responseObject isKindOfClass:[NSDictionary class]] && [responseObject objectForKey:@"message"] != nil && [[responseObject objectForKey:@"message"] length] > 0) { return [responseObject objectForKey:@"message"]; } } return @"Server Error. Please try again later"; }
หากคุณเพิ่ม MBProgressHUD คุณสามารถใช้ได้ที่นี่:
#import "MBProgressHUD.h" @interface NetworkManager() @property (nonatomic, strong) MBProgressHUD *progressHUD; @end … - (void)showProgressHUD { [self hideProgressHUD]; self.progressHUD = [MBProgressHUD showHUDAddedTo:[[UIApplication sharedApplication] delegate].window animated:YES]; [self.progressHUD removeFromSuperViewOnHide]; self.progressHUD.bezelView.color = [UIColor colorWithWhite:0.0 alpha:1.0]; self.progressHUD.contentColor = [UIColor whiteColor]; } - (void)hideProgressHUD { if (self.progressHUD != nil) { [self.progressHUD hideAnimated:YES]; [self.progressHUD removeFromSuperview]; self.progressHUD = nil; } }
และคำขอตรวจสอบโทเค็นของเรา:
- (void)tokenCheckWithSuccess:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *token = [defaults objectForKey:@"token"]; if (token == nil || [token length] == 0) { if (failure != nil) { failure(@"Invalid Token", -1); } return; } [self showProgressHUD]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; [[self getNetworkingManagerWithToken:token] GET:@"/checktoken" parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) { [self hideProgressHUD]; if (success != nil) { success(responseObject); } } failure:^(NSURLSessionTask *operation, NSError *error) { [self hideProgressHUD]; NSString *errorMessage = [self getError:error]; if (failure != nil) { failure(errorMessage, ((NSHTTPURLResponse*)operation.response).statusCode); } }]; }
ในเมธอด ViewController.m viewWillAppear เราจะเรียกเมธอดซิงเกิลตันนี้ สังเกตความเรียบง่ายของคำขอและการใช้งานเพียงเล็กน้อยที่ฝั่ง View Controller
[[NetworkManager sharedManager] tokenCheckWithSuccess:^(id responseObject) { // Allow User Access and load content //[self loadContent]; } failure:^(NSString *failureReason, NSInteger statusCode) { // Logout user if logged in and deny access and show login view //[self showLoginView]; }];
แค่นั้นแหละ! สังเกตว่าข้อมูลโค้ดนี้สามารถใช้ได้จริงในแอปพลิเคชันใดๆ ที่จำเป็นต้องตรวจสอบการพิสูจน์ตัวตนเมื่อเปิดใช้ได้อย่างไร
ในทำนองเดียวกัน เราสามารถจัดการคำขอ POST สำหรับการเข้าสู่ระบบ: NetworkManager.h:
- (void)authenticateWithEmail:(NSString*)email password:(NSString*)password success:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure;
NetworkManager.m:
- (void)authenticateWithEmail:(NSString*)email password:(NSString*)password success:(NetworkManagerSuccess)success failure:(NetworkManagerFailure)failure { if (email != nil && [email length] > 0 && password != nil && [password length] > 0) { [self showProgressHUD]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; [params setObject:email forKey:@"email"]; [params setObject:password forKey:@"password"]; [[self getNetworkingManagerWithToken:nil] POST:@"/authenticate" parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) { [self hideProgressHUD]; if (success != nil) { success(responseObject); } } failure:^(NSURLSessionTask *operation, NSError *error) { [self hideProgressHUD]; NSString *errorMessage = [self getError:error]; if (failure != nil) { failure(errorMessage, ((NSHTTPURLResponse*)operation.response).statusCode); } }]; } else { if (failure != nil) { failure(@"Email and Password Required", -1); } } }
เราสามารถจินตนาการได้ที่นี่และเพิ่มการแจ้งเตือนด้วย AlertController+Blocks บนหน้าต่าง AppDelegate หรือเพียงแค่ส่งวัตถุที่ล้มเหลวกลับไปที่ตัวควบคุมการดู นอกจากนี้ เราสามารถบันทึกข้อมูลรับรองผู้ใช้ที่นี่ หรือให้ตัวควบคุมการดูจัดการแทนก็ได้ โดยปกติ ฉันจะใช้ UserManager singleton แยกต่างหากที่จัดการข้อมูลรับรองและการอนุญาตที่สามารถสื่อสารกับ NetworkManager ได้โดยตรง (ความชอบส่วนตัว)
อีกครั้งที่ด้านตัวควบคุมการดูนั้นเรียบง่ายมาก:
- (void)loginUser { NSString *email = @"[email protected]"; NSString *password = @"SomeSillyEasyPassword555"; [[NetworkManager sharedManager] authenticateWithEmail:email password:password success:^(id responseObject) { // Save User Credentials and show content } failure:^(NSString *failureReason, NSInteger statusCode) { // Explain to user why authentication failed }]; }
อ๊ะ! เราลืมเวอร์ชัน API และส่งประเภทอุปกรณ์ นอกจากนี้ เราได้อัปเดตปลายทางจาก “/checktoken” เป็น “/token” เนื่องจากเรารวมศูนย์เครือข่ายของเรา การอัปเดตนี้จึงง่ายมาก เราไม่จำเป็นต้องขุดคุ้ยรหัสของเรา เนื่องจากเราจะใช้พารามิเตอร์เหล่านี้กับคำขอทั้งหมด เราจะสร้างตัวช่วย
#define API_VERSION @"1.0" #define DEVICE_TYPE UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? @"tablet" : @"phone" - (NSMutableDictionary*)getBaseParams { NSMutableDictionary *baseParams = [NSMutableDictionary dictionary]; [baseParams setObject:@"version" forKey:API_VERSION]; [baseParams setObject:@"device_type" forKey:DEVICE_TYPE]; return baseParams; }
สามารถเพิ่มพารามิเตอร์ทั่วไปจำนวนเท่าใดก็ได้ในอนาคต จากนั้นเราสามารถอัปเดตการตรวจสอบโทเค็นและวิธีตรวจสอบสิทธิ์ได้ดังนี้:
… NSMutableDictionary *params = [self getBaseParams]; [[self getNetworkingManagerWithToken:token] GET:@"/checktoken" parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) { … … NSMutableDictionary *params = [self getBaseParams]; [params setObject:email forKey:@"email"]; [params setObject:password forKey:@"password"]; [[self getNetworkingManagerWithToken:nil] POST:@"/authenticate" parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
สรุปการสอน AFNetworking ของเรา
เราจะหยุดที่นี่ แต่อย่างที่คุณเห็น เราได้รวมศูนย์พารามิเตอร์และวิธีการเครือข่ายทั่วไปในตัวจัดการซิงเกิลตัน ซึ่งทำให้การใช้งานตัวควบคุมมุมมองของเราง่ายขึ้นอย่างมาก การอัปเดตในอนาคตจะง่ายและรวดเร็ว และที่สำคัญที่สุดคือทำให้เครือข่ายของเราแตกต่างจากประสบการณ์ของผู้ใช้ ครั้งหน้าทีมออกแบบขอให้ยกเครื่อง UI/UX เราจะรู้ว่างานของเราเสร็จเรียบร้อยแล้วในด้านเครือข่าย!
ในบทความนี้ เราเน้นที่การสร้างเครือข่ายแบบซิงเกิลตัน แต่หลักการเดียวกันนี้สามารถนำไปใช้กับฟังก์ชันส่วนกลางอื่นๆ ได้มากมาย เช่น:
- การจัดการสถานะผู้ใช้และการอนุญาต
- การกำหนดเส้นทางการแตะเพื่อนำทางแอป
- การจัดการวิดีโอและเสียง
- การวิเคราะห์
- การแจ้งเตือน
- อุปกรณ์ต่อพ่วง
- และอีกมากมาย...
นอกจากนี้เรายังมุ่งเน้นไปที่สถาปัตยกรรมแอปพลิเคชัน iOS แต่สิ่งนี้สามารถขยายไปยัง Android และแม้แต่ JavaScript ได้อย่างง่ายดาย เป็นโบนัสโดยการสร้างโค้ดที่มีการกำหนดสูงและเน้นฟังก์ชันทำให้การโยกย้ายแอปไปยังแพลตฟอร์มใหม่ทำได้เร็วขึ้นมาก
โดยสรุป ด้วยการใช้เวลาเพิ่มขึ้นเล็กน้อยในการวางแผนโครงการช่วงแรกๆ เพื่อสร้างวิธีซิงเกิลตันที่สำคัญ เช่น ตัวอย่างเครือข่ายด้านบน รหัสในอนาคตของคุณจะสะอาดขึ้น ง่ายขึ้น และสามารถบำรุงรักษาได้มากขึ้น