MVVM+RAC寫的一個(gè)登錄demo

MVVM的結(jié)構(gòu)

我們將MVCController中的邏輯模塊抽取到ViewModel中,使Controller只負(fù)責(zé)界面顯示,這里Controller通過綁定與ViewModel進(jìn)行數(shù)據(jù)通知.即:

model層望门,API請(qǐng)求的原始數(shù)據(jù)
view層,視圖展示呆万,由viewController來控制
viewModel層蚓挤,負(fù)責(zé)業(yè)務(wù)處理和數(shù)據(jù)轉(zhuǎn)化

MVVM的特點(diǎn)

  • MVVM方便測(cè)試
  • 方便業(yè)務(wù)的復(fù)用
  • 方便對(duì)職責(zé)進(jìn)行劃分,例如可以讓初級(jí)開發(fā)開發(fā)UI,高級(jí)開發(fā)開發(fā)邏輯
  • 代碼更加優(yōu)雅,增加可維護(hù)性

ReactiveCocoa

本文使用ReactiveCocoa來完成了綁定這個(gè)功能,但使用它之前首先需要了解他的幾個(gè)特點(diǎn):

  • 學(xué)習(xí)成本很高
  • 調(diào)試需要較高的技巧
  • 這是一個(gè)很重型的框架,幾乎替代了蘋果官方所有的事件機(jī)制
  • 每個(gè)人的代碼風(fēng)格都不盡相同,使用該框架需要團(tuán)隊(duì)成員幾乎每天都要互相review團(tuán)隊(duì)成員的代碼
  • 好處當(dāng)然也是有的:響應(yīng)式,函數(shù)式,高聚合,低耦合

代碼分析

需求: 當(dāng)用戶輸入完手機(jī)號(hào)與四位驗(yàn)證碼時(shí),進(jìn)行手機(jī)號(hào)規(guī)則校驗(yàn),通過后進(jìn)行登錄請(qǐng)求

Controller層

我們可以看到,Controller層僅僅是單向綁定了輸入框與ViewModel的字符屬性,代碼已經(jīng)很簡(jiǎn)潔了.

    #import "MRCLoginController.h"
    #import "MRCLoginViewModel.h"

    @interface MRCLoginController ()
    @property (weak, nonatomic) IBOutlet UITextField *mobileNumTF;
    @property (weak, nonatomic) IBOutlet UITextField *smsCodeTF;
    @property (strong, nonatomic) MRCLoginViewModel *viewModel;

    @end

    @implementation MRCLoginController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
        
        self.viewModel = [[MRCLoginViewModel alloc] init];
        
        //綁定
        [self p_bindViewModel];
    }

    #pragma mark - bind
    - (void)p_bindViewModel{
        //電話號(hào)碼
        RAC(self.viewModel,mobileNum) = self.mobileNumTF.rac_textSignal;
        //驗(yàn)證碼
        RAC(self.viewModel,smsCode) = self.smsCodeTF.rac_textSignal;
    }

    @end

ViewModel層

  • 1頭文件

頭文件這里command在外界并沒與用上,但其他viewModel可能會(huì)用上所以還是拿了出來.

    #import "BaseViewModel.h"

    @interface MRCLoginViewModel : BaseViewModel

    @property (nonatomic,copy)NSString *mobileNum;
    @property (nonatomic,copy)NSString *smsCode;
    @property (nonatomic,strong,readonly) RACCommand *loginCommand;

    @end
  • 2實(shí)現(xiàn)文件

這里有幾個(gè)坑需要說一下:

  • RAC的KVO寫法不能觀察系統(tǒng)的只讀屬性(其實(shí)OC也不能)
  • 觀察對(duì)象必須賦初值,否則可能有很奇怪的問題
    #import "MRCLoginViewModel.h"
    #import "FYRequestTool.h"
    #import "SimulateIDFA.h"

    @interface MRCLoginViewModel ()

    @property (nonatomic,strong,readwrite) RACCommand *loginCommand;

    @end

    @implementation MRCLoginViewModel

    - (instancetype)init{
        
        if (self = [super init]) {
            //這里字符串必須初始化!,否則數(shù)組會(huì)崩潰
            self.mobileNum = @"";
            self.smsCode = @"";
            RACSignal * mobileNumSignal = [RACObserve(self, mobileNum) filter:^BOOL(NSString * value) {
                return value.length == 11;
            }];
            
            RACSignal * smsCodeSignal =[RACObserve(self, smsCode) filter:^BOOL(NSString * value) {
                return value.length == 4;
            }];
            
            @weakify(self);
            [[RACSignal combineLatest:@[mobileNumSignal,smsCodeSignal]] subscribeNext:^(id x) {
                @strongify(self);
                NSLog(@"觸發(fā)請(qǐng)求");
                [self.loginCommand execute:nil];
            }];
            
        }
        return self;
    }

    #pragma mark - get && set
    - (RACCommand *)loginCommand{
        
        if (nil == _loginCommand) {
            @weakify(self);
            _loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
                @strongify(self);
                
                //這里位數(shù)以及驗(yàn)證碼在控制器那里已經(jīng)篩選過了,這里只是做個(gè)簡(jiǎn)單的例子,可以在這里對(duì)手機(jī)正則等進(jìn)行校驗(yàn)
                //最好的寫法:button.rac_command = viewmodel.loginCommand...把位數(shù)判斷移到這里
                if (self.mobileNum.length != 11) {
                    return [RACSignal error:[NSError errorWithDomain:@"" code:10 userInfo:@{@"errorInfo":@"手機(jī)號(hào)碼位數(shù)不對(duì)"}]];
                }
                if (self.smsCode.length != 4) {
                    return [RACSignal error:[NSError errorWithDomain:@"" code:20 userInfo:@{@"errorInfo":@"驗(yàn)證碼位數(shù)不對(duì)"}]];
                }
                
                return [self loginSignalWithMobileNum:self.mobileNum smsCode:self.smsCode];
            }];
                
        }
        return _loginCommand;
    }

    #pragma mark - private

    - (RACSignal *)loginSignalWithMobileNum:(NSString *)mobileNo smsCode:(NSString *)authCodeSMS{
        
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            //添加deviceID參數(shù)
            NSMutableDictionary *params = [[NSMutableDictionary alloc]init];
            [params setObject:[self deviceNo] forKey:@"deviceNo"];
            NSDictionary * dict = @{@"mobileNo":mobileNo,
                                    @"authCodeSMS":authCodeSMS};
            NSMutableDictionary *newParams = [dict mutableCopy];
            [newParams addEntriesFromDictionary:params];
            
            //網(wǎng)絡(luò)請(qǐng)求
            [FYRequestTool POST:@"" parameters:newParams progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                //發(fā)送成功內(nèi)容(需要什么發(fā)什么,也可以直接給單例賦值)
                [subscriber sendNext:responseObject];
                [subscriber sendCompleted];
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                [subscriber sendError:error];
            }];
            
            //完成信號(hào)后取消
            return [RACDisposable disposableWithBlock:^{
                [FYRequestTool cancel];
            }];
            
        }];
    }
    - (NSString *)deviceNo
    {
        return [SimulateIDFA createSimulateIDFA];
        
    }

    @end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末磺送,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子灿意,更是在濱河造成了極大的恐慌估灿,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缤剧,死亡現(xiàn)場(chǎng)離奇詭異馅袁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)荒辕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門汗销,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抵窒,你說我怎么就攤上這事弛针。” “怎么了李皇?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵削茁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我掉房,道長(zhǎng)茧跋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任卓囚,我火速辦了婚禮瘾杭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捍岳。我一直安慰自己富寿,他們只是感情好睬隶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著页徐,像睡著了一般苏潜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上变勇,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天恤左,我揣著相機(jī)與錄音,去河邊找鬼搀绣。 笑死飞袋,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的链患。 我是一名探鬼主播巧鸭,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼麻捻!你這毒婦竟也來了纲仍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贸毕,失蹤者是張志新(化名)和其女友劉穎郑叠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體明棍,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乡革,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摊腋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沸版。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖歌豺,靈堂內(nèi)的尸體忽然破棺而出推穷,到底是詐尸還是另有隱情,我是刑警寧澤类咧,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布馒铃,位于F島的核電站,受9級(jí)特大地震影響痕惋,放射性物質(zhì)發(fā)生泄漏区宇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一值戳、第九天 我趴在偏房一處隱蔽的房頂上張望议谷。 院中可真熱鬧,春花似錦堕虹、人聲如沸卧晓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逼裆。三九已至郁稍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胜宇,已是汗流浹背耀怜。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桐愉,地道東北人财破。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像从诲,于是被迫代替她去往敵國(guó)和親左痢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容