swift组件化开发

2019-05-14 10:44:29 zhangxiweicaochen 阅读数 919

pod组件化,swift,objective-c混合开发

pod 组件化管理

  • 创建swift工程(PodSwift)

  • 创建objective-c工程(httpmanager)

  • 进入工程目录下创建spec

    pod lib create PodSwift

    填写提示信息,然后创建需要暴露调用的类所有的swift类

    s.source_files = 'PodSwiftLibs/**/*.swift'

swift objective-c混合开发

编译问题查看 https://www.jianshu.com/p/c7623c31d77b

pod注意事项

  • local pod 引用 pod库

    s.dependency "Masonry"

  • 引用预处理宏定义

local pod库中调用masonry中简写方法需要在spec申明:

#pod中的macro
 s.pod_target_xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'MAS_SHORTHAND=1 MAS_SHORTHAND_GLOBALS=1'}
 #项目中的macro
 s.user_target_xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'MAS_SHORTHAND=1 MAS_SHORTHAND_GLOBALS=1'}
 

git:demo地址

2019-11-19 19:02:10 LeeCSDN77 阅读数 85

作者:madaoCN
链接:https://www.jianshu.com/p/ae581ea8a011
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

前言

最近公司新开了个项目,需要从零开始搭建,之前做过一些组件化的工作,但是之前的开发语言是OC,新项目切换到了Swift,虽然差异不大,但是还是写了本文作为记录

tips: 这篇文章写得不错 iOS swift的xcworkspace多项目管理(架构思想)

正如文章中所说,多工程需要面临的几个问题

  • 各个工程如何创建,如何维护第三方的依赖(这里使用Cocopods进行依赖管理)
  • 主工程如何调用Library工程
  • Library之前的依赖和调用

跟着我走一遍流程,follow me, Let's start it!

正文

一、主工程创建

首先,我们创建一个普通工程

 

创建普通工程

然后集成 Cocopods

step1:  cd /Volumes/SubMadao/git4Madao/ularization/ (你的工程目录)
step2:  pod init

然后编辑 podfile

platform:ios,'8.0'
inhibit_all_warnings!
use_frameworks!

# workspace
def workspace_pods
end


target 'Modularization' do
  workspace_pods
  target 'ModularizationTests' do
    inherit! :search_paths
  end

  target 'ModularizationUITests' do
    inherit! :search_paths
  end
end
step3:  pod install

WorkSpace

那么,我们的工作空间(WorkSpace)也就创建好了,其实也就是Cocopods集成的基本流程

二、Library创建和关联

我们暂定各个模块如下(借鉴他人):

  • 主工程:承载主要的表层业务代码
  • Core:独立存在,应用加密,接口请求认证等敏感代码
  • Base:基类封装,拓展,基本的数据处理
  • Service:服务层,封装业务工具类,网络层服务,持久化层服务等
  • Pods: 三方依赖

依赖架构

创建各个Base ,Service ,Core等Library

new project 新建一个工程并选择 Cocoa Touch Framework类型

new project

加入正确的 Group和 WorkSpace

add to WorkSpace

然后需要将 Mach-O Type 修改为静态库 Static Library

Static Library

 

如此重复,创建 Base ,Service ,Core

项目总览

到这里,项目架构的基本雏形已经出来了,但是工程中各个Library之间并没有相互依赖的关系

三、主工程调用Library

设置依赖

Linked Frameworks Libraries 中添加即可,添加之后,只需要Build主工程,Library能够自动联编

创建文件

很自然地,我们需要在主工程中调用Library中的文件,那么我们在 MADCore 中添加一个文件

///  MADCoreSettings.swift
import UIKit
// 类需要声明为 Public
public class MADCoreSettings: NSObject {

    // 属性需要声明为 Public
    public static let SECRET_KEY = "SECRET_KEY"
}

还需要在 Building Phases 中的 Headers 中添加为 Public

Building Phases

import UIKit
import MADCore

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // 调用 MADCore库文件
        let key = MADCoreSettings.SECRET_KEY
    }
}

这样子,只需要  import MADCore 即可调用 MADCore 库中的文件了

四、子工程Library互相调用

也和主工程调用Library类似,对应添加依赖即可,暴露header即可

五、Cocopods 管理主工程和 Library的三方依赖

我们需要在 Service 中 封装网络层的代码,那么我们需要用Cocopods为Library添加三方依赖

platform:ios,'8.0'
inhibit_all_warnings!
use_frameworks!

workspace 'Modularization.xcworkspace'

########################################  dependency

# workspace
def workspace_pods
    
    # react
    pod 'RxSwift'
    pod 'RxCocoa'
    
    # orm
    pod 'ObjectMapper', '~> 3.3.0'
    pod 'SwiftyJSON'
end

# main project dependency
def project_only_pods
    
    pod 'SnapKit', '~> 4.0.0'
    pod 'Kingfisher', '~> 4.0'
    pod 'MJRefresh'
    pod 'MJExtension'
    pod 'MD-UITableView+FDTemplateLayoutCell'
    pod 'SVProgressHUD', '2.0.4'
end

# network layer dependency
def network_layer_pods
    
    pod 'Moya/RxSwift', '~> 11.0'
    pod 'Moya', '~> 11.0.2'
    pod 'Alamofire', '~> 4.5'
end

######################################## main project

target 'Modularization' do

    workspace_pods
    project_only_pods
    network_layer_pods

    target 'ModularizationTests' do
        inherit! :search_paths
    end

    target 'ModularizationUITests' do
        inherit! :search_paths
    end
end

######################################## Library

# Service
target 'MADService' do
    project 'MADService/MADService.xcodeproj'
    
    #
    workspace_pods
    network_layer_pods
end

# Base
target 'MADBase' do
    project 'MADBase/MADBase.xcodeproj'
    
    #
    workspace_pods
end

# Core
target 'MADCore' do
    project 'MADCore/MADCore.xcodeproj'
    
    #
    workspace_pods
end

需要注意的两行是

workspace 'Modularization.xcworkspace'

project 'MADService/MADService.xcodeproj'
project 'MADBase/MADBase.xcodeproj'
project 'MADCore/MADCore.xcodeproj'

接下来只需要 pod install 即可完成我们的配置依赖

分别配置了 workspace 的文件路径, 和子project的路径

以此为基础,使用 Moya + RxSwift 封装了网络层的请求代码,仅供参考

网络层封装

 

最后

到这里,我们已经完成了组件化的基本操作和配置,只要愉快地填充代码就可以啦,常规惯例Demo传送门:GITHUB

 

2020-02-02 22:28:37 iCloudEnd 阅读数 159

思路

  1. 数据类型实现范型

  2. 数据管理通过class实现继承,通过init完成初始化

  3. 界面组件通过范型

实现

struct ListPageViewG<T:PageModelProtocal,U:PageRowProtocal>: View  where U:View{
    // 数据管理对象通过class的继承实现灵活更换
    var pageMgr:PageMgrG<T>;
    // 数据类型通过范型T
    @State var items:[T] = []
    @State  var isLoading: Bool = false
    @State  var page: Int = 0
    
    // 数据初始化
    init(pmgr:PageMgrG<T>) {
        self.pageMgr = pmgr
        _items = State(initialValue: pageMgr.next())
        
        
    }
    
    var body: some View {
        List(items){ item in
            //self.pageRow
            VStack{
                //界面组件通过范型
                U.init(item: item.getShowDict())
            }.onAppear {
                self.listItemAppears(item)
            }
            
            
        }
    }
    
}

实例化调用

ListPageViewG<Author,AuthorPageRow2>(pmgr: PageMgrG<Author>())

更多SwiftUI教程和代码关注专栏

2017-01-03 11:09:51 qiuxuewei2012 阅读数 7580

转载注明出处:http://blog.csdn.net/qxuewei/article/details/53945445

   因为OC 的局限性, 使得iOS 开发组件化编程变得不可能,得益于面向对象语言的特性 (封装,继承,多态) 在我们熟悉的设计模式中渐渐形成统一的软件开发思想.
  在抽取某些功能作为基类的不断运用中,代码的可移植性逐渐减弱. 就如同一棵树,从主干到各个分支,每个分支再长成细枝末叶.代码的耦合性也相应增加.
  随着苹果 swift 语言的推出, 对于传统OC 语言取其精华,弃其糟粕. 加上开源社区众大神的齐力维护.逐渐成为一门非常优秀的高级语言.
  其中 swift中的面向协议编程思想 就是其中很有代表性的一项优秀组成部分.

OC中协议中定义的方法必须在所继承的类中进行实现,这就减弱了协议的功能.
swift中主类 和 extension 扩展类的协同工作,保留了 在 主类中定义方法 在所继承的类中进行实现的特性,又新增了 在 extension 拓展类中定义并实现的方法在任何继承自此协议的类中可以任意调用. 从而实现 组件化编程
如同造一辆汽车,同品牌下的任意车型都可直接调用 本品牌生产的各种零部件,其实效益最大化.
两个简单的使用场景可以充分体现swift组件化编程的优势:

1.xib 加载 UIView

我们使用XIB加载的自定义View都会使用相同的方法:
Bundle.main.loadNibNamed("RedView", owner: nil, options: nil)?.last

通常在自定义View的实现中定义一个类方法
例如:

class func loadWithNib() -> RedView {
        return Bundle.main.loadNibNamed("RedView", owner: nil, options: nil)?.last! as! RedView
    }

如果我们定义一个可从XIB加载某类的协议,在自定义类中仅仅需要遵守这个协议,无需实现协议中的方法,便可实现此协议中实现的所有方法.例如定义一个 可从XIB加载的协议 NibLoadable


import Foundation

protocol NibLoadable {

}

extension NibLoadable {
    static func loadViewWithNib() -> Self {
        return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.last! as! Self
    }
}

只要在xib加载的类中继承此协议

import UIKit

class GreenView: UIView , NibLoadable{

}

就可以在需要初始化此对象时直接调用

GreenView.loadViewWithNib()

2.具备粒子效果的控制器

定义一个可添加和移除粒子效果的协议,凡是遵守此协议的控制器(注意在协议中 表明 where Self : UIViewController 意味着着仅仅控制器类能遵守此协议). 在需要粒子效果的控制器中直接调用协议中实现的方法即可:

可实现粒子动画的协议 Particleable

//
//  Particleable.swift
//  XWProtocolDemo
//
//  Created by 邱学伟 on 2017/1/3.
//  Copyright © 2017年 邱学伟. All rights reserved.
//  可实现粒子动画的

import Foundation
import UIKit

protocol Particleable {

}

extension Particleable where Self : UIViewController {
    /// 增加粒子效果 position 粒子发射器位置
    func addParticle(_ position : CGPoint = CGPoint(x: UIScreen.main.bounds.width * 0.85, y: UIScreen.main.bounds.height - 30)) {
        let emitter = CAEmitterLayer()
        emitter.position = position
        emitter.preservesDepth = true
        var cells = [CAEmitterCell]()
        for i in 0..<10 {
            let cell = CAEmitterCell()
            cell.birthRate = Float(arc4random_uniform(4)) + 3
            cell.lifetime = 5
            cell.lifetimeRange = 3
            cell.scale = 0.7
            cell.scaleRange = 0.3
            cell.emissionLongitude = CGFloat(-M_PI_2)
            cell.emissionRange = CGFloat(M_PI_4 * 0.6)
            cell.velocity = 90
            cell.velocityRange = 50
            cell.spin = CGFloat(M_PI_2)
            cell.contents = UIImage(named: "good\(i)_30x30")?.cgImage
            cells.append(cell)
        }
        emitter.emitterCells = cells
        view.layer.addSublayer(emitter)
    }

    /// 删除粒子效果
    func removeParticle() {
        view.layer.sublayers?.filter({ $0.isKind(of: CAEmitterLayer.self) }).last?.removeFromSuperlayer()
    }
}

这里写图片描述

完整Demo: https://github.com/qxuewei/XWCSDNDemos 中 XWProtocolDemo 项目

2019-07-03 12:28:02 zgpeace 阅读数 660

语言: swift, 版本:swift4.2,XCode:10.2
写作时间:2019-07-02

Framework简介

有没有遇到这种场景:

  1. 同时维护过几个项目,发现一个项目有个bug,修复bug。其它项目也会有相应的问题,这个时候一般是git来cherry pick修复代码到其它项目。
  2. 一个二维码扫一扫的功能,项目一组的同学开发了,项目二组的同学过来说,这份代码也分享一份呗。然后就代码copy。

上面的场景的解决方案就是代码模块化modularize,java有jar包,Nodejs有packages,iOS就用framework。像AlamofireSnapkitKingfisher。PodFile引入,pod update就能解决。这篇文章就是记录如何实现这个功能。

1. 下载demo工程

demo工程:https://github.com/zgpeace/KnobControl/tree/start
目录为Starter, 运行后效果如下:

这个项目里面主要有两个类Knob.swift负责圆圈的逻辑,ViewController.swift负责调用圆圈的逻辑,这里framework就是要复用Knob.swift这个类,把原来工程的类删掉。然后通过framework的方式引入进来,实现代码分享的功能。

创建framework

  1. StarterPrepare文件夹里面的项目是经过改进的项目可以直接运行。
  2. 创建framework。Xcode, 选择 File ▸ New ▸ Project….
    Choose iOS ▸ Framework & Library ▸ Cocoa Touch Framework.
  3. 选择下一步, 填写信息如下KnobControl,最后create选择StarterPrepare的根目录,和KnobShowcase文件夹在同一层级。
    在这里插入图片描述
  4. 在KnobControl中copy来自KnobShowcase的文件 Knob.swiftKnobRender.swiftRotationGestureRecognizer.swift.
  5. 删除KnobShowcase的文件 Knob.swiftKnobRender.swiftRotationGestureRecognizer.swift.
  6. 集成Binary
    右键点击 KnobShowcase > Add Files to “KnobShowcase” > select KnobControl.xcodeproj > 增加 KnobControl.xcodeproj 作为 sub-project.
  7. build and run 发现错误在这里插入图片描述
    虽然framework跟project在同一个room里面,还是需要在targets中建立连接。先看看KnobControl的目录结构,找到KnobControl.framework文件
    在这里插入图片描述
    选择最上面KnobShowcase > the project editor > KnobShowcase target > General tab > Embedded Binaries section > 把 KnobControl.framework 拖到 KnobControl.xcodeproj Embedded Binaries. Linked Frameworks and Libraries也会同步加进来。
    在这里插入图片描述
    运行发现还是有错误。
  8. 访问权限
    打开文件ViewController.swift, import头文件KnobControl.
import KnobControl

方法需要修改为public修饰,因为默认的方法修饰为Internal,是指同一个framework里面可见。
运行还是失败…

  1. 修改StoryBoard权限
    用到storyboards的时候, 关联的自定义classes,需要在Identity inspector中设置class name 和 module . storyboard创建的时候,Knob 是APP的module一部分, 现在它在framework中.

更新storyboard告诉它到哪里可以找到自定义的custom view:

Main.Storyboard > 选择 Knob in the Document outline >
Identity inspector > under Custom Class > 修改Module 为 KnobControl a在这里插入图片描述
运行成功

创建本地pod

  1. 清除KnobShowcase中与KnobControl.xcodeproj的链接。
  2. 创建Pod,在Terminal中运行
pod spec create KnobControl

这句话创建了文件KnobControl.podspec

  1. 替换KnobControl.podspec 的内容
s.name         = "KnobControl"
s.version      = "1.0.0"
s.summary      = "A knob control like the UISlider, but in a circular form."
s.description  = "The knob control is a completely customizable widget that can be used in any iOS app. It also plays a little victory fanfare."
s.homepage     = "http://raywenderlich.com"
s.license      = "MIT"
s.platform     = :ios, "12.2"
s.source       = { :path => '.' }
s.source_files = "KnobControl"

在end之前加上swift版本的说明

s.swift_version = "4.2" 
  1. 验证Podspec的正确性, 在Terminal中运行
pod spec lint

显示error,因为source是local的。所以目前不需要验证Podspec。

  1. 创建pod, 在Terminal中运行
pod init

运行后,就创建了Podfile文件

  1. Podfile文件内容修改如下:
platform :ios, '12.0'

target 'KnobShowcase' do
  use_frameworks!

  pod 'KnobControl', :path => '../KnobControl'

end

# Workaround for Cocoapods issue #7606
post_install do |installer|
    installer.pods_project.build_configurations.each do |config|
        config.build_settings.delete('CODE_SIGNING_ALLOWED')
        config.build_settings.delete('CODE_SIGNING_REQUIRED')
    end
end

  1. 保存Podfile文件内容,Terminal中运行
 pod install

运行成功以后,创建了KnobShowcase.xcworkspace文件。
同时Terminal会有警告:
在这里插入图片描述
修复方法如下, 选择KnobShowcase根目录下打开项目KnobShowcase.xcodeproj > KnobShowcase target > Build Settings tab > search > ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES > 选择Other选项,在弹框里填入内容 $(inherited).

再次运行pod install , 警告消失了。

  1. 打开项目KnobShowcase.xcworkspace,查看发现有两个Targets

    Pods-KnobShowcase: pod project把其它pod和当前项目合并为最终结果的一个framework: Pods-KnobShowcase.
    KnobControl: pod 集成进来的三方库.
  2. 查看文件目录在Development Pods, 因为是local pod的原因。

pod仓库上传到git

  1. 创建git仓库
  2. 下载远程仓库
    创建目录
mkdir repo
cd repo

clone 仓库

git clone URL

显示clone成功

Cloning into 'KnobControl'...
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.

  1. 接着,copy文件夹KnobControl的内容到repo/KnobControl.
    打开copy过来的KnobControl.podspec, 修改s.source的内容:
s.source       = { :git => "URL", :tag => "1.0.0" }

  1. 推送到远程仓库
cd KnobControl/
git add .
git commit -m "Initial commit"
git push -u origin master

在这里插入图片描述

  1. 给git仓库打tag

KnobControl.podspec

s.version      = "1.0.0"

打tag,并推送到git仓库

git tag 1.0.1
git push --tags
  1. 检查创建的spec
pod spec lint

结果是验证通过。

  1. 更新在KnobShowcase中的Podfile引入pod内容如下:
pod 'KnobControl', :git => 'URL', :tag => '1.0.0'
s.homepage     = 'URL'
  1. Terminal中更新三方库
pod update

运行项目成功。

发布pod到公网

平时用开源三方库很方便,类似如下:

pod ‘ZgpeaceKnobControl’, ‘1.0.1’

  1. 先注册为pod trunk用户
$ pod trunk register zgpeace@gmail.com 'zgpeace' --description='zgpeace macbook pro'

说明:

  • 邮箱:zgpeace@gmail.com, 提交后,会收到验证邮件,点击确认后即可注册成功,这里没有密码,类似于邮箱跟电脑的绑定了连接session token。
  • 用户名:zgpeace
  • 描述:zgpeace macbook pro
  1. 打开邮箱 > 点击确认的pod trunk链接 > 显示注册成功
    在这里插入图片描述
  2. terminal 验证信息
% pod trunk me
  - Name:     zgpeace
  - Email:    zgpeace@gmail.com
  - Since:    July 2nd, 02:47
  - Pods:
    - ZgpeaceKnobControl
  - Sessions:
    - July 2nd, 02:47 - November 7th, 18:03. IP: *.*.*.* Description: 15 inch macbook pro
    - 
  1. 发布pod到公网

如果用名字KnobControl,发现已经被注册了。

% pod trunk push ./KnobControl.podspec
Updating spec repo `master`

CocoaPods 1.7.3 is available.
To update use: `sudo gem install cocoapods`

Validating podspec
 -> KnobControl (1.0.1)
    - NOTE  | xcodebuild:  note: Using new build system
    - NOTE  | [iOS] xcodebuild:  note: Planning build
    - NOTE  | [iOS] xcodebuild:  note: Constructing build description

[!] You (zgpeace@gmail.com) are not allowed to push new versions for this pod. The owners of this pod are dev.mjun@gmail.com.

实际上主要简单重命名podspec文件的名字,比如加个前缀(例子:ZgpeaceKnobControl), 并且把文件的s.name也改为相同的名字即可.

s.name         = "ZgpeaceKnobControl"

重新发布pod, 成功信息如下

% pod trunk push ./ZgpeaceKnobControl.podspec
Updating spec repo `master`
Validating podspec
 -> ZgpeaceKnobControl (1.0.1)
    - NOTE  | xcodebuild:  note: Using new build system
    - NOTE  | [iOS] xcodebuild:  note: Planning build
    - NOTE  | [iOS] xcodebuild:  note: Constructing build description

Updating spec repo `master`

--------------------------------------------------------------------------------
 ?  Congrats

 ?  ZgpeaceKnobControl (1.0.1) successfully published
 ?  July 2nd, 02:57
 ?  https://cocoapods.org/pods/ZgpeaceKnobControl
 ?  Tell your friends!
--------------------------------------------------------------------------------

  1. 运用到项目
    修改Podfile的引入为
pod 'ZgpeaceKnobControl', '1.0.1'

导入三方库

pod update

成功后运行项目,跟原来一样。

7. 总结

Framework给iOS提供了代码共享的解决方案,本地创建Framework可以验证重用代码的可行性,Local Pod可以验证通过pod方式引入三方库,发布到git可以共享pod,pod trunk push可以在公网共享三方库。

创建依赖的Framework请参考 – 创建CocoaPods的Framework Swift组件化之路(下)

代码下载

demo 原始代码(StarterPrepare文件夹)、中间代码(Intermediate文件夹)、本地Pod结果代码(Final文件夹)、公网pod结果代码(FinalPodPush文件夹):
https://github.com/zgpeace/KnobControl/tree/start
pod 仓库:
https://github.com/zgpeace/KnobControl

参考

https://www.raywenderlich.com/5109-creating-a-framework-for-ios
https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html
https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/Introduction/Introduction.html#//apple_ref/doc/uid/10000123i
https://guides.cocoapods.org/making/getting-setup-with-trunk.html
https://guides.cocoapods.org/using/the-podfile.html

iOS组件化开发

阅读数 458