最近在自研一个新的项目,在考虑运用的技能栈时,调研了许多,比方react-native,flutter,以及端原生的oc跟swift,可是最终选择了swiftUI + combine,之所以有如此决定,一方面是希望能够完善自己关于iOS系统开发的技能完整性,另一方面希望了解iOS开发未来的一个技能方向,那么闲言少叙切入正题。什么是swiftUI?

SwiftUI是什么?

更准确地解说能够移步到苹果开发者中心,概念性的东西,这儿不做过多介绍。经过对其的一段时间开发,个人总结,swiftUI绝不是swift+UI这么简略的概念,从设计上,swiftUI非常趋近于web前端,苹果好像有意将swift做得愈加简化,swiftUI也是将开发者得注意力从之前无穷尽地修正UI转到愈加关注其app内部的逻辑处理。

简而言之,假如你的项目需求崇尚极简主义,重视逻辑而不选用复杂且臃肿的交互设计,那么swiftUI绝对是值得一试的技能手段。

关于swiftUI的各个组件,官方都给出的案例,这儿先不做研究,之后我会在自研项目上线之后,关于其间所用到的组件,遇到的问题,进行逐渐汇总,其间会有一些在国内论坛并不简略找到的问题答案。可是现在咱们先从根底数据下手,咱们先了解一下什么是combine

如何了解combine

谈到combine不得不提的便是swift中的特点润饰器– @propertyWrapper

@propertyWrapper

实话实说,假如你还没有用过propertyWrapper,那一定要尝试的运用一下,由于这个功能确实太好用了,这儿引证官方解说的一段话:

For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.

塑料翻译:

例如,假如你要为数据存储的一些根底特点供给线程安全或许存储它们,你不得不在每一个特点中都写相同的方法,这会让代码变得非常恶心。可是当你运用propertyWrapper时,当你为操作代码界说了一个润饰器,那么这些操作代码会应用在它润饰的多个特点中。

上面的解说,是我在学习propertyWrapper所能看到的最为浅显的解说。下面也是供给了一段官方代码,帮助了解。

@propertyWrapper
struct TwelveOrLess {
  private var number = 0
  var wrappedValue: Int {
    get { return number }
    set { number = min(newValue, 12) }
  }
}
struct SmallRectangle {
  @TwelveOrLess var height: Int
  @TwelveOrLess var width: Int
}
​
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
​
rectangle.height = 10
print(rectangle.height)
// Prints "10"
​
rectangle.height = 24
print(rectangle.height)
// Prints "12"

简略解说一下上面的代码,声明一个特点润饰器TwelveOrLess,内部的逻辑是输出的特点都比12小,假如大于12则输出12。

下面的SmallRectangle包装了两个特点heightwidth,当咱们为这两个特点赋值,再调用get方法时,能够看到,咱们的逻辑代码生效了,输出数字被控制在小于或等于12的值。

无需多余代码,特点润饰器给了swift开发者更多的幻想空间。

简略的介绍了一下propertyWrapper,接下来咱们回归正题,持续说回combine

Publishers 与 subscribers

假如想运用combine就不得不了解两个概念,Publishers 与 subscribers。假如你之前有做过Rxswift,或许关于RAC有一定了解的话,关于这两个概念一定不生疏。即便是关于上述框架并不了解,想要了解Publisherssubscribers也不难,由于能够把它了解为观察者模式中的发送者与监听者。

由于官方的案例选用的是通知中心的demo,这在我初学combine时给我带来了极大的困扰,因而,本文的案例并不计划选用官方案例,防止给读者带来相同的困扰。而是经过一段自己的部分开源代码对其进行解说。

struct XXAssetModel{
  var id = UUID()
  var currency: Int
 }
class XXResourceViewModel: ObservableObject {
   @Published var myAsset: XXAssetModel = UserData.userCurrency
      fileprivate func editCurrency() {
    myAsset.currency = myAsset.currency + 10
   }
}
 struct ConverterView : View {
  @ObservedObject var viewModel = XXResourceViewModel()
    var body: some View {
     return Text(viewModel.myAsset.currency) 
    }
}

这个比如相对简略,便于入门,咱们来看一下,首要,在XXResourceViewModel中声明一个被 @Published润饰的特点myAsset,由于咱们刚刚现已介绍过特点润饰器了,所以应该不难了解这个润饰的效果。下面引证官方的一段话。

Add the @Published annotation to a property of one of your own types. In doing so, the property gains a publisher that emits an event whenever the property’s value changes.

将 @Published 注释添加到类中的特点。这样做使该特点成为了一个publisher,只需该特点的值发生变化,publisher就会发出一个事件。

回到上面一段代码,publisher就像是电影《风声》中的老鬼,他的责任便是将自己获取的情报传递给他的上级老枪,那么,谁是subscribers老枪。上例中,Text控件便是老枪。他与viewModel.myAsset.currency形成了一种绑定联系,一旦viewModel.myAsset.currency发生改变,Text接收到信号之后,就会做出对应行动。

看到这有没有人在想到了一种设计模式?没错,便是MVVM。

Subject的运用

combine作为苹果官方推出的呼应式编程框架,很大程度的融合了其他呼应式编程框架的优点。除了这种自动发送信号的publisher,还有一种能够主动发送信号的Subject,看一下下面的比如。

final class UserData: ObservableObject {
  let objectWillChange = PassthroughSubject<UserData, Never>()
  
 var allCurrencies: [Currency] {
    didSet {
      objectWillChange.send(self)
    }
  }
 }
 struct ConverterView : View {
  @EnvironmentObject var userData: UserData
    var body: some View {
     return list(userData.allCurrencies) {
        Item()
     } 
    }
}

UserData作为信号发送方,没有选用publisher的方式,而是利用重写set方法对其进行了主动发送。

当然如何选择要具体问题,具体分析,苹果供给了相对丰富的方法,应对不同的运用场景。

Operators的运用

当然不只是监听信号这么简略,苹果还为开发者供给了多种Operators,意在愈加轻松的让开发者完结函数式编程。代码如下:

  static func request(_ kind: XXKind, _ queryItems: [URLQueryItem]?) -> AnyPublisher<XXResource, Error> {
    guard var components = URLComponents(url: baseUrl.appendingPathComponent(kind.rawValue), resolvingAgainstBaseURL: true)
      else { fatalError("Couldn't create URLComponents") }
    components.queryItems = queryItems
​
    let request = URLRequest(url: components.url!)
​
    return apiClient.run(request)
      .map(.value) // 为XXResource中界说的实际值
      .eraseToAnyPublisher()
  }

上述比如中,将回来的数据,经过map()函数进行了过滤操作,提取出回来值中value的数据,并将其发送给subscribers。如图所示:

SwiftUI开发总结(一) 这大概是最容易理解的combine

总结

本文作为SwiftUI学习的第一章,侧重的介绍了combine及其运用方法。文章主要以实战为主,少了许多花里胡哨的介绍跟润饰,希望能够让同学们能够愈加快速简略的了解。如最初所说,后续还会总结一下swiftUI中控件在运用时,与正常UIKit不太一样的坑。毕竟国内关于swiftUI的学习并不多,所以希望能够跟同学们一同进步。

respect!!!