推送消息 跳转到指定控制器

作者: gitKong | 来源:发表于2016-09-09 13:20 被阅读464次

    一、前言

    • 1、最近整理以前项目的资料,处理推送消息的时候显示只能在主页面,安卓那边就很好处理,可以根据不同的推送消息指定跳转到任意控制器,那么iOS 能不能实现呢?答案是肯定的!

    • 2、需要后台配合,传一个要跳转的控制器的名字以及指定控制器所需参数 原文 地址 ,简单说说这种方式出现的问题吧

    • 问题一

    没有封装工具类使用,delegate里面实现并处理逻辑,这显然不应该是delegate去做,造成delegate 冗余

    • 问题二

    面向字典开发,可复用性差,处理情况单一(json格式定死),逻辑处理不完善,数据获取麻烦且容易出错(需要写key,字符串容易出错,如果需要多次使用就更麻烦)


    二、完善

    • 1、封装工具类,继承NSObject,解耦合,移植性、复用性、拓展性强,对外提供一个方法,专门处理推送消息,自动跳转控制器,交给专业的人去做,更加面向对象

    • 2、面向模型,后台返回json数据,转模型存储数据,存储方便,不容易出错,而且如果后台返回的json格式不一样了,我依然可以创建子类,重写构造方法进行特殊处理,而且面向模型开发,直观


    三、API 设计

    • 1、工具类一般弄成单例,需要一个单例创建方法,还需要对外提供一个方法,接收到外界数据后,处理逻辑并跳转到指定控制器

    • 2、数据当然从模型里面那,因此还需要一个模型类,专门存储消息数据,在工具类中创建,统一管理,最关键、最重要的是claseName属性,后台必须返回一个需要跳转的控制器的名字给我,其他不同控制器对应的属性,就交给子类去添加,有属性当然就需要自定义构造方法,字典转模型嘛

    FLRemotePushModel 模型

    • 子类继承,对应不同的控制器所需的参数,添加对应的属性到子类中,默认层级都是同一级,子类可重写fl_remotePushModel方法,对应不同的字典转模型
    • 注意一点:子类中所添加的属性,应该都为对象,基本数据类型不行喔,而且后台返回的都是对象,基本数据用NSNumber包装
    
    /**
     *  @author Clarence-lie, 16-09-08 22:09:01
     *
     *  需要跳转的类名,后台返回
     */
    @property (nonatomic,copy)NSString *className;
    /**
     *  @author Clarence-lie, 16-09-08 22:09:38
     *
     *  字典转模型,可重写,根据不同格式转模型(如果重写,需要调用父类)
     *
     *  @param dict 后台返回的字典(json)
     *
     *  @return 返回一个模型
     */
    + (instancetype)fl_remotePushModel:(NSDictionary *)dict;
    
    

    FLRemotePushManager 管理工具

    /**
     *  @author Clarence-lie, 16-09-08 22:09:59
     *  创建单例对象,方便管理
     */
    + (instancetype)fl_shareInstance;
    /**
     *  @author Clarence-lie, 16-09-08 22:09:59
     *
     *  根据模型内容跳转到指定界面
     *
     *  @param remotePushModel FLRemotePushModel子类
     */
    - (void)fl_pushWithRemotePushModel:(FLRemotePushModel *)remotePushModel;
    
    

    四、实现

    大概思路:根据json数据创建模型 -> 工具类获取模型数据 -> 根据类名创建控制器实例对象 -> 获取模型对象属性名并存储到数组中 -> 遍历模型属性名数组并判断控制器实例对象是否有对应属性 -> 有对应属性则通过KVC赋值给控制器 -> 获取导航控制器进行跳转
    定时器模拟推送消息
    • 关键代码简析
    • 1.获取对象的属性列表: class_copyPropertyList

    unsigned int outCount, i;
    // 获取对象里的属性列表
    objc_property_t * properties = class_copyPropertyList([instance class], &outCount);
    
    • 2.通过属性名创建对应属性的getter方法:NSSelectorFromString OC的getter方法就是属性的本身
    /**
     *  @author Clarence-lie, 16-09-08 22:09:33
     *
     *  通过字符串来创建该字符串的getter方法
     *
     *  @param propertyName 属性名
     *
     *  @return 对应的getter方法
     */
    - (SEL)fl_creatGetterWithPropertyName: (NSString *) propertyName{
        //1.返回get方法: oc中的get方法就是属性的本身
        return NSSelectorFromString(propertyName);
    }
    
    • 3.获取属性名对应的属性值:获得方法签名(getter方法)-> 签名中获取调用对象 -> 设置对象的target -> 设置对象的方法 -> 发送消息 -> 接收返回值(就是属性值)
    /**
     *  @author Clarence-lie, 16-09-08 22:09:49
     *
     *  获取指定属性名对应的值
     *
     *  @param propertyName 属性名
     *
     *  @return 属性值
     */
    - (id)fl_getValueByPropertyName:(NSString *)propertyName{
        //获取get方法
        SEL getSel = [self fl_creatGetterWithPropertyName:propertyName];
        
        if ([self respondsToSelector:getSel]) {
            
            //获得方法的签名
            NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
            
            //从签名获得调用对象
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
            
            //设置target
            [invocation setTarget:self];
            
            //设置selector
            [invocation setSelector:getSel];
            
            //接收返回的值
            NSObject *__unsafe_unretained returnValue = nil;
            
            //发消息
            [invocation invoke];
            
            //接收返回值
            [invocation getReturnValue:&returnValue];
            
            return returnValue;
        }
        else{
            return nil;
        }
    }
    

    五、调用

    调用起来很简单,只需要创建好对应的推送模型,然后执行 fl_pushWithRemotePushModel 方法就行

    - (void)pushSecond
    {
        // 跟服务端沟通好跳转对应的界面需要对应的参数以及格式
        NSDictionary *userInfo = @{
                                   @"className": @"SecondViewController",
                                   @"id": @"12",
                                   @"content": @"测试推送内容消息"
                                   };
        
        [[FLRemotePushManager fl_shareInstance] fl_pushWithRemotePushModel:[FLSecondVcRemoteModel fl_remotePushModel:userInfo]];
    }
    
    - (void)pushThird{
        // 非默认同一级的json格式,子类重写构造方法即可
        NSDictionary *userInfo = @{
                                   @"className": @"ThirdViewController",
                                   @"msg"      : @{
                                        @"name": @"clarence",
                                         @"age": @12
                                                 }
                                   };
        
        [[FLRemotePushManager fl_shareInstance] fl_pushWithRemotePushModel:[FLThirdVcRemoteModel fl_remotePushModel:userInfo]];
    }
    

    六、总结

    • ** 1、注意:必须跟后台配合,定好规则,哪条推送消息需要跳转到哪个控制器、以及该控制器中请求后台所需的参数都要返回给客户端,归根到底还是利用了OC的运行时特点,可以获取对象以及对象属性、方法**

    • ** 2、如果推送页面都是固定的,可以直接写死,只需要传入不同参数就行,但是如果是不固定的,当然,可以传入不同的type 然后前台判断,跳转不同的控制器,此时相对就没那么直观,但确安全很多,因为控制器名是字符串,写错了就没办法,当然,框架中处理了,如果没找到指定的控制器 ,就会创建新的,看需求**

    • ** 3、实现比较简单,关键还是思路,详细的Demo都在 GitHub 上面,欢迎大家关注我,喜欢给个like 和 star **

    相关文章

      网友评论

        本文标题:推送消息 跳转到指定控制器

        本文链接:https://www.haomeiwen.com/subject/vfeeettx.html