我的编程空间,编程开发者的网络收藏夹
学习永远不晚

iOS组件化开发实战记录

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

iOS组件化开发实战记录

1. 组件化需求来源

起初的这个项目,App只有一条产品线,代码逻辑相对比较清晰,后期随着公司业务的迅速发展,现在App里面承载了大概五六条产品线,每个产品线的流程有部分是一样的,也有部分是不一样的,这就需要做各种各样的判断及定制化需求。大概做了一年多后,出现了不同产品线提过来的需求,开发人员都需要在主工程中开发,但是开发人员开发的是不同的产品线,也得将整个工程跑起来,代码管理、并行开发效率、分支管理、上线时间明显有所限制。大概就在去年底,我们的领导提出了这个问题,希望作成组件化,将代码重构拆分成模块,在主工程中组装拆分的模块,形成一个完整的App。

2. 组件化初识

随着业务线的增多,业务的复杂度增加,App的代码逻辑复杂度也增加了,后期的开发维护成本也增加了,为什么这么说呢?业务逻辑没有分类,查找问题效率降低(针对新手),运行也好慢哦,真的好烦哦......我们要改变这种局面。而组件化开发,就是将一个臃肿,复杂的单一工程的项目, 根据功能或者属性进行分解,拆分成为各个独立的功能模块或者组件 ; 然后根据项目和业务的需求,按照某种方式, 任意组织成一个拥有完整业务逻辑的工程。

组件化开发的缺点:

  • 代码耦合严重
  • 依赖严重
  • 其它app接入某条产品线难以集成
  • 项目复杂、臃肿、庞大,编译时间过长
  • 难以做集成测试
  • 对开发人员,只能使用相同的开发模式
  • ......

组件化开发的优点:

  • 项目结构清晰
  • 代码逻辑清晰
  • 拆分粒度小
  • 快速集成
  • 能做单元测试
  • 代码利用率高
  • 迭代效率高
  • ......

组件化的实质:就是对现有项目或新项目进行基础、功能及业务逻辑的拆分,形成一个个的组件库,使宿主工程能在拆分的组件库里面查找需要的功能,组装成一个完整的App。

3. 组件化必备的工具使用

组件的存在方式是以每个pod库的形式存在的。那么我们组合组件的方法就是通过利用CocoaPods的方式添加安装各个组件,我们就需要制作CocoaPods远程私有库,将其发不到公司的gitlab或GitHub,使工程能够Pod下载下来。

Git的基础命令:


echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/c/test.git
git push -u origin master

Git命令的简单整理

CocoaPods远程私有库制作:

Create Component Project


pod lib create ProjectName

Use Git


echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/c/test.git
git push -u origin master

Edit podspec file


vim CoreLib.podspec

Pod::Spec.new do |s|
 s.name  = '组件工程名'
 s.version  = '0.0.1'
 s.summary  = 'summary'

 s.description = <<-DESC
 description
   DESC

 s.homepage  = '远程仓库地址'
 s.license  = { :type => 'MIT', :file => 'LICENSE' }
 s.author  = { '作者' => '作者' }
 s.source  = { :git => '远程仓库地址', :tag => s.version.to_s }

 s.ios.deployment_target = '8.0'

 s.source_files = 'Classes*.{swift,h,m,c}'
 s.resources = 'Assets*.{h,m}','Classes/Bugly*.{h,m}','Classes/AMap*.{h,m}']
 s.public_header_files = ['Classes*.framework'
 s.xcconfig = { "FRAMEWORK_SEARCH_PATHS" => "Pods/WDContainerLib/Frameworks" }

 s.requires_arc = true
end

6. 组件之间的通讯

在将业务控制器拆分出去后,如果一个组件要调用另一个组件里面的控制器,平常的做法是直接==#import "控制器头文件"==,现在在不同的组件里面是无法import的,那该怎么做呢?答案就是使用==消息发送机制==。

思路:

  1. 每个业务组件库里面会有一个控制器的配置文件(路由配置文件),标记着每个控制器的key;
  2. 在App每次启动时,组件通讯的工具类里面需要解析控制器配置文件(路由配置文件),将其加载进内存;
  3. 在内存中查询路由配置,找到具体的控制器并动态生成类,然后使用==消息发送机制==进行调用函数、传参数、回调,都能做到。

((id (*)(id, SEL, NSDictionary *)) objc_msgSend)((id) cls, @selector(load:), param);
((void(*)(id, SEL,NSDictionary*))objc_msgSend)((id) vc, @selector(callBack:), param);

Or

[vc performSelector:@selector(load:) withObject:param];
[vc performSelector:@selector(callBack:) withObject:param];

好处:

解除了控制器之间的依赖; 使用iOS的消息发送机制进行传参数、回调参数、透传参数; 路由表配置文件,能实现界面动态配置、动态生成界面; 路由表配置文件放到服务端,还可以实现线上App的跳转逻辑; 将控制器的key提供给H5,还可以实现H5跳转到Native界面;

7. 组件化后的资源加载

新项目已开始就采用组件化开发,还是特别容易的,如果是老项目重构成组件化,那就比较悲剧了,OC项目重构后,app包里面会有一个==Frameworks==文件夹,所有的组件都在这个文件夹下,并且以==.framework==(比如:WDComponentLogin.framework)结尾。在工程中使用的==xib、图片==,使用正常的方式加载,是加载不到的,原因就是xib、图片的路径==(工程.app/Frameworks/WDComponentLogin.framework/LoginViewController.nib、工程.app/Frameworks/WDComponentLogin.framework/login.png)==发生了变化。

以下是在组件库中加载nib文件/图片文件的所有情况:



+ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName;


+ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName frameworkName:(NSString *_Nonnull)frameworkName;


+ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName bundleName:(NSString *_Nonnull)bundleName frameworkName:(NSString *_Nonnull)frameworkName;


+ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName bundleName:(NSString *_Nonnull)bundleName;


+ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName targetClass:(Class _Nonnull)targetClass;


+ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName bundleName:(NSString *_Nonnull)bundleName targetClass:(Class _Nonnull)targetClass;


+ (UINib *_Nullable)loadNibClass:(NSObject *_Nonnull)targetClass;

控制器加载方式:


@implementation WDBaseViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
 NSString *classString = [[NSStringFromClass(self.class) componentsSeparatedByString:@"."] lastObject];
 if ([[NSBundle bundleForClass:[self class]] pathForResource:classString ofType:@"nib"] != nil) {
 //有xib
  return [super initWithNibName:classString bundle:[NSBundle bundleForClass:[self class]]];
 }else if ([[NSBundle mainBundle] pathForResource:classString ofType:@"nib"] == nil) {
 //没有xib
 return [super initWithNibName:nil bundle:nibBundleOrNil];
 } else {
 return [super initWithNibName:(nibNameOrNil == nil ? classString : nibNameOrNil) bundle:nibBundleOrNil];
 }
}
@end

UIView视图加载方式:

OC版本


+ (id)loadFromNIB {
 if ([[NSFileManager defaultManager] fileExistsAtPath:[NSBundle bundleForClass:[self class]].bundlePath]) {
 return [[[NSBundle bundleForClass:[self class]] loadNibNamed:[self description]
        owner:self
        options:nil] lastObject];
 }else{
 return [[[NSBundle mainBundle] loadNibNamed:[self description] owner:self options:nil] lastObject];
 }
 
}

+ (id)loadFromNIB:(NSInteger)index {
 if ([[NSFileManager defaultManager] fileExistsAtPath:[NSBundle bundleForClass:[self class]].bundlePath]) {
 return [[NSBundle bundleForClass:[self class]] loadNibNamed:[self description]
        owner:self
        options:nil][index];
 }else{
 return [[NSBundle mainBundle] loadNibNamed:[self description] owner:self options:nil][index];
 }
 
}

Swift版本


// MARK: - 通过nib加载视图
@objc public static func loadFromNIB() -> UIView! {
 return (Bundle(for: self.classForCoder()).loadNibNamed(self.description().components(separatedBy: ".")[1], owner: self, options: nil)?.first as? UIView)!
}

8. OC工程底层换swift代码

目前正在做OC底层的统一,换成swift写的代码。

控制器Base、Web控制器Base使用OC代码,因为OC控制器不能继承Swift,而Swift控制器可以继承OC写的控制器。

导航栏、工具栏、路由、基础组件、功能组件、混合开发插件都是用Swift语言。

Swift移动组件大部分完成,OC工程、Swift工程都统一使用开发的移动组件库。

9. 总结

经过半年的努力重构,终于将工程拆分成组件化开发了,也从中学到了很多,希望自己能再接再厉和同事一起进步。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对编程网的支持。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

iOS组件化开发实战记录

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

iOS组件化开发实战记录

目录1. 组件化需求来源2. 组件化初识3. 组件化必备的工具使用5. 组件工程兼容swift环境6. 组件之间的通讯7. 组件化后的资源加载8. OC工程底层换swift代码9. 总结1. 组件化需求来源起初的这个项目,App只有一条产品
2022-05-25

iOS WKWebView秒开方案实战记录

目录前言秒开流程图秒开方案技术数据秒开效果对比WKWebView究竟好在哪里呢?总结前言 WKWebView 秒开方案,不仅需要端上优化,也需要前后端配合,我们业务前期在UIWebView上已经通过各种优化达到了秒开,但是由于苹果2020年
2022-05-23

ios组件化开发的方法是什么

iOS组件化开发的方法有很多种,以下是其中几种常用的方法:1. CocoaPods:使用CocoaPods可以方便地管理项目中的各个组件。每个组件都会以Pod的形式进行管理,并通过Podfile文件来指定项目所需要的组件。可以通过私有Pod
2023-08-15

Vue组件封装之input输入框实战记录

在vue中会将常用的组件进行封装,下面这篇文章主要给大家介绍了关于Vue组件封装之input输入框的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
2022-11-13

Vue-router怎么实现组件化开发

这篇文章主要介绍“Vue-router怎么实现组件化开发”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Vue-router怎么实现组件化开发”文章能帮助大家解决问题。代码如下:#单个组件路由impo
2023-07-04

详解SpringBoot Start组件开发之记录接口日志信息

这篇文章主要为大家介绍了SpringBoot-Start组件开发之记录接口日志信息详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-18

Angular开发问题记录:组件拿不到@Input输入属性

最近在工作中实现一个feature的时候,碰到一个小问题:Angular​组件拿不到@Input输入属性的问题,尽管对这些问题都比较了解,但是找问题是需要一个过程的,所以还是把这个问题总结记录了下。
2023-05-14

Angular开发问题记录:组件数据不能实时更新到视图上

工作中碰到一个问题:Angular组件数据不能实时更新到视图上,问题本身比较容易解决,但还是总结记录一下。
2023-05-14

【Android】Jetpack全组件实战开发短视频应用App(三)

前言 这一篇我们将使用Navigation搭建我们App的基础架构,我们先看下效果Navigation介绍 官网地址 快速入门 导航组件由以下三个关键部分组成: 导航图:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单
2022-06-06

【Android】Jetpack全组件实战开发短视频应用App(四)

前言 我们在上一篇基本上已经实现我们要的效果了,但是还遗留了几个问题,这一篇我们就来解决下 自定义解析器 我们上一篇介绍过NavDestination是通过解析xml生成的,我们不想在xml中写死,通过注解的方式实现,我们接下来就自定义注解
2022-06-06

【由浅入深】vue组件库实战开发总结分享

​很庆幸标题能够赶上2022结束的脚步。本文由浅入深层层递进,对组件库的开发过程做个了小结。
2023-05-14

【Android】Jetpack全组件实战开发短视频应用App(十)

前言 我们已经把首页列表的Item布局完成,接下来我们就开始加载首页数据,我们这一篇主要是做封装,具体网络请求放在下一篇 引入依赖 //刷新分页组件 api 'com.scwang.smartrefresh:SmartRefreshLayo
2022-06-06

【Android】Jetpack全组件实战开发短视频应用App(六)

前言 这一篇我们主要介绍下Room数据库的创建以及简单使用 Room介绍 Room是Google为了简化SQLite推出的 拥有SQLite所有的操作功能; 使用简单,通过注解方式实现功能,类似Retrofit,编译时自动生成实现类 与Li
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录