質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
RxSwift

RxSwiftは、Reactive ExtensionsのSwift向けの実装です。iOS開発に用いられ、リアクティブプログラミングを可能にします。

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

1回答

737閲覧

RxSwiftで多次元配列をBehaviorSubjectに流したい

oyasumi

総合スコア13

RxSwift

RxSwiftは、Reactive ExtensionsのSwift向けの実装です。iOS開発に用いられ、リアクティブプログラミングを可能にします。

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2018/09/22 11:59

前提・実現したいこと

Swift初学者です。Todoアプリを作成しており新規のtodoを作成で ObservableArray<Dictionary>に新しい辞書を追加できたのですがアプリを落とさず再度todoを追加するときに一つ前の辞書が消えてしまいます。値をBehaviorSubjectに流せば良いのかと考えたのですがRxにも慣れておらず良い方法が思いつかず困っています。
イメージ説明

最終的に実現したいこととしては
アプリを落とすまではtoDosに辞書を追加し続け値に変更があるたびに表示するVCに配列を流せるようにしたいです。

該当のコード

全てView.Modelのコードです。

Swift

1import RxSwift 2import RxCocoa 3import ObservableArray_RxSwift 4 5final class CreateListViewModel { 6 7 let bag = DisposeBag() 8/// 中間Stream for Input 9 //PublishSubject:外からの入力のみを受け付ける 10 private let createBtnDidTapStream = PublishSubject<Void>() 11 private let genreStream = BehaviorSubject<String>(value: "") 12 private let toDoStream = BehaviorSubject<String>(value: "") 13/// 中間Stream for Output 14 private let navigateToMainStream = PublishSubject<Void>() 15 16 //仮説:ほんとはここに値を流して最新のものをあとで参照すれば良いのか 17 //private let toDoListStream = BehaviorSubject<[[String : Any]]>( value: [[:]]) 18 19 init() { 20 self.createBtnDidTapStream 21 .subscribe(onNext: {[weak self] _ in 22 self?.createList() 23 }) 24 .disposed(by: bag) 25 26 self.genreStream 27 .subscribe(onNext: { [weak self] genre in 28 print(genre) 29 }) 30 .disposed(by: bag) 31 self.toDoStream 32 .subscribe(onNext: { [weak self] toDo in 33 print(toDo) 34 }) 35 .disposed(by: bag) 36 } 37 38 39 var toDos: ObservableArray<Dictionary> = 40 [["toDo":"test", "hasDone": false, "genre": "test" ]] 41 42 func createList() { 43 //オンメモリでデータの一時保存。。これは保存というより代入 44 //データ保存の永続化は今回はなし 45 46 toDos.append([ 47 "toDo": try! toDoStream.value(), 48 "genre": try! genreStream.value(), 49 "hasDone": false 50 ]) 51 print(toDos) 52 //main画面へ遷移 53 self.navigateToMainStream.onNext(()) 54 55 56 } 57 58 59 60 func editStatus() { 61 62 } 63 64} 65///input 66extension CreateListViewModel { 67 //投稿ボタン 68 var createBtnDidTap: AnyObserver<()> { 69 return createBtnDidTapStream.asObserver() 70 } 71 //ジャンルテキストフィールド 72 var genre: AnyObserver<String> { 73 return genreStream.asObserver() 74 } 75 var toDo: AnyObserver<String> { 76 return toDoStream.asObserver() 77 } 78 79 80} 81 82 83///output 84extension CreateListViewModel { 85 var navigateToMain: Observable<Void> { 86 return self.navigateToMainStream.asObservable() 87 } 88// var toDoList: ObservableArray<[String: Any]> { 89// return self.toDoStream.asObservable() 90// } 91 92} 93 94 95

試したこと

return self.toDoStream.asObservable()でどうにか値を流せないか四苦八苦してました。

補足情報(FW/ツールのバージョンなど)

XCode 9.4.1
Swift 4.1.2
pod 1.5.3

配列をObservableにするためObservableArray_RxSwiftというライブラリを使っています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ObservableArray_RxSwift のREADMEを読む限りだと、使い方が違っているようですね。 rx_elements() メソッドにより提供されるストリームが合成後の配列を流してくれるものですから、そちらを見るようにしてみてください。


ここからは余談で、実装方針についてのアドバイスです。

ご質問の今のコードだと、RxSwiftがただの通知用ライブラリとして使われている感じでちょっともったいないです。せっかくRxSwiftを使っているのですから、もうちょっとリアクティブな実装にしてみましょう。

Rxでは、入力となるストリームを元にして出力となるストリームを組み立てていくことで処理を実装します。
今回の場合は、入力となるストリーム

  • createBtnDidTapStream
  • genreStream
  • toDoStream

の3つを元に出力 toDoListStream を組み立てることを考えてみます。

まず、値を追加する契機となるイベントはボタンのタップですから、 createBtnDidTapStreamがベースとなります。そこにgenreStream, toDoStreamの最新の値を加えてDictionaryを流すストリームは、以下のように実装できます。

swift

1self.toDoListStream = self.createBtnDidTapStream 2 .withLatestFrom(Observable.combineLatest( 3 self.toDoStream.asObservable(), 4 self.genreStream.asObservable() 5 )) { _, todoAndGenre -> [String: Any] in 6 let (todo, genre) = todoAndGenre 7 return [ "toDo": todo, "genre": genre, "hasDone": false ] 8 }

さらに、イベントが流れるたびに値を配列に追加していく処理は、scan オペレーターで実装できます。scanはreduceオペレーターと似ていますが、イベントが流れるたびに結果を流す点で異なります。(詳しくは調べてみてください)

swift

1self.toDoListStream = self.createBtnDidTapStream 2 .withLatestFrom(Observable.combineLatest( 3 self.toDoStream.asObservable(), 4 self.genreStream.asObservable() 5 )) { _, todoAndGenre -> [String: Any] in 6 let (todo, genre) = todoAndGenre 7 return [ "toDo": todo, "genre": genre, "hasDone": false ] 8 } 9 .scan([]) { todoList, dict in 10 return todoList + [dict] 11 }

これで、 ボタンがタップされるたびにTODOを配列に追加してその結果を流すストリーム の完成です。この方法なら、ObservableArray-RxSwiftを使う必要もありません。

あとはこれを外部に公開してVC側でsubscribeするなりしてみてください。

投稿2018/09/22 16:18

編集2018/09/23 07:04
kakajika

総合スコア3131

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oyasumi

2018/09/23 06:48

kakajikaさん丁寧な回答ありがとうございます!実装方針についてかなり参考になったのですが toDoListStreamを多次元配列もしくはArrayで持つとどうしてもエラーを吐かれてしまいビルドできません。どういう風にtoDoListStreamを置くことを想定されていたか教えていただけないでしょうか?よろしくお願いいたします。
kakajika

2018/09/23 07:06

toDoListStreamの型が何かということでしょうか? let toDoListStream: Observable<[[String: Any]]> です
oyasumi

2018/09/23 08:25

度々申し訳ないです。 Cannot assign to property: 'toDoListStream' is a 'let' constant Change 'let' to 'var' to make it mutableと出てしまいletで定義できなかったので一旦fixしました。立て続けにCannot assign value of type 'Observable<[String : Any?]>' to type 'Observable<[[String : Any]]>'というエラーになってしまったので todoAndGenre -> [String: Any?]でAnyがOptionalなのを変更してみました。 それとは別で init()のところでVariable 'self.toDoListStream' used before being initializedというエラーも起きました。 ボタンのタップを元にtoDoListStreamを生成するということはわかりやすかったのですが なぜvmをイニシャライズするところで怒られるのかが分かりませんでした。 分かりづらい文章ですみません。もう少々お付き合いください。。m(__)m
kakajika

2018/09/23 09:14

まだSwiftは書きなれてない感じですかね? letで宣言したプロパティは、initの中で初期化してあげないと怒られます。また、init後に書き換えることはできません。 一応、gistにコード例を置いておきました。ビルド・動作確認はしてません。 https://gist.github.com/kakajika/1cb14658237808a570bd01c6cd60150e [String: Any?] については、すみません。ちょっと前に回答を編集したのですが、編集前のコードだとOptionalになっていました。最新の回答をご覧ください。
oyasumi

2018/09/23 17:40

コードまでありがとうございます!描き慣れておらず基本的なところも抜け漏れていました。 おかげさまでかなり勉強になりました。丁寧にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問