Git Product home page Git Product logo

frdintent's Introduction

FRDIntent

Test Status Language IDE iOS

README in English

FRDIntent 包括两部分 FRDIntent/IntentFRDIntent/URLRoutes。它们分别可以用于处理 iOS 系统中,应用内和应用外的 view controller 调用。

FRDIntent/Intent 是一个消息传递对象,用于启动 UIViewController。可以认为它是对 Android 系统中的 Intent 的模仿。

FRDIntent/URLRoutes 是一个 URL Router。通过 FRDIntent/URLRoutes 可以用 URL 调起一个注册过的 block。

可以看出,FRDIntent/URLRoutes 和社区已经存在的诸多 URL Routers 的功能和目的差别不大。在 FRDIntent 中,实现 URLRoutes 是为了让 FRDIntent/URLRoutes 和 FRDIntent/Intent 一起配合解决应用内和应用外 view controller 的调用。Intent 处理内部 view controller 跳转;URLRoutes 负责外部调用。在 FRDIntent/URLRoutes 的实现中,FRDIntent/URLRoutes 只是起了暴露外部调用入口,接收外部调用的作用。在应用内,仍然是通过 FRDIntent/Intent 启动 view controller。也就是说外部调用实际上是通过内部调用实现的。

这么做其实是为了隔离了外部调用和内部调用,做这个区分会带来一些好处:

  • iOS 系统提供的通过 URL 调用另外一个应用功能本身就是使用在应用间的。iOS 系统中,应用之间的隔离是清晰而明确的,通过 URL 在应用之间传递信息是合适的。但是,如果在应用内部调用也使用 URL 传递信息,就会带来诸多限制。Intent 更适合内部调用的场景。通过 Intent,可以传递复杂数据对象,可以较容易地定义转场动画。这些在 URL 方案中都很难做到。
  • 区分了外部调用和内部调用。我们就可以选择是否将一个内部调用暴露给外部使用。这就避免了在 URL 的方案中,无法区分内部调用和外部调用,将本应只给内部使用的调用也暴露给了外部。

安装

Install Cocoapods

CocoaPods 是一个 Objective-c 和 Swift 的依赖管理工具。你可以通过以下命令安装 CocoaPods:

$ gem install cocoapods

Podfile

只使用 FRDInent/Intent:

target 'TargetName' do
  pod 'FRDIntent/Intent', :git => 'https://github.com/douban/FRDIntent.git'
end

使用 FRDIntent/Intent 和 FRDIntent/URLRoutes:

target 'TargetName' do
  pod 'FRDIntent', :git => 'https://github.com/douban/FRDIntent.git'
end

注意:pod FRDInentpod FRDIntent/URLRoutes 将引入相同的代码。这是因为 FRDIntent/URLRoutes 依赖于 FRDIntent/Intent。

然后,命令行运行:

$ pod install

版本

版本选择:https://github.com/douban/FRDIntent/releases

Intent

FRDIntent/Intent 是一个消息传递对象,用于启动 UIViewController。可以认为它是对 Android 系统中的 Intent 的模仿。当然,相对于 Android Intent,FRDIntent/Intent 做了极度简化。这是因为 FRDIntent/Intent 的使用场景更为简单:只处理应用内的 view controller 间跳转。

直接使用 iOS 系统方法完成各 view controller 之间的跳转,各 view controller 会耦合得很紧。跳转时,一个 view controller 需要知道下一个 view controller 是如何创建的各种细节。这造成了 view controller 之间的依赖。使用 FRDIntent/Intent 传递跳转信息,可以解除 view controller 之间的耦合。

如果需要对项目进行模块化,重要的一步就是解除各 view controller 之间的耦合。在这方面,FRDIntent 是一个可以考虑的方案。

FRDIntent/Intent 有如下优势:

  • 充分解耦。调用者和被调用者完全隔离,被调用者只需要依赖协议:FRDIntentReceivable。一个 UIViewControlller 符合该协议即可被启动。
  • 对于“启动一个页面,并从该页面获取结果”这种较普遍的需求提供了一个通用的解决方案。具体查看方法:startControllerForResult。这是对 Android 中 startActivityForResult 的模仿和简化。
  • 支持自定义转场动画。
  • 支持传递复杂数据对象。

使用

主要通过类 FRDControllerManager 使用 FRDIntent/Intent。它提供了三个方法:register 用于注册,startControllerstartControllerForResult 用于启动页面。

注册

通过代码注册:

  let controllerManager = FRDControllerManager.sharedInstance
  controllerManager.register(URL(string: "/frodo/firstview")!, clazz: FirstViewController.self)

通过 plist 文件批量注册:

  let plistPath = Bundle.main.path(forResource: "FRDIntentRegisters", ofType: "plist")
  let controllerManager = FRDControllerManager.sharedInstance
  controllerManager.register(plistFile: plistPath)

启动 view controller

通过指定类名启动 view controller:

  let intent = FRDIntent(clazz: SecondViewController.self)
  let manager = FRDControllerManager.sharedInstance
  manager.startController(source: self, intent: intent)

通过 URL 启动 view controller:

  let intent = FRDIntent(uri: URL(string: "/frodo/firstview")!)
  let manager = FRDControllerManager.sharedInstance
  manager.startController(source: self, intent: intent)

启动一个会返回结果的 view controller

调用页面,该页面同时也是接受返回结果的页面。该 view controller 需要符合协议 FRDIntentForResultSendable

  extension ViewController: FRDIntentForResultSendable {

    func onControllerResult(requestCode: Int, resultCode: FRDResultCode, data: Intent) {
      if (requestCode == RequestText) {
        if (resultCode == .ok) {
          let text = data.extra["text"]
          print("Successful confirm get from destination : \(text)")
        } else if (resultCode == .canceled) {
          let text = data.extra["text"]
          print("Canceled get from destination : \(text)")
        }
      }
    }

  }

被调用的 view controller 需要符合协议 FRDIntentForResultReceivable。该协议是 FRDIntentReceivable 的子协议。在 FRDIntentReceivable 基础上,多了两个实例变量定义:

  var delegate: FRDIntentForResultSendable?
  var requestCode: Int?

通过 startControllerForResult 启动页面:

  let intent = FRDIntent(clazz: ThirdViewController.self)
  intent.putExtra(name: "text", data: "Text From Source")
  let manager = FRDControllerManager.sharedInstance
  manager.startControllerForResult(source: self, intent: intent, requestCode: RequestText)

自定义转场动画

在 FRDIntent 中,转场动画被抽象为协议:FRDControllerDisplay。自定义转场动画的实现需要符合该协议,并在启动页面时,将自定义的转场动画对象赋给 FRDIntent 的实例变量 controllerDisplay 即可完成转场动画的设置。

FRDIntent/Intent 已提供了两个转场动画的实现:FRDPushDisplayFRDPresentationDisplay。如果不指定转场动画,通过 startController 启动页面使用的是 FRDPushDisplay;通过 startControllerForResult 启动页面使用的是 FRDPresentationDisplay

URLRoutes

FRDIntent/URLRoutes 是一个 URL Router。通过 FRDIntent/URLRoutes 可以用 URL 调起一个注册过的 block。

iOS 系统为各个应用间的相互调用提供了一种基于 URL 的处理方案。即应用可以声明自己可以处理某些有特定 scheme 和 host 的 URL。其他应用就可以通过调用这些 URL 而跳转到该应用的某些页面。

FRDIntent/URLRoutes 是为了使得 iOS 系统中这种基于 URL 的应用间调用的处理更为简单。所以 FRDIntent/URLRoutes 和社区已经存在的诸多 URL Routers 的功能和目的差别不大。FRDIntent 实现 URLRoutes 是为了使 FRDIntent/URLRoutes 可以和 FRDIntent/Intent 配合解决应用内和应用外 view controller 的调用。

使用

向系统暴露应用可以接收的 URL

在 Xcode 中选择你的项目的 Target, 点击 Info, 添加一项 URL Types。 例如:

  • Identifier: com.frdintent
  • URL Schemes: frdintent
  • Role: Editor
  • Icon:

接管应用的 URL 处理

  func application(app: UIApplication, openURL url: URL, options: [String : AnyObject]) -> Bool {
    return FRDURLRoutes.sharedInstance.route(url: url)
  }

注册

通过代码注册一个 view controler。在第三方应用调起该 URL 时,会启动该 view controller。该 view controller 的进入动画为 Push 横滑进入方式。

  let routes = FRDURLRoutes.sharedInstance
  routes.register(url: URL(string: "/story/:storyId")!, clazz: SecondViewController.self)

通过 plist 文件批量注册,效果和上面通过代码注册一样。注册的 view controller 进入动画都为 Push 横滑进入方式。

  let plistPath = Bundle.main.path(forResource: "FRDURLRoutesRegisters", ofType: "plist")
  let routes = FRDURLRoutes.sharedInstance
  routes.register(plistFile: plistPath)

注册一个 block handler。下面例子中的 block handler 中,用注册时的 URL 构造了一个 Intent,并将该 Intent 送出。FRDControllerManager 会处理这个 Intent。看是否有合适的 view controller 可以被启动。

如果,需要定制 view controller 的转场动画,可以使用该方法注册 URL。

  let router = FRDURLRoutes.sharedInstance
  router.register(url: URL(string: "/user/:userId")!) { (params: [String: Any]) in
    let intent = Intent(url: params[URLRoutes.URLRoutesURL] as! URL)
    if let topViewController = UIApplication.topViewController() {
      FRDControllerManager.sharedInstance.startController(source: topViewController, intent: intent)
    }
  }

获取 URL 参数

FRDIntent/URLRoutes 支持简单的 URL 参数模式适配。上例中,我们以模式匹配的形式注册了 URL "/story/:storyId"。如有诸如 frdintent://frdintent.com/story/123 这样的外部调用,FRDIntent/URLRoutes 会将键 storyId 和值 123 存入 block handler 的参数 params 中。这样在 block handler 中就能通过键 storyId 获取值 123

注意点

Prefix

Swift 由于有可见性声明,并无需前缀来避免命名冲突。所以,前缀在 Swift 项目中并不需要。但我们仍然为公开类都添加了FRD前缀。这是由于该库仍然主要供 Objective-C 项目使用,为了避免 Objective-C 代码的命名冲突,还是加上了前缀。

参数 source 的类型

FRDControllerManager 的方法 startControllerForResult(source: UIViewController, intent: FRDIntent, requestCode: Int) 没有严格限制 source 参数类型。source 精确的类型应该分别是形如 UIViewController<FRDIntentForResultSendable> 所表达的:“这是一个类,并且符合一个协议”。这在 Swift 3 中,仍然需要别扭地使用泛型声明来实现。但这里使用泛型声明并不精确,同时更麻烦的是泛型方法无法暴露给 Objective-C 使用。因此,FRDIntent 做了折衷,source 类型只是 UIViewController。使用者需要自己保证它也是符合 FRDIntentForResultSendable 协议的。

FRDIntentDemo

FRDIntentDemo 对 FRDIntent 各种使用方法都做了演示。FRDIntentDemo 使用 Objective-C 实现,这是为了演示 FRDIntent 虽然使用 Swift 完成,但是对 Objective-C 有良好的兼容。

对于外部调用的演示,可以在模拟器的 Safari 的地址栏中输入 frdintent://frdintent.com/user/123。正常情况下,访问该 URL 将会启动 FRDIntentDemo,并进入 FirstViewController。

单元测试

FRDIntentTests 文件夹包含了 FRDIntent 单元测试代码。单元测试不仅是对代码正确性的验证,也是查看如何使用 FRDIntent 的良好示例。

License

The MIT license.

frdintent's People

Contributors

bigyelow avatar calvingit avatar daydayfree avatar hao-feng avatar imhanhan avatar lincode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

frdintent's Issues

#探讨# TabBarViewController的几个Tab切换有必要用Intent吗?

个人感觉TabBarViewController和它的几个子ViewController是强耦合的东西,把TabBarViewController当做一个整体来看比当做几个子模块看会更好。

还有一些比如ChildViewController的切换,似乎都限制在它的ParentController里面,这种解耦是没必要的。

不知道你怎么认为?

页面跳转问题

安装的是xcode9,用cocoapods安装运行不了,xcode9最低只支持swift3.0的语法,直接下载demo导入项目中,页面不能跳转.

我想在导航状态下也可以进行返回结果的获取

在使用FRDIntent的时候发现只有模态状态才可以进行FRDIntentForResultSendable的方法获取返回的数据
尝试通过自定义转场动画发现不行看源码才知道 startControllerForResult 默认是模态的方法切无法进行自定义,只能通过更改源代码进行实现。希望作者看到后可以接纳我的建议
最后非常感谢作者提供了这个轮子

FRDIntent 跳转到VC后,在pop,这个VC不释放啊

FRDIntent *intent = [[FRDIntent alloc] initWithPathIdentifier:@"/video_detail"];
[intent putExtraWithName:@"videoId" data: [NSString stringWithFormat:@"%ld",(long)self.videoList[indexPath.row].videoId]];
[self startControllerWithIntent: intent];

啊啊啊

已经不维护了吗,pod不下来了

通过 plist 注册不成功

image

通过 plist 注册不成功,但是单独注册没问题,我看到里面的判断始终过不去,不懂为什么

if let clazz = NSClassFromString(className) as? FRDIntentReceivable.Type
这里的判断过不去,我的 TestController

image

Safari打开Demo后没有跳转到FirstVC

操作步骤:在Safari输入:frdintent://frdintent.com/user/123,点击"Open"
期望结果:打开FirstVC
实际结果:打开了主界面
环境:Xcode 8.1,iOS 10.1, iPhone 7 Plus Simulator

PS:未修改任何代码

模块间页面跳转没问题,如果单纯的数据流转呢?

比如说 B 模块向 A 模块提供一个数据获取获取方法 getData(), A 模块怎么去拿呢?

就是说不涉及到页面流转,只是单纯的数据流转。

我刚想了下,充分解耦之后,理论上应该让 A B 之间互相都不知道对方,页面跳转解决了,单纯数据交互呢?
有什么建议吗?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.