「Viper」は複雑で、怖くて、まあ、変わっていますね。 VIPERの表面下には毒の気配があり、それは痛みを伴うことがあります。 しかし、時間が経つにつれてその痛みは治まり、コードを構造化するための治療法のひとつになります。 バイバイ、毒。 ハロー、ハピネス。
ここで、iOS開発者が共感できることを紹介します。
VIPER デザイン パターンは、多くの開発者にとって混乱の&源となっています。 このパターンの長所&短所と実用的な使い方は不明確である。 MVC、MVVM、MVVM + RxSwift パターンのアプリケーションに取り組んだ後、私はこの悪意のある響きのデザイン パターンを深く理解し、この話題 (ヒス?) が何であるかを確認したいと思ったのです。 その結果、次のような仮説を検証することにしました。
開発者として、私は再利用可能なモジュールを構築するために VIPER デザイン パターンを使用したいと思います。
VIPER
Mutual Mobile の数人は、iOS アプリのテスト カバレッジを向上させたいと考えていました。 その結果、彼らはこのパターン自体を思いつきました。 このパターンがどこから来たかについての詳細は、こちらをご覧ください。
文字通り言うと、Viper は View、Interactor、Presenter、Entity、Router の頭文字です。
理論的に言うと、Viper を使用しながら、アプリケーションまたはフレームワークは VIPER 「モジュール」(再利用できる & モジュールのコンポーネント)に分解されます。 図にすると、
View のようになります。 インターフェイス層はビューをレイアウトし、画像やラベルを更新し、ユーザー入力をプレゼンターに渡す
Interactor: インターフェイス。 ビジネス ロジック、データの操作、モデルの変換、API サービス & データ マネージャー クラスとのやり取りを行います。 出力をプレゼンター
Presenter に渡します。 インタラクターから受け取ったデータをビュー用にフォーマットし、ビューから入力を受け取り、インタラクターに渡す
Entity: VIPERモジュールが機能するために必要なものを表す単純なデータ
ルーター(Router)。 モジュールナビゲーションとアプリケーションナビゲーションを担当
VIPER in codeは、読んだ文字と同じ順序で動作するわけではありません。 言い換えれば…あなたのコード内のアクションは、以下の順序でよりそう流れます。
Interactor → Router , Entity, &Externals → Presenter → View
VIPER はより IREPV のようになる… それは誰か、どこかへの言葉です。
EXPERIMENT
我々は、再利用できるモジュールでVIPERを使う仮説を枠組みを作って検証しています。 このフレームワークは、ビデオ・コンテンツの再生に焦点を当てたものです。 ビデオの再生には、UIの更新、データのダウンロード&、データの同期が含まれる。 これらの相互作用により、ビデオ再生モジュールが VIPER 構造化モジュールの価値ある候補であることが証明されました。
OVERVIEW
VIPER モジュールはプロトコル指向です。 すべての機能、プロパティ、入力、出力はプロトコルを介して実装される。
その結果、私たちはaの考えを使用した。
Contract: VIPER モジュールで使用されるすべてのプロトコルを記述した swift ファイル。 これは objective-C のヘッダーファイルに似ています。 以下のコード スニペット:
//*** Interactor//*//*protocol VPKVideoPlaybackInteractorProtocol: class {
init(entity: VPKVideoType)}
protocol VPKVideoPlaybackInteractorInputProtocol: class {
var presenter: VPKVideoPlaybackInteractorOutputProtocol? { get set }var videoType: VPKVideoType { get set }var remoteImageURL.VPKVideoType: { get set }var remoteImageURL.VPKVideoType? URL? { get set }var localImageName: 文字列? { get set }
var playbackManager: (VPKVideoPlaybackManagerInputProtocol &VPKVideoPlaybackManagerOutputProtocol &VPKVideoPlaybackManagerProtocol)? { get set }
// PRESENTER -> INTERACTORfunc didTapVideo(videoURL: URL, at indexPath: NSIndexPath?)func didScrubTo(_ timeInSeconds: TimeInterval)func didSkipBack(_ seconds: Float)func didSkipForward(_ seconds: Float)func didToggleViewExpansion()func didMoveOffScreen()func didReuseInCell()}
私たちは、VIPER モジュール設定に、このフレームワーク開発者に容易な方法で対応してほしいとも思っています。
その結果、私たちは
Builder というアイデアを使用しました。 VIPER モジュールを構成し、依存関係を設定するために使用される単純なオブジェクト。 これは、プレゼンターとインタラクタのインスタンスを作成します。 最終的には、ビューコントローラにビューを返します。
public class VPKVideoPlaybackBuilder: VPKVideoPlaybackBuilderProtocol {
//Feedpublic static func vpk_buildViewInCell(for videoType:VPKVideoType, at indexPath: NSIndexPath, with playbackBarTheme:ToolBarTheme = .normal, completion viewCompletion:VideoViewClosure) {
let interactor = VPKVideoPlaybackInteractor(entity:videoType)let presenter: VPKVideoPlaybackPresenterProtocol &VPKVideoPlaybackInteractorOutputProtocol =VPKVideoPlaybackPresenter(with: false, showInCell: nil,playbackTheme: playbackBarTheme)viewCompletion(VPKDependencyManager.videoView(with:interactor, and: presenter))
}
また、モジュールのすべての依存関係を設定するプロトコルを使用しました:
class VPKDependencyManager: VPKDependencyManagerProtocol {
static func videoView(with interactor.VideoView:VideoViewClosed) {3531>
let interactor = VPKVideoPlaybackInteractor(entity:videoType)let presenter: VPKVideoPlaybackPresenterProtocol &VPKVideoPlaybackInteractorOutputProtocol =VPKVideoPlaybackPresenter(with: false, showInCell: nil,playbackTheme: playbackBarTheme)viewCompletion(VPKDependencyManager.videoView(with:interactor, and: presenter))
let videoView = VPKVideoView(frame: .zero)let playbackBarView: VPKPlaybackControlViewProtocol =VPKPlaybackControlView(theme: presenter.playbackTheme ?? .normal)let videoPlaybackManager: VPKVideoPlaybackManagerInputProtocol &VPKVideoPlaybackManagerOutputProtocol &VPKVideoPlaybackManagerProtocol = VPKVideoPlaybackManager.shared//Dependency setuppresenter.interactor = interactorinteractor.presenter = presentervideoView.presenter = presentervideoView.playbackBarView = playbackBarViewpresenter.playbackBarView = playbackBarViewplaybackBarView.presenter = presenterpresenter.videoView = videoViewinteractor.playbackManager = videoPlaybackManagerreturn videoView
}
The FRAMEWORK.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX.DX: VIDEOPLAYBACKKIT
Let’s consider the user story.VIDEOPLAYBACKKITは、VIDEOPLAYBACKKITのユーザー ストーリーです。
**PLAY A VIDEO **
Using the AVFOUNDATION we know the following:
- Must use AVPlayer
- AVPlayer will give you an AVPlayerLayer to add on your view
- AVPlayer takes time to prepare – – (AVFOUNDATIONの準備に時間がかかる) 特にリモートビデオURL
- Remote video data needs time to download
-
Two primary view actions
- Play
- Pause
-
Result of these actions。
- AVPlayer はデータをダウンロードするためのビデオURLを受け取ります
- 準備ができたら、AVPlayer は AVPlayerLayer
- AVPlayerLayer をビューに追加しなければなりません
- 一時停止したら、AVPlayer は再生を停止します
**IREPV (AKA VIPER) & AVKit.NET、AVKit.NET、AVKit.NET、AKA VIPER、AVKit.NET、AVKit.NET、AVKit.NET、AVKit.NET、AVKit.NET、AVKit.NET AVFoundation **
INPUTS AND OUTPUTS
ここで、パターンの理解、脳の沈静化、ラビットホールの複雑さに陥るのを避けるためのキーポイントを説明します。
Major Lesson 1: モジュールの入力と出力を理解する。
View
- Input: didTapPlay
- Output: didTapPlay
- Output: Presenter didTapPlay
Presenter
- Input: didTapPlay は view から受け取る
- Output: onPlayerLayerSuccess(playerLayer) – これを presenter
Interactor
- Input: DidTapPlay は view に出力する。 didTapPlay – 外部マネージャに伝える
-
Output: onVideoPlayerLayerSuccess – プレーヤー層をプレゼンターに渡す
protocol VPKVideoPlaybackInteractorOutputProtocol: class {
var progressTime.Output: VPKVideoPlayerLayerSuccess: プレイヤー層をプレゼンターに渡す。 TimeInterval { get set }
//INTERACTOR –> PRESENTERfunc onVideoResetPresentation()func onVideoLoadSuccess(_ playerLayer: AVPlayerLayer)func onVideoLoadFail(_ error: String)func onVideoDidStartPlayingWith(_ duration.XXX) –>
【PROFILE】onVideoLoadPresentation()func onVideoDidStartPlaying(_ error: XXX) TimeInterval)func onVideoDidStopPlaying()func onVideoDidPlayToEnd()func onVideoPlayingFor(_ seconds: TimeInterval)}
プレゼンターからAVPlayerLayerを受け取ると、ビューのインターフェースをリロードできるようになりました。
protocol VPKVideoViewProtocol: class {func reloadInterface(with playerLayer: AVPlayerLayer)}
INTERACTOR AND PRESENTER RELATIONSHIP
VIPERの部品間の関係は、他のiOSパターンとは異なっています。 各オブジェクトは互いに(もちろん弱く)しがみついています。 これは、あるオブジェクトが他のオブジェクトのデリゲートになっていると考えることができます。 以下の例を見てほしい。
-
プレゼンターはインタラクターへの弱い参照を保持しています。 プレゼンターはインタラクタの入力プロトコルを気にするだけである。 didTapPlay」入力の転送。
protocol VPKVideoPlaybackPresenterProtocol: class {var interactor: VPKVideoPlaybackInteractorInputProtocol: クラス {var interactor: VPKVideoPlaybackInteractorInputProtocol? { get set }//weak}
-
Presenterは入力メッセージを転送しますが、Presenterはビデオエンティティデータに関するインタラクタの知識にも依存します。
public class VPKVideoPlaybackPresenter:VPKVideoPlaybackPresenterProtocol {func didTapVideoView() {guard let safeVideoUrl = interactor?.videoType.videoUrl else {return }interactor?.didTapVideo(videoURL: safeVideoUrl, at:self.indexPath)}}
PROTOCOL CONFORMANCE
Presenter and Interactor relationship is the most important part of the data flow.
The Presenter cares about the Interactors Output
The Interactor cares about the Presenters Input
See below.を参照してください。
プレゼンターはインタラクタの出力プロトコルに従います。
//MARK: INTERACTOR --> Presenterextension VPKVideoPlaybackPresenter:VPKVideoPlaybackInteractorOutputProtocol { func onVideoLoadSuccess(_ playerLayer: AVPlayerLayer) {videoView?.reloadInterface(with: playerLayer)
}
インタラクタは出力データを返すためにプレゼンターの弱いプロパティを使います。
public class VPKVideoPlaybackInteractor:VPKVideoPlaybackInteractorProtocol {
weak var Presenter: VPKVideoPlaybackInteractorOutputProtocol?
func playbackManager(_: VPKVideoPlaybackManager, didPrepareplayerLayer: AVPlayerLayer) {presenter?.onVideoLoadSuccess(playerLayer)
}}
THE PROS
THE CONS
RECOMMENDATION
こんなときはVIPERの使用をお勧めします。
- チームが新しいパターンを学ぶことに前向きである
- チームがそれを学び、理解し、実験するのに少なくとも 1 週間ある
- そのパターンの非連結性によりテスト駆動アプローチをサポートしやすい
- それについて興奮している 🙂
- チームメンバーが異なるモジュールに責任を持つ
以下の場合、VIPER のフォームを使用することはお勧めしません。
- チームが反応的で、機能がほぼ毎日変わる場合
- 実験的で問題をテストするための小さなアプリケーションを作成している場合
- テストに関心がない場合
VIPER は長期プロジェクト、セキュリティとビジネスロジックが高いアプリケーション、テストを実装しなければならない場合などで輝きを放つでしょう。
あなたのコードがよりきれいになることに気がつくでしょう。 チームメンバー間で機能モジュール & を簡単に分けることができるようになります。 UI フォーマットのバグがある場合、Presenter クラスを確認することが即座にわかります。 論理的なバグがあるとき、それがインタラクタのせいであることがわかります。 3531>
ADDITIONAL INFO
このトピックは、TRY! Swift NYC 2017 で発表しました。 発表内容はこちらでご覧いただけます。
すべての追加のtry! Swift の講演は、こちらでご覧いただけます。