RxSwift 是我在 Github 上關注已久的一個項目,今天花點時間過了一下它的示例代碼,感受頗有意思。react
我主要是經過項目裏的 Rx.playground 進行學習和了解的,這種方式確實便捷高效。只須要把文檔用 /*: */
註釋便可,直接用 Markdown 編寫,簡單方便。不過 Xcode7 中這種方式如今還不是很穩定,會有大量的空行,並且有個最大的問題就是閱讀到中間而後切到其餘文件再切回來的時候,閱讀的進度條是從頭開始的,並不能記錄上次閱讀的位置。心累。jquery
下面是個人簡單筆記,只是把學習過程當中的收穫記錄下來,大部份內容來自於項目內的 playground 。注意!是很大部分!並且操場裏圖文並茂,很容易理解。因此,各位若是感興趣,建議 clone 官方項目,跑個操場玩玩。git
參考文獻中羅列了我在學習過程當中查閱的相關資料,能夠做爲補充閱讀。github
SupportCode
在進入正題以前,先看下項目裏的 SupportCode.swift
,主要爲 playground 提供了兩個便利函數。swift
一個是 example
函數,專門用來寫示例代碼的,統一輸出 log 便於標記瀏覽,同時還能保持變量不污染全局:promise
- public func example(description: String, action: () -> ()) {
- print("\n--- \(description) example ---")
- action()
- }
另外一個是 delay
函數,經過 dispatch_after
用來演示延時的:閉包
- public func delay(delay:Double, closure:()->()) {
- dispatch_after(
- dispatch_time(
- DISPATCH_TIME_NOW,
- Int64(delay * Double(NSEC_PER_SEC))
- ),
- dispatch_get_main_queue(), closure)
- }
Introduction
主要介紹了 Rx 的基礎: Observable
。 Observable<Element>
是觀察者模式中被觀察的對象,至關於一個事件序列 (GeneratorType
) ,會向訂閱者發送新產生的事件信息。事件信息分爲三種:dom
-
.Next(value)
表示新的事件數據。 -
.Completed
表示事件序列的完結。 -
.Error
一樣表示完結,可是表明異常致使的完結。
(打個岔:協議命名,想起來上午湯哥在微博說的一段話:
另外,我以爲 protocol 名字用形容詞會更加語義分明,好比 Swift : Flyable, Killable, Visible。全用名詞的話顯得比較生硬,好比 Swift : Head, Wings, Ass。
empty
empty
是一個空的序列,它只發送 .Completed
消息。
- example("empty") {
- let emptySequence: Observable<Int> = empty()
- let subscription = emptySequence
- .subscribe { event in
- print(event)
- }
- }
- --- empty example ---
- Completed
never
never
是沒有任何元素、也不會發送任何事件的空序列。
- example("never") {
- let neverSequence: Observable<String> = never()
- let subscription = neverSequence
- .subscribe { _ in
- print("This block is never called.")
- }
- }
- --- never example ---
just
just
是隻包含一個元素的序列,它會先發送 .Next(value)
,而後發送 .Completed
。
- example("just") {
- let singleElementSequence = just(32)
- let subscription = singleElementSequence
- .subscribe { event in
- print(event)
- }
- }
- --- just example ---
- Next(32)
- Completed
sequenceOf
sequenceOf
能夠把一系列元素轉換成事件序列。
- example("sequenceOf") {
- let sequenceOfElements/* : Observable<Int> */ = sequenceOf(0, 1, 2, 3)
- let subscription = sequenceOfElements
- .subscribe { event in
- print(event)
- }
- }
- --- sequenceOf example ---
- Next(0)
- Next(1)
- Next(2)
- Next(3)
- Completed
form
form
是經過 asObservable()
方法把 Swift 中的序列 (SequenceType
) 轉換成事件序列。
- example("from") {
- let sequenceFromArray = [1, 2, 3, 4, 5].asObservable()
- let subscription = sequenceFromArray
- .subscribe { event in
- print(event)
- }
- }
- --- from example ---
- Next(1)
- Next(2)
- Next(3)
- Next(4)
- Next(5)
- Completed
create
create
能夠經過閉包建立序列,經過 .on(e: Event)
添加事件。
- example("create") {
- let myJust = { (singleElement: Int) -> Observable<Int> in
- return create { observer in
- observer.on(.Next(singleElement))
- observer.on(.Completed)
- return NopDisposable.instance
- }
- }
- let subscription = myJust(5)
- .subscribe { event in
- print(event)
- }
- }
- --- create example ---
- Next(5)
- Completed
failWith
failWith
建立一個沒有元素的序列,只會發送失敗 (.Error
) 事件。
- example("failWith") {
- let error = NSError(domain: "Test", code: -1, userInfo: nil)
- let erroredSequence: Observable<Int> = failWith(error)
- let subscription = erroredSequence
- .subscribe { event in
- print(event)
- }
- }
- --- failWith example ---
- Error(Error Domain=Test Code=-1 "The operation couldn’t be completed. (Test error -1.)")
deferred
deferred
會等到有訂閱者的時候再經過工廠方法建立 Observable
對象,每一個訂閱者訂閱的對象都是內容相同而徹底獨立的序列。
- example("deferred") {
- let deferredSequence: Observable<Int> = deferred {
- print("creating")
- return create { observer in
- print("emmiting")
- observer.on(.Next(0))
- observer.on(.Next(1))
- observer.on(.Next(2))
- return NopDisposable.instance
- }
- }
- print("go")
- deferredSequence
- .subscribe { event in
- print(event)
- }
- deferredSequence
- .subscribe { event in
- print(event)
- }
- }
- --- deferred example ---
- go
- creating
- emmiting
- Next(0)
- Next(1)
- Next(2)
- creating
- emmiting
- Next(0)
- Next(1)
- Next(2)
爲何須要 defferd
這樣一個奇怪的傢伙呢?其實這至關因而一種延時加載,由於在添加監聽的時候數據未必加載完畢,例以下面這個例子:
- example("TestDeferred") {
- var value: String? = nil
- var subscription: Observable<String?> = just(value)
- // got value
- value = "Hello!"
- subscription.subscribe { event in
- print(event)
- }
- }
- --- TestDeferred example ---
- Next(nil)
- Completed
若是使用 deffered
則能夠正常顯示想要的數據:
- example("TestDeferred") {
- var value: String? = nil
- var subscription: Observable<String?> = deferred {
- return just(value)
- }
- // got value
- value = "Hello!"
- subscription.subscribe { event in
- print(event)
- }
- }
- --- TestDeferred example ---
- Next(Optional("Hello!"))
- Completed
Subjects
接下來是關於 Subject
的內容。 Subject
能夠看作是一種代理和橋樑。它既是訂閱者又是訂閱源,這意味着它既能夠訂閱其餘 Observable
對象,同時又能夠對它的訂閱者們發送事件。
若是把 Observable
理解成不斷輸出事件的水管,那 Subject
就是套在上面的水龍頭。它既懟着一根不斷出水的水管,同時也向外面輸送着新鮮水源。若是你直接用水杯接着水管的水,那可能導出來什麼王水膠水徹底把持不住;若是你在水龍頭下面接着水,那你能夠爲所欲爲的調成你想要的水速和水溫。
(好吧上面一段文檔裏沒有,是我瞎掰的,若是理解錯了還望打臉( ̄ε(# ̄)☆╰╮( ̄▽ ̄///))
在開始下面的代碼以前,先定義一個輔助函數用於輸出數據:
- func writeSequenceToConsole<O: ObservableType>(name: String, sequence: O) {
- sequence
- .subscribe { e in
- print("Subscription: \(name), event: \(e)")
- }
- }
PublishSubject
PublishSubject
會發送訂閱者從訂閱以後的事件序列。
- example("PublishSubject") {
- let subject = PublishSubject<String>()
- writeSequenceToConsole("1", sequence: subject)
- subject.on(.Next("a"))
- subject.on(.Next("b"))
- writeSequenceToConsole("2", sequence: subject)
- subject.on(.Next("c"))
- subject.on(.Next("d"))
- }
- --- PublishSubject example ---
- Subscription: 1, event: Next(a)
- Subscription: 1, event: Next(b)
- Subscription: 1, event: Next(c)
- Subscription: 2, event: Next(c)
- Subscription: 1, event: Next(d)
- Subscription: 2, event: Next(d)
ReplaySubject
ReplaySubject
在新的訂閱對象訂閱的時候會補發全部已經發送過的數據隊列,bufferSize
是緩衝區的大小,決定了補發隊列的最大值。若是 bufferSize
是1,那麼新的訂閱者出現的時候就會補發上一個事件,若是是2,則補兩個,以此類推。
- example("ReplaySubject") {
- let subject = ReplaySubject<String>.create(bufferSize: 1)
- writeSequenceToConsole("1", sequence: subject)
- subject.on(.Next("a"))
- subject.on(.Next("b"))
- writeSequenceToConsole("2", sequence: subject)
- subject.on(.Next("c"))
- subject.on(.Next("d"))
- }
- --- ReplaySubject example ---
- Subscription: 1, event: Next(a)
- Subscription: 1, event: Next(b)
- Subscription: 2, event: Next(b) // 補了一個 b
- Subscription: 1, event: Next(c)
- Subscription: 2, event: Next(c)
- Subscription: 1, event: Next(d)
- Subscription: 2, event: Next(d)
BehaviorSubject
BehaviorSubject
在新的訂閱對象訂閱的時候會發送最近發送的事件,若是沒有則發送一個默認值。
- example("BehaviorSubject") {
- let subject = BehaviorSubject(value: "z")
- writeSequenceToConsole("1", sequence: subject)
- subject.on(.Next("a"))
- subject.on(.Next("b"))
- writeSequenceToConsole("2", sequence: subject)
- subject.on(.Next("c"))
- subject.on(.Completed)
- }
- --- BehaviorSubject example ---
- Subscription: 1, event: Next(z)
- Subscription: 1, event: Next(a)
- Subscription: 1, event: Next(b)
- Subscription: 2, event: Next(b)
- Subscription: 1, event: Next(c)
- Subscription: 2, event: Next(c)
- Subscription: 1, event: Completed
- Subscription: 2, event: Completed
Variable
Variable
是基於 BehaviorSubject
的一層封裝,它的優點是:不會被顯式終結。即:不會收到 .Completed
和 .Error
這類的終結事件,它會主動在析構的時候發送 .Complete
。
e
- xample("Variable") {
- let variable = Variable("z")
- writeSequenceToConsole("1", sequence: variable)
- variable.value = "a"
- variable.value = "b
- writeSequenceToConsole("2", sequence: variable)
- variable.value = "c"
- }
- --- Variable example ---
- Subscription: 1, event: Next(z)
- Subscription: 1, event: Next(a)
- Subscription: 1, event: Next(b)
- Subscription: 2, event: Next(b)
- Subscription: 1, event: Next(c)
- Subscription: 2, event: Next(c)
- Subscription: 1, event: Completed
- Subscription: 2, event: Completed
Transform
咱們能夠對序列作一些轉換,相似於 Swift 中 CollectionType
的各類轉換。在之前的坑中曾經提到過,能夠參考:函數式的函數。
map
map
就是對每一個元素都用函數作一次轉換,挨個映射一遍。
- example("map") {
- let originalSequence = sequenceOf(1,2,3)
- originalSequence
- .map { $0 * 2 }
- .subscribe { print($0) }
- }
- --- map example ---
- Next(2)
- Next(4)
- Next(6)
- Completed
flatMap
map
在作轉換的時候很容易出現『升維』的狀況,即:轉變以後,從一個序列變成了一個序列的序列。
什麼是『升維』?在集合中咱們能夠舉這樣一個例子,我有一個好友列表 [p1, p2, p3]
,那麼若是要獲取我好友的好友的列表,能夠這樣作:
myFriends.map { $0.getFriends() }
結果就成了 [[p1-1, p1-2, p1-3], [p2-1], [p3-1, p3-2]]
,這就成了好友的好友列表的列表了。這就是一個『升維』的例子。
(以上內容文檔中依舊沒有,依舊是我瞎掰的,依舊歡迎有錯誤當面打臉( ̄ε(# ̄)☆╰╮( ̄▽ ̄///))
在 Swift 中,咱們能夠用 flatMap
過濾掉 map
以後的 nil
結果。在 Rx 中, flatMap
能夠把一個序列轉換成一組序列,而後再把這一組序列『拍扁』成一個序列。
- example("flatMap") {
- let sequenceInt = sequenceOf(1, 2, 3)
- let sequenceString = sequenceOf("A", "B", "--")
- sequenceInt
- .flatMap { int in
- sequenceString
- }
- .subscribe {
- print($0)
- }
- }
- --- flatMap example ---
- Next(A)
- Next(B)
- Next(--)
- Next(A)
- Next(B)
- Next(--)
- Next(A)
- Next(B)
- Next(--)
- Completed
scan
- scan 有點像 reduce ,它會把每次的運算結果累積起來,做爲下一次運算的輸入值。
- example("scan") {
- let sequenceToSum = sequenceOf(0, 1, 2, 3, 4, 5)
- sequenceToSum
- .scan(0) { acum, elem in
- acum + elem
- }
- .subscribe {
- print($0)
- }
- }
- --- scan example ---
- Next(0)
- Next(1)
- Next(3)
- Next(6)
- Next(10)
- Next(15)
- Completed
Filtering
除了上面的各類轉換,咱們還能夠對序列進行過濾。
filter
filter
只會讓符合條件的元素經過。
- example("filter") {
- let subscription = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
- .filter {
- $0 % 2 == 0
- }
- .subscribe {
- print($0)
- }
- }
- --- filter example ---
- Next(0)
- Next(2)
- Next(4)
- Next(6)
- Next(8)
- Completed
distinctUntilChanged
- distinctUntilChanged 會廢棄掉重複的事件。
- example("distinctUntilChanged") {
- let subscription = sequenceOf(1, 2, 3, 1, 1, 4)
- .distinctUntilChanged()
- .subscribe {
- print($0)
- }
- }
- --- distinctUntilChanged example ---
- Next(1)
- Next(2)
- Next(3)
- Next(1)
- Next(4)
- Completed
take
take
只獲取序列中的前 n 個事件,在知足數量以後會自動 .Completed
。
- example("take") {
- let subscription = sequenceOf(1, 2, 3, 4, 5, 6)
- .take(3)
- .subscribe {
- print($0)
- }
- }
- --- take example ---
- Next(1)
- Next(2)
- Next(3)
- Completed
Combining
這部分是關於序列的運算,能夠將多個序列源進行組合拼裝成一個新的事件序列。
startWith
- startWith 會在隊列開始以前插入一個事件元素。
- example("startWith") {
- let subscription = sequenceOf(4, 5, 6)
- .startWith(3)
- .subscribe {
- print($0)
- }
- }
- --- startWith example ---
- Next(3)
- Next(4)
- Next(5)
- Next(6)
- Completed
combineLatest
若是存在兩條事件隊列,須要同時監聽,那麼每當有新的事件發生的時候,combineLatest
會將每一個隊列的最新的一個元素進行合併。
- example("combineLatest 1") {
- let intOb1 = PublishSubject<String>()
- let intOb2 = PublishSubject<Int>()
- combineLatest(intOb1, intOb2) {
- "\($0) \($1)"
- }
- .subscribe {
- print($0)
- }
- intOb1.on(.Next("A"))
- intOb2.on(.Next(1))
- intOb1.on(.Next("B"))
- intOb2.on(.Next(2))
- }
- --- combineLatest 1 example ---
- Next(A 1)
- Next(B 1)
- Next(B 2)
zip
- zip 人如其名,就是壓縮兩條隊列用的,不過它會等到兩個隊列的元素一一對應地湊齊了以後再合併。
- example("zip 1") {
- let intOb1 = PublishSubject<String>()
- let intOb2 = PublishSubject<Int>()
- zip(intOb1, intOb2) {
- "\($0) \($1)"
- }
- .subscribe {
- print($0)
- }
- intOb1.on(.Next("A"))
- intOb2.on(.Next(1))
- intOb1.on(.Next("B"))
- intOb1.on(.Next("C"))
- intOb2.on(.Next(2))
- }
- --- zip 1 example ---
- Next(A 1)
- Next(B 2)
marge
merge
就是 merge 啦,把兩個隊列按照順序組合在一塊兒。
- example("merge 1") {
- let subject1 = PublishSubject<Int>()
- let subject2 = PublishSubject<Int>()
- sequenceOf(subject1, subject2)
- .merge()
- .subscribeNext { int in
- print(int)
- }
- subject1.on(.Next(1))
- subject1.on(.Next(2))
- subject2.on(.Next(3))
- subject1.on(.Next(4))
- subject2.on(.Next(5))
- }
- --- merge 1 example ---
- 1
- 2
- 3
- 4
- 5
switch
當你的事件序列是一個事件序列的序列 (Observable<Observable<T>>
) 的時候,(能夠理解成二維序列?),可使用 switch
將序列的序列平鋪成一維,而且在出現新的序列的時候,自動切換到最新的那個序列上。和 merge
類似的是,它也是起到了將多個序列『拍平』成一條序列的做用。
- example("switchLatest") {
- let var1 = Variable(0)
- let var2 = Variable(200)
- // var3 is like an Observable<Observable<Int>>
- let var3 = Variable(var1)
- let d = var3
- .switchLatest()
- .subscribe {
- print($0)
- }
- var1.value = 1
- var1.value = 2
- var1.value = 3
- var1.value = 4
- var3.value = var2
- var2.value = 201
- var1.value = 5
- var3.value = var1
- var2.value = 202
- var1.value = 6
- }
- --- switchLatest example ---
- Next(0)
- Next(1)
- Next(2)
- Next(3)
- Next(4)
- Next(200)
- Next(201)
- Next(5)
- Next(6)
注意,雖然都是『拍平』,可是和 flatmap
是不一樣的, flatmap
是將一條序列變成另外一條序列,而這變換過程會讓維度變高,因此須要『拍平』,而 switch
是將原本二維的序列(序列的序列)拍平成了一維的序列。
Error Handling
在事件序列中,遇到異常也是很正常的事情,有如下幾種處理異常的手段。
catchError
catchError
能夠捕獲異常事件,而且在後面無縫接上另外一段事件序列,絲毫沒有異常的痕跡。
- example("catchError 1") {
- let sequenceThatFails = PublishSubject<Int>()
- let recoverySequence = sequenceOf(100, 200)
- sequenceThatFails
- .catchError { error in
- return recoverySequence
- }
- .subscribe {
- print($0)
- }
- sequenceThatFails.on(.Next(1))
- sequenceThatFails.on(.Next(2))
- sequenceThatFails.on(.Error(NSError(domain: "Test", code: 0, userInfo: nil)))
- }
- --- catchError 1 example ---
- Next(1)
- Next(2)
- Next(100)
- Next(200)
- Completed
retry
retry
顧名思義,就是在出現異常的時候會再去從頭訂閱事件序列,妄圖經過『從頭再來』解決異常。
- example("retry") {
- var count = 1 // bad practice, only for example purposes
- let funnyLookingSequence: Observable<Int> = create { observer in
- let error = NSError(domain: "Test", code: 0, userInfo: nil)
- observer.on(.Next(0))
- observer.on(.Next(1))
- if count < 2 {
- observer.on(.Error(error))
- count++
- }
- observer.on(.Next(2))
- observer.on(.Completed)
- return NopDisposable.instance
- }
- funnyLookingSequence
- .retry()
- .subscribe {
- print($0)
- }
- }
- --- retry example ---
- Next(0)
- Next(1)
- Next(0)
- Next(1)
- Next(2)
- Completed
Utility
這裏列舉了針對事件序列的一些方法。
subscribe
subscribe
在前面已經接觸過了,有新的事件就會觸發。
- example"subscribe") {
- let sequenceOfInts = PublishSubject<Int>()
- sequenceOfInts
- .subscribe {
- print($0)
- }
- sequenceOfInts.on(.Next(1))
- sequenceOfInts.on(.Completed)
- }
- --- subscribe example ---
- Next(1)
- Completed
- subscribeNext
- subscribeNext 也是訂閱,可是隻訂閱 .Next 事件。
- example("subscribeNext") {
- let sequenceOfInts = PublishSubject<Int>()
- sequenceOfInts
- .subscribeNext {
- print($0)
- }
- sequenceOfInts.on(.Next(1))
- sequenceOfInts.on(.Completed)
- }
- --- subscribeNext example ---
- 1
subscribeCompleted
- subscribeCompleted 是隻訂閱 .Completed 完成事件。
- example("subscribeCompleted") {
- let sequenceOfInts = PublishSubject<Int>()
- sequenceOfInts
- .subscribeCompleted {
- print("It's completed")
- }
- sequenceOfInts.on(.Next(1))
- sequenceOfInts.on(.Completed)
- }
- --- subscribeCompleted example ---
- It's completed
subscribeError
- subscribeError 只訂閱 .Error 失敗事件。
- example("subscribeError") {
- let sequenceOfInts = PublishSubject<Int>()
- sequenceOfInts
- .subscribeError { error in
- print(error)
- }
- sequenceOfInts.on(.Next(1))
- sequenceOfInts.on(.Error(NSError(domain: "Examples", code: -1, userInfo: nil)))
- }
- --- subscribeError example ---
- Error Domain=Examples Code=-1 "The operation couldn’t be completed. (Examples error -1.)"
doOn
- doOn 能夠監聽事件,而且在事件發生以前調用。
- example("doOn") {
- let sequenceOfInts = PublishSubject<Int>()
- sequenceOfInts
- .doOn {
- print("Intercepted event \($0)")
- }
- .subscribe {
- print($0)
- }
- sequenceOfInts.on(.Next(1))
- sequenceOfInts.on(.Completed)
- }
- --- doOn example ---
- Intercepted event Next(1)
- Next(1)
- Intercepted event Completed
- Completed
Conditional
咱們能夠對多個事件序列作一些複雜的邏輯判斷。
takeUntil
takeUntil
其實就是 take
,它會在終於等到那個事件以後觸發 .Completed
事件。
- example("takeUntil") {
- let originalSequence = PublishSubject<Int>()
- let whenThisSendsNextWorldStops = PublishSubject<Int>()
- originalSequence
- .takeUntil(whenThisSendsNextWorldStops)
- .subscribe {
- print($0)
- }
- originalSequence.on(.Next(1))
- originalSequence.on(.Next(2))
- whenThisSendsNextWorldStops.on(.Next(1))
- originalSequence.on(.Next(3))
- }
- --- takeUntil example ---
- Next(1)
- Next(2)
- Completed
takeWhile
takeWhile
則是能夠經過狀態語句判斷是否繼續 take
。
- example("takeWhile") {
- let sequence = PublishSubject<Int>()
- sequence
- .takeWhile { int in
- int < 2
- }
- .subscribe {
- print($0)
- }
- sequence.on(.Next(1))
- sequence.on(.Next(2))
- sequence.on(.Next(3))
- }
- --- takeWhile example ---
- Next(1)
- Completed
Aggregate
咱們能夠對事件序列作一些集合運算。
concat
concat
能夠把多個事件序列合併起來。
- example("concat") {
- let var1 = BehaviorSubject(value: 0)
- let var2 = BehaviorSubject(value: 200)
- // var3 is like an Observable<Observable<Int>>
- let var3 = BehaviorSubject(value: var1)
- let d = var3
- .concat()
- .subscribe {
- print($0)
- }
- var1.on(.Next(1))
- var1.on(.Next(2))
- var3.on(.Next(var2))
- var2.on(.Next(201))
- var1.on(.Next(3))
- var1.on(.Completed)
- var2.on(.Next(202))
- }
- --- concat example ---
- Next(0)
- Next(1)
- Next(2)
- Next(3)
- Next(201)
- Next(202)
reduce
這裏的 reduce
和 CollectionType
中的 reduce
是一個意思,都是指經過對一系列數據的運算最後生成一個結果。
- example("reduce") {
- sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
- .reduce(0, +)
- .subscribe {
- print($0)
- }
- }
- --- reduce example ---
- Next(45)
- Completed
Next
基礎入門大概就是這些了,有了前面 《Functional Reactive Programming in Swift - Part 1》 的鋪墊,彷佛理解起來十分愉快,不過仍是不夠深刻,在下一章會在具體項目中操練起來。
操練起來!跑個操場吧少年!
Run the playground in your Xcode!
參考文獻:
- ReactiveX
- RAC Marbles
- defer
- jQuery 的 deferred 對象詳解
- jQuery deferred 對象的 promise 方法
- Deferring Observable code until subscription in RxJava
- To Use Subject Or Not To Use Subject?
- flatmap
- Grokking RxJava, Part 2: Operator, Operator
- Transformation - Select Many
- RxJava Observable tranformation: concatMap() vs flatMap()
- Recursive Observables with RxJava
- Combining sequences
- switch
- RxSwift Getting Started