50

Click here to load reader

iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

  • Upload
    irgaly

  • View
    1.383

  • Download
    14

Embed Size (px)

DESCRIPTION

2014/11/22 第2回 Japan Xamarin User Group Conference 東日本編 で発表したスライドです。 https://atnd.org/events/57246

Citation preview

Page 1: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

iOS の動画アプリ開発に Xamarin を使ってみた

@irgaly

Japan Xamarin User GroupJXUG Conference (East) #2

2014/11/22@日本マイクロソフト 品川本社 セミナールームA

Page 2: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

目次• 自己紹介 • 実績 • アプリ説明/設計 • Xamarin.iOSのメリット/デメリット • メモリ管理の罠

• 原因と対策 • メディアの扱い • 画面回転 • Background Fetch • Jenkinsビルド • まとめ

Page 3: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

自己紹介• 北川大智 / @irgaly • フェンリル株式会社

• 共同開発部 • Android / iOS エンジニア • Android Lollipop, Material Design に興味あり • いずれ Dart の時代が来ると思っている • vim / arch linux

とりあえず新しいものに食い付いておくタイプ

Page 4: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

実績

Page 5: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

過去の実績• NHK 紅白歌合戦アプリ(2013)

ViewModel 実装の担当 • Xamarin/C# エンジニアを名乗れるようになった

• Objective-C からの解脱

NHK紅白アプリ

Page 6: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

業務に使ってみて

Page 7: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

今回のアプリ

• マルチメディア系 iPhone アプリ • iOS 7.0 以上

• Android は開発対象外 • Android 版は先行開発済み • Android 版の移植開発

• 機能 • ソーシャルログイン

• Twitter, Facebook • 動画撮影

• ショートムービー、連続撮影、Landscape, Portrait • 動画ダウンロード・再生

• 動画の連続再生

Page 8: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

設計

• MVVMCross で MVVM • いつでもクロスプラットフォーム展開できるように • Xamarin を選ぶ理由

• Model, API などは Service で制御。それ以外は ViewModel + View • Service または MVVMCross Plugin

• Facobook iOS SDK (Xamarin Components) • Xamarin.Social (Twitter ログイン) (Xamarin Components) • 効果測定ネイティブ SDK バイディング • 録画機能もプラグイン化 (クロスプラットフォーム対応に向けて)

• その他 • Mac/Xamarin Studio • Xib/AutoLayout/AssetCatalog...

Page 9: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Xamarin.iOS を使って・メリット• モバイルアプリ開発に MVVM を導入できる • C# の恩恵

• LINQ の威力 • Objective-C の闇にはまらない • Attribute, Reflection...

• 必要な機能はひととおり NuGet/Xamarin Component で調達 できる

• OS のすべての機能にアクセスでき、ネイティブ Binding も作れる

• 業務利用にも耐えられる • C# ノウハウでの知識集約

• クロスプラットフォーム展開 • OS やプロジェクトを越えてのプラグイン利用

• MVVMCross の強力な武器を使える • Service (DI), Messenger, Plugin...

※ iOS/Objective-C 開発と比較した場合

Page 10: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Xamarin.iOS を使って・デメリット• MVVM のオーバーヘッド

• クラス数/インスタンス数が多く、画面生成のパフォーマンスに難儀

• iOSはもともとMVVM設計ではない → 内部的にリフレクションを多用することのオーバーヘッド

• iOS でのメモリリーク問題 • どう頑張ってもメモリリークを駆逐できない • GC の問題

• 基本的な対策はとりつつ、今後のXamarin社の対応に期待

• エンジニアの問題 • チームメンバーが多いと、どうしてもメモリリーク対応漏れが発生する

※ iOS/Objective-C 開発と比較した場合

Page 11: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

現実• 開発スピードが上がったり、コストが削減されるようなものではない。

• MVVMの採用で品質は向上する • もちろんクロスプラットフォーム対象に開発すれば総合で見た場合のスピード・コスト・品質に変化あり

• エンジニアとしてはモダンな開発環境で揃えられるので心安らぐ

• 構成切り替え、Jenkins ビルド、Reveal、Crashlytics などに対応しており、不便になることはない

• Xamarin Profilerなど、今後もツールは充実する方向にある

• C#を書けるスマホアプリエンジニアの確保が難しい

※ iOS/Objective-C 開発と比較した場合

Page 12: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

メモリ管理の罠

Page 13: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

メモリリークに気付くある日

(絶望)※当時はXcode Instrumentsでした

Page 14: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

調べる• c# - Xamarin iOS memory leaks everywhere - Stack Overflow

• http://stackoverflow.com/questions/25532870/xamarin-ios-memory-leaks-everywhere • Best way to recursively dispose UIViews in Xamarin iOS

• http://education-eco.blogspot.jp/2014/08/best-way-to-recursively-dispose-uiviews.html

• Memory and Performance Best Practices | Xamarin • http://developer.xamarin.com/guides/cross-platform/

deployment,_testing,_and_metrics/memory_perf_best_practices/ • Advanced Memory Management on iOS and Android - Mark Probst

and Rodri… • http://www.slideshare.net/Xamarin/advanced-memory-management-on-ios-and-

android-mark-probst-and-rodrigo-kumpera • Memory Management Pitfalls in Xamarin iOS - Introduction

• http://bluetubeinc.com/blog/2013/6/memory-management-pitfalls-in-xamarin-ios-introduction

奥が深そう

Page 15: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

原因は?

• 二つのGCが同時に機能している • Xamarin.iOS の GC と iOS の GC の相性が悪い

• 参照カウンタ方式の闇 • UIView でだいたいリークする

Page 16: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

原因と発生パターン• ネイティブオブジェクト間での循環参照 • サブクラス化と EventHandler • アンマネージリソースを Dispose していない

Page 17: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

UIView の循環参照

• NSObject を継承したクラスを作ると発生

Page 18: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

対策

1 public class MyView : UIView { 2 WeakReference<UIViewController> _parent; 3 public MyView (UIViewController parent) { 4 _parent = new WeakReference<UIViewController>(parent); 5 } 6 }

• WeakReference を使う • Objective-C ではおなじみの習慣

• C#で書いていると忘れがちになるが、同じように対応する

Page 19: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

サブクラス化とEventHandler

1 public class CustomButton : UIButton {} 2 3 public class MyViewController : UIViewController { 4 public override void ViewDidLoad () { 5 var button = new CustomButton(); 6 View.Add(button); 7 button.TouchUpInside += (s, e) => { 8 ButtonClick(); 9 };10 }11 }

• EventHandler で循環参照発生

Page 20: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

対策 1 public override void ViewWillAppear(bool animated) 2 3 { 4 UIButton.TouchUpInside += ButtonClick; 5 } 6 7 public override void ViewWillDisappear(bool animated) 8 { 9 UIButton.TouchUpInside -= ButtonClick;10 }

• EventHandler の削除

Page 21: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

アンマネージリソースの Dispose 漏れ

1 public override void ViewDidLoad () { 2 var imageView = ...; 3 var image = UIImage.FromBundle ("hoge.png"); 4 imageView.Image = image; 5 6 _otherButton.TouchUpInside += (s, e) => { 7 imageView.RemoveFromSuperview();// UIImageを解放できていない 8 } 9 }

Page 22: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

対策 1 public override void ViewDidLoad () { 2 var imageView = ...; 3 var image = UIImage.FromBundle ("hoge.png"); 4 imageView.Image = image; 5 6 _otherButton.TouchUpInside += (s, e) => { 7 imageView.RemoveFromSuperview(); 8 imageView.Dispose(); 9 image.Dispose(); // すぐに解放する10 }11 }

Page 23: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

対策まとめ• 親の参照には WeakReference を使う • 不要になったら EventHandler の削除 • 怪しかったら Dispose する

• → むしろ Touch プロジェクト(Xamarin.iOS) 側のコードビハインドをできる限り減らす

• Core プロジェクト(PCL) プロジェクトのコードはこれらの危険が少ない

Page 24: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

メディアの扱い

Page 25: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

メディアの扱い• 動画撮影

• プラグインを独自実装 • PlatformService

• プラットフォーム固有の機能について • 機能紹介

DIを使うことで、Core プロジェクトから プラットフォームの機能を扱えるようにする。

Page 26: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

独自のMVVMCross Plugin

• PCLとプラットフォームとでプロジェクトを分ける

Page 27: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

IMvxVideoCapture 1 public interface IMvxVideoCapture 2 { 3 Task<bool> SetupCaptureSessionAsync(); 4 bool StartRecording(string filePath); 5 Task<bool> StopRecording(); 6 bool SwitchCamera(); 7 bool UpdateOrientation(VideoOrientation orientation); 8 } 9 10 public interface IMvxVideoCapture<TPreviewView> : IMvxVideoCapture11 {12 bool StartPreview(IMvxVideoDisplay<TPreviewView> display);13 bool StopPreview();14 }

• プラットフォームに依存しないインタフェースを定義する

Page 28: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

MvxVideoCapture

1 var session = new AVCaptureSession 2 { 3 SessionPreset = AVCaptureSession.PresetHigh 4 }; 5 var camera = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video); 6 var mic = AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Audio); 7 var cameraInput = AVCaptureDeviceInput.FromDevice(camera); 8 session.AddInput(cameraInput); 9 var micInput = AVCaptureDeviceInput.FromDevice(mic);10 session.AddInput(micInput);

• Touch プロジェクト側で実装を書く • iOS AVFoundation クラスを C# からそのまま扱える

初期化からプレビュー開始まで(part1)

Page 29: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

MvxVideoCapture

11 var queue = new DispatchQueue("CaptureBuffer");12 var videoOutput = new AVCaptureVideoDataOutput();13 session.AddOutput(videoOutput);14 videoOutput.SetSampleBufferDelegate(this, queue);15 var connection = videoOutput.ConnectionFromMediaType(AVMediaType.Video);16 connection.VideoOrientation = GetCaptureOrientation(_currentOrientation);17 var audioOutput = new AVCaptureAudioDataOutput();18 session.AddOutput(audioOutput);19 audioOutput.SetSampleBufferDelegateQueue(this, queue);20 var previewLayer = new AVCaptureVideoPreviewLayer(session);21 session.StartRunning();

• Touch プロジェクト側で実装を書く • iOS AVFoundation クラスを C# からそのまま扱える

初期化からプレビュー開始まで(part2)

Page 30: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

MvxVideoCapture 1 var videoSettings = new NSMutableDictionary(); 2 videoSettings.Add(AVVideo.CodecKey, AVVideo.CodecH264); 3 videoSettings.Add(AVVideo.WidthKey, new NSNumber(640)); 4 videoSettings.Add(AVVideo.HeightKey, new NSNumber(480)); 5 var compressionSettings = new NSMutableDictionary(); 6 compressionSettings.Add(AVVideo.AverageBitRateKey, new NSNumber(2413000)); 7 videoSettings.Add(AVVideo.CompressionPropertiesKey, compressionSettings); 8 videoSettings.Add(AVVideo.ScalingModeKey, AVVideoScalingModeKey. 9 ResizeAspectFill);10 11 var videoInput = new AVAssetWriterInput(AVMediaType.Video, new 12 AVVideoSettingsCompressed(videoSettings));13 videoInput.ExpectsMediaDataInRealTime = true;14 writer.AddInput(videoInput);

• AVAssetWriterInput の引数を作成するために、NSMutableDictionary を C# から扱っています。

コーデックの設定から撮影開始部分(part1)

Page 31: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

MvxVideoCapture

15 16 var audioSettings = new NSMutableDictionary();17 audioSettings.Add(AVAudioSettings.AVFormatIDKey, NSNumber.FromInt32((int)18 AudioFormatType.MPEG4AAC));19 audioSettings.Add(AVAudioSettings.AVNumberOfChannelsKey, NSNumber.FromInt16(2))20 ;21 audioSettings.Add(AVAudioSettings.AVSampleRateKey, NSNumber.FromFloat(44100.0f)22 );23 var audioInput = new AVAssetWriterInput(AVMediaType.Audio, new AudioSettings(24 audioSettings));25 writer.AddInput(audioInput);

• AVAssetWriterInput の引数を作成するために、NSMutableDictionary を C# から扱っています。

コーデックの設定から撮影開始部分(part2)

Page 32: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Core プロジェクトから MvxVideoCapture を使う

1 ... 2 var videoCapture = Mvx.Resolve<IMvxVideoCapture>(); 3 videoCapture.StartRecording(filePath); 4 5 ... 6 7 await videoCapture.StopRecording();

• MVVMCross プラグインとしてインタフェースとプラットフォーム固有の実装を分けたため、ViewModel 側の PCL プロジェクトへこれを記述できる

Page 33: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

PlatformService 1 public class TouchPlatformService : IPlatformService 2 { 3 public IMediaFile GetMediaFile(string path) 4 { 5 return new TouchMediaFile(path); 6 } 7 8 public async Task Share(string shareText) 9 {10 var items = new NSObject[] {11 new NSString(shareText)12 };13 var activity = new UIActivityViewController(items, null);14 var vc = ...;15 ...16 await vc.PresentViewControllerAsync(activity, true);17 }18 19 public string VersionName()20 {21 return NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleVersion").22 ToString();23 }24 25 ...

• プラットフォーム固有の機能を呼び出すユーティリティサービス

Page 34: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

画面回転

Page 35: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

画面回転

• 現在の iPhone の傾きや、傾きの変化を取得する • 動画撮影の縦、横切り替えに使用 • BeginGeneratingDeviceOrientationNotifications で判定する

• 画面ロック時には回転通知は発生しない

Page 36: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

画面回転 1 public override void ViewWillAppear(bool animated) 2 { 3 base.ViewWillAppear(animated); 4 5 UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications(); 6 var notificationAvailable = false; 7 _orientationObserverToken = NSNotificationCenter.DefaultCenter.AddObserver( 8 UIDevice.OrientationDidChangeNotification, notification => 9 {10 notificationAvailable = true; // 傾き取得成功11 ... // 傾きに応じた処理12 });13 14 Task.Run(async () =>15 {16 await Task.Delay(500);17 if(!notificationAvailable) {18 … // 傾きロックされている可能性がある。Portraitであるとみなして処理19 }20 }).Forget();21 }

Page 37: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

画面回転

1 public override void ViewWillDisappear(bool animated) 2 3 { 4 base.ViewWillDisappear(animated); 5 UIDevice.CurrentDevice.EndGeneratingDeviceOrientationNotifications(); 6 if (_orientationObserverToken != null) 7 { 8 NSNotificationCenter.DefaultCenter.RemoveObserver(_orientationObserverT 9 oken);10 _orientationObserverToken = null;11 }12 }

Page 38: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Background Fetch

Page 39: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Background Fetch

• iOS7 以上で、バックグラウンドのアプリが一定期間ごとに起動し、なんらかの処理を実行できる機能 • 起動の間隔はOSが決定するため、指定することはできない • 30秒間のみ処理可能であり、できることは限られてくる • 主にAPIとの通信で、データ更新をチェックするための機能 • 30秒を過ぎるとプロセスが殺される

Page 40: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

プロジェクトの設定

• iOSのプロジェクト設定から、Background fetchオプションを設定する

Page 41: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Background Fetch 1 public override bool FinishedLaunching(UIApplication app, NSDictionary options) 2 3 { 4 ... 5 6 // BackgrondFetchの呼び出し間隔指定 7 UIApplication.SharedApplication 8 .SetMinimumBackgroundFetchInterval( 9 UIApplication.BackgroundFetchIntervalMinimum);10 11 return true;12 }

• AppDelegateで、起動時に Background Fetch の間隔を設定

Page 42: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

PerformFetchの実装 1 public override async void PerformFetch(UIApplication application, Action< 2 UIBackgroundFetchResult> 3 completionHandler) 4 5 { 6 var cancelToken = new CancellationTokenSource(); 7 try 8 { 9 // 25秒で強制中断10 cancelToken.CancelAfter(25000);11 12 // API通信などを実行13 … completionHandler(UIBackgroundFetchResult.NewData);14 }15 catch (OperationCanceledException)16 {17 completionHandler(UIBackgroundFetchResult.Failed);18 }19 }

• 保険として25秒制限をしているが、安全に対応するならBackground Transfer (NSURLSession Background mode)で対応する

• Xamarin はアップデートが早いため、OSの最新機能もすぐ使えるようになる • マイナーな機能は SDK にバグがあることもある

Page 43: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Jenkins ビルド

Page 44: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Jenkins ビルド

• 弊社ではすべてのプロダクトは Jenkins でビルドしています • 納品時のトラブルを防ぐ • エンジニア不在の時でも最新のアプリの動作確認を行える

• Xamarin.iOS x Jenkins は公式ガイドあり • http://developer.xamarin.com/guides/cross-platform/ci/jenkins_walkthrough/

• Jenkins は Mac 上で実行されている • Jenkins ビルドでの構成(定数)切り替えビルド対応

Page 45: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

プロジェクト側の準備

• mdtool コマンドは定数切り替えに対応していない • 定数切り替え対応のため、Touchプロジェクトにシンボルを定義しておく

Page 46: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

Jenkins 環境の準備

• Xamarin Studio をインストールする • ライセンス認証する • Stable の最新バージョンにアップデートしておく

• 開発チームとJenkinsで、「常にXamarin StudioのStable最新環境で作業する」という取り決めをしておく

• Xcode も一度は起動して使える状態にしておく(念のため) • プロビジョニングプロファイルと署名をインポートしておく

Jenkins 環境で作業します

Page 47: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

ビルド構成を作成• フリースタイルビルド構成

• ビルド環境 > ビルドプロセスに環境変数をインジェクト (Environment Injector Plugin)

Page 48: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

ビルド構成を作成• ビルド > シェルの実行 > シェルスクリプト

1 sed -i '' -Ee "s/__JENKINS__;/${Options};/g" MyProject.*/MyProject.*.csproj 2 4 /Applications/Xamarin\ Studio.app/Contents/MacOS/mdtool -v build -t:Build -c:$(BuildConfiguration}\|iPhone -p:MyProject.Touch MyProject/MyProject.sln 5 6 find MyProject/MyProject.Touch/bin/${BuildConfiguration} -name '*.ipa' -exec 7 mv {} MyProject/MyProject.Touch/bin/${ 8 BuildConfiguration}/MyProject-${ 9 BuildConfiguration}-${BUILD_ID}.ipa \;

Page 49: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

まとめ

Page 50: iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East

まとめ

• iOS寄りの各機能の紹介をコードレベルで紹介しました • PCLとプラットフォームのプロジェクトを分けて実装する

• パッケージマネージャなどの機能は揃っており、Xamarin は開発プラットフォームとしては強力

• プラットフォーム固有の機能へのアクセスもできるため、業務に耐えうる

• プラットフォームごとの対応の手間はかかる、罠も多い