美文网首页
iOS 拦截篇(一):拦截H5通过<input>标签

iOS 拦截篇(一):拦截H5通过<input>标签

作者: dvlproad | 来源:发表于2019-02-01 18:56 被阅读28次

    一、场景

    H5经常会需要选择手机照片来完善自身内容;比如上传身份证照片、资产照片等等等。有时候这些照片经常需要app处理完后才上传给H5,那么这时候作为app的你就必须先处理好H5选择的手机照片,再上传给他。所以本文接下来将讲解如何拦截处理H5选择的手机照片;

    二、基础知识准备

    普及基础知识:
    H5选择手机照片的方式主要有两种:

    • 1、通过与app的JS交互,选择手机照片;
    • 2、通过H5自身的<input>标签,选择手机照片;

    第一种JS交互的,图片数据的获取太容易处理了,就不再这里讲了。我们这边主要讲H5通过H5自身的<input>标签,选择手机照片。

    三、拦截的方法

    拦截的思路:1、拦截、替换系统方法;2、在新方法中按产品需求处理图片;
    详细步骤如下。

    1、拦截、替换系统方法

    即:将在WKFileUploadPanel中实现的UIImagePickerController的代理方法imagePickerController:didFinishPickingMediaWithInfo:替换为你在其他类(因为系统没提供WKFileUploadPanel这个类给你,供你在该类中直接写交换的方法)中写的其他方法。
    如:将其替换为你在CJHookFileUploadPanel类中实现的- (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info方法。
    通过此步,你就能够在新写的方法- (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info里面获取到图片选择器选取到的图片数据了。

    @implementation CJHookFileUploadPanel
    
    + (void)hookFileUploadPanel:(BOOL)hook {
        SEL originalSelector = @selector(imagePickerController:didFinishPickingMediaWithInfo:);
        SEL swizzledSelector = @selector(swizzled_imagePickerController:didFinishPickingMediaWithInfo:);
        Class originalClass = NSClassFromString(@"WKFileUploadPanel");
        Class otherClass = [CJHookFileUploadPanel class];
        if (hook) {
            bool success = HookCJHelper_exchangeOriMethodToNewMethodWhichAddFromDiffClass(originalClass, originalSelector, otherClass, swizzledSelector);
            NSLog(@"exchangeOriMethodToNewMethod:%@", success ? @"success": @"failure");
        } else {
            bool success = HookCJHelper_recoverOriMethodToNewMethodWhichAddFromDiffClass(originalClass, originalSelector, otherClass, swizzledSelector);
            NSLog(@"recoverOriMethodToNewMethod:%@", success ? @"success": @"failure");
        }
    }
    
    - (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
        NSMutableDictionary *new_info = [[NSMutableDictionary alloc] initWithDictionary:info];
       // 处理新new_info......
        
        [self swizzled_imagePickerController:picker didFinishPickingMediaWithInfo:new_info];
    }
    
    @end
    

    上述为某类添加另一个类中的方法的实现代码如下:

    #pragma mark - Deal New Method Which From Diff Class
    
    /**
     *  exchange class1's method to class2's method (can recover originalMethod)
     *  @brief      add swizzledSelOwnerClass's swizzledSelector method to originalSelOwnerClass, and make originalSelector method's imp is swizzledSelector's imp
     *  @attention  swizzledSelOwnerClass and originalSelOwnerClass shouldn't same;
     *
     *  @param originalSelOwnerClass    the class which will add swizzledMethod, and exchangeImp between originalMethod and swizzledMethod
     *  @param originalSelector         the method which will be exchange
     *  @param swizzledSelOwnerClass    the new method owner class
     *  @param swizzledSelector         the method which will be add for class1, which is in class2
     *
     *  @return is add and exchange Success
     */
    bool HookCJHelper_exchangeOriMethodToNewMethodWhichAddFromDiffClass(Class originalSelOwnerClass, SEL originalSelector, Class swizzledSelOwnerClass, SEL swizzledSelector) {
        return HookCJHelper_dealNewMethodWhichFromDiffClass(YES, originalSelOwnerClass, originalSelector, swizzledSelOwnerClass, swizzledSelector);
    }
    
    ///recover class1's method to class2's method (can recover originalMethod)
    bool HookCJHelper_recoverOriMethodToNewMethodWhichAddFromDiffClass(Class originalSelOwnerClass, SEL originalSelector, Class swizzledSelOwnerClass, SEL swizzledSelector) {
        return HookCJHelper_dealNewMethodWhichFromDiffClass(NO, originalSelOwnerClass, originalSelector, swizzledSelOwnerClass, swizzledSelector);
    }
    
    /**
     *  accoding to the `didAddAndExchange` boolValue, do the corresponding deal. for example, if YES:add the new method which from diff class to origin class; if NO:did recover origin method if has exchange origin method ever before
     *  @brief      if YES:add swizzledSelOwnerClass's swizzledSelector method to originalSelOwnerClass, and make originalSelector method's imp is swizzledSelector's imp; if NO:did recover origin method if has exchange origin method ever before
     *  @attention  swizzledSelOwnerClass and originalSelOwnerClass shouldn't same;
     *
     *  @param didAddAndExchange        if YES:add the new method which from diff class to origin class; if NO:did recover origin method if has exchange origin method ever before
     *  @param originalSelOwnerClass    the class which will add swizzledMethod, and exchangeImp between originalMethod and swizzledMethod
     *  @param originalSelector         the method which will be exchange
     *  @param swizzledSelOwnerClass    the new method owner class
     *  @param swizzledSelector         the method which will be add for class1, which is in class2
     *
     *  @return is deal the new method Success
     */
    bool HookCJHelper_dealNewMethodWhichFromDiffClass(bool didAddAndExchange, Class originalSelOwnerClass, SEL originalSelector, Class swizzledSelOwnerClass, SEL swizzledSelector) {
        NSString *class1String = NSStringFromClass(originalSelOwnerClass);
        NSString *class2String = NSStringFromClass(swizzledSelOwnerClass);
        if ([class1String isEqualToString:class2String]) {
            NSLog(@"Error:the two class should be difference");
            return false;
        }
        
        Method oriClassSwizzledMethod = class_getInstanceMethod(originalSelOwnerClass, swizzledSelector);
        if (oriClassSwizzledMethod) {
            NSLog(@"Error:the original class has exist the new method");
            return false;
        }
        
        Method originalMethod = class_getInstanceMethod(originalSelOwnerClass, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(swizzledSelOwnerClass, swizzledSelector);
        // demand:exchange the class1's originalMethod to class1's newMethod, but the newMethod can't implemente in class1
        // problem analysis: because of apple system doesn't give WKFileUploadPanel class, so we can't implemente and add the new method for it, directly. So
        // step1: we implemente the new method in other class at first, for example UIImagePickerController class. Then we add the class2's new method for the class1.
        // step2: exchange the class1's originalMethod to class1's newMethod
        
        // eg1:
        // decription: add `diff_change_printLog` method for `TestChangeModel1` class
        // originalSelOwnerClass:TestChangeModel1 just exist printLog and common_change_printLog method
        // swizzledMethod:diff_change_printLog
        // eg2:
        // decription: add `swizzled_imagePickerController:didFinishPickingMediaWithInfo:` method for `WKFileUploadPanel` class
        // originalSelOwnerClass:NSClassFromString(@"WKFileUploadPanel") just exist `imagePickerController:didFinishPickingMediaWithInfo:`
        // swizzledMethod:swizzled_imagePickerController:didFinishPickingMediaWithInfo:
        
        
        
        NSString *newMethodName = NSStringFromSelector(swizzledSelector);
        NSString *oriMethodBeenChangedToNewMethodKey = [NSString stringWithFormat:@"cj_addMethod_%@_%@", class2String, newMethodName]; // a key of flag mark the origin class's origin method's IMP will last be change to which class's which method
        
        BOOL oriMethodBeenChangedToNewMethod = [objc_getAssociatedObject(originalSelOwnerClass, &oriMethodBeenChangedToNewMethodKey) boolValue]; // mark if the origin class's origin method's IMP has been change to other class's method's IMP
        if (oriMethodBeenChangedToNewMethod) {
            BOOL shouldRecoverIfHasExchangeOriginMethod = !didAddAndExchange;
            if (shouldRecoverIfHasExchangeOriginMethod) {
                Method oriClassNewMethod = class_getInstanceMethod(originalSelOwnerClass, swizzledSelector);
                method_exchangeImplementations(originalMethod, oriClassNewMethod);
                
                objc_setAssociatedObject(originalSelOwnerClass, &oriMethodBeenChangedToNewMethodKey, @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            }
            
            return false;
        }
        
        
        // if originalClass doesn't exist swizzledSelector, add it
        BOOL swizzMethodIsNewMethodForOriClass =
        class_addMethod(originalSelOwnerClass,
                        swizzledSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
        // if swizzMethod is new method for OriClass before, then the above method will add swizzMethod for OriClass and return YES, else it will return NO;
        // eg: UIImagePickerController class doesn't exist method2, then add it to UIImagePickerController class;
        // eg: now UIImagePickerController exist method1 and method2
        
        // swizzledSelOwnerClass:NSClassFromString(@"WKFileUploadPanel")
        // originalMethod:imagePickerController:didFinishPickingMediaWithInfo:
        
        // then: change UIImagePickerController method2 to method1
        if (swizzMethodIsNewMethodForOriClass) {
            Method oriClassNewMethod = class_getInstanceMethod(originalSelOwnerClass, swizzledSelector);
            method_exchangeImplementations(originalMethod, oriClassNewMethod);
            
            objc_setAssociatedObject(originalSelOwnerClass, &oriMethodBeenChangedToNewMethodKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            
            return true;
        }
        
        NSLog(@"Error:the class1 has exist implemete or added the method, please use new method name for your method which you want to add to class1 from class2");
        return false;
    }
    

    2、在新方法中按产品需求处理图片

    即在你的以上新方法中处理即可。

    - (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
        NSMutableDictionary *new_info = [[NSMutableDictionary alloc] initWithDictionary:info];
       // 处理新new_info......
       // 略
        
        [self swizzled_imagePickerController:picker didFinishPickingMediaWithInfo:new_info];
    }
    

    结束语

    感谢阅读!

    相关文章

      网友评论

          本文标题:iOS 拦截篇(一):拦截H5通过<input>标签

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