【Unity】SocialWorkerをObservableに使うラッパークラスを書いた【UniRx】

# はじめに
[【Unity】SocialWorkerでLINEにテキスト投稿ができない問題の対応【SocialWorker】](http://qiita.com/su10/items/0ced9bf8885438e71fb7) の記事でも紹介してますが、[SocialWorker](http://okamura0510.jp/%E9%96%8B%E7%99%BA%E3%83%96%E3%83%AD%E3%82%B0/socialworker/) はUnityからSNSに手軽にシェアを行うことができる素晴らしいアセットです。

ただ、[UniRx](https://github.com/neuecc/UniRx) 使いとしてコールバックを受け取るようなメソッドは使いたくなかったのでRxの世界で使いやすいように`IObservable<Unit>`を返すラッパークラスを書いてみました。

# ソースコード
LINEへの投稿については [【Unity】SocialWorkerでLINEにテキスト投稿ができない問題の対応【SocialWorker】](http://qiita.com/su10/items/0ced9bf8885438e71fb7) の記事で解説している問題への対応を含めています。

## <font color="red">2017/7/6 追記</font>
実装が色々まずかったので修正しました。

- 各メソッド名の最後に"Async"をつけた
- `Subscribe()`されないと実行されないようにした
- マルチスレッド対応（できてるか不安...）
- 専用のエラークラス実装

```ObservableSocialWorker.cs
using SWorker;
using UniRx;

namespace Jagapippi.Social
{
    public static class ObservableSocialWorker
    {
        public static IObservable<Unit> PostTwitterAsync(string message, string url, string imagePath)
        {
            return Observable.Create<Unit>(observer =>
            {
                var gate = new object();
                var isDisposed = false;
                SocialWorker.PostTwitter(message, url, imagePath, result =>
                {
                    lock (gate)
                    {
                        if (isDisposed) return;
                        if (result == SocialWorkerResult.Success)
                        {
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(new SocialWorkerException(result));
                        }
                    }
                });
                return Disposable.Create(() =>
                {
                    lock (gate)
                    {
                        isDisposed = true;
                    }
                });
            });
        }

        public static IObservable<Unit> PostFacebookAsync(string imagePath)
        {
            return Observable.Create<Unit>(observer =>
            {
                var gate = new object();
                var isDisposed = false;
                SocialWorker.PostFacebook(imagePath, result =>
                {
                    lock (gate)
                    {
                        if (isDisposed) return;
                        if (result == SocialWorkerResult.Success)
                        {
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(new SocialWorkerException(result));
                        }
                    }
                });
                return Disposable.Create(() =>
                {
                    lock (gate)
                    {
                        isDisposed = true;
                    }
                });
            });
        }

        public static IObservable<Unit> PostLineAsync(string message, string imagePath)
        {
            return Observable.Create<Unit>(observer =>
            {
                var gate = new object();
                var isDisposed = false;
                SocialWorker.PostLine(System.Uri.EscapeUriString(message), imagePath, result =>
                {
                    lock (gate)
                    {
                        if (isDisposed) return;
                        if (result == SocialWorkerResult.Success)
                        {
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(new SocialWorkerException(result));
                        }
                    }
                });
                return Disposable.Create(() =>
                {
                    lock (gate)
                    {
                        isDisposed = true;
                    }
                });
            });
        }

        public static IObservable<Unit> PostInstagramAsync(string imagePath)
        {
            return Observable.Create<Unit>(observer =>
            {
                var gate = new object();
                var isDisposed = false;
                SocialWorker.PostInstagram(imagePath, result =>
                {
                    lock (gate)
                    {
                        if (isDisposed) return;
                        if (result == SocialWorkerResult.Success)
                        {
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(new SocialWorkerException(result));
                        }
                    }
                });
                return Disposable.Create(() =>
                {
                    lock (gate)
                    {
                        isDisposed = true;
                    }
                });
            });
        }

        public static IObservable<Unit> PostMailAsync(
            string[] to,
            string[] cc,
            string[] bcc,
            string subject,
            string message,
            string imagePath)
        {
            return Observable.Create<Unit>(observer =>
            {
                var gate = new object();
                var isDisposed = false;
                SocialWorker.PostMail(to, cc, bcc, subject, message, imagePath, result =>
                {
                    lock (gate)
                    {
                        if (isDisposed) return;
                        if (result == SocialWorkerResult.Success)
                        {
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(new SocialWorkerException(result));
                        }
                    }
                });
                return Disposable.Create(() =>
                {
                    lock (gate)
                    {
                        isDisposed = true;
                    }
                });
            });
        }

        public static IObservable<Unit> CreateChooserAsync(string message, string imagePath)
        {
            return Observable.Create<Unit>(observer =>
            {
                var gate = new object();
                var isDisposed = false;
                SocialWorker.CreateChooser(message, imagePath, result =>
                {
                    lock (gate)
                    {
                        if (isDisposed) return;
                        if (result == SocialWorkerResult.Success)
                        {
                            observer.OnNext(Unit.Default);
                            observer.OnCompleted();
                        }
                        else
                        {
                            observer.OnError(new SocialWorkerException(result));
                        }
                    }
                });
                return Disposable.Create(() =>
                {
                    lock (gate)
                    {
                        isDisposed = true;
                    }
                });
            });
        }
    }

    public class SocialWorkerException : System.Exception
    {
        private readonly SocialWorkerResult _result;

        public SocialWorkerResult error
        {
            get { return _result; }
        }

        public SocialWorkerException(SocialWorkerResult result)
        {
            _result = result;
        }
    }
}
```

# 雑感
だいぶRx使いこなせるようになったけど

- メソッド名に"Async"とか"AsObservable"とつけるべきか

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">単発でOnCompletedも一緒にでる奴はAsync、ストリーム長が複数～無限な奴はAsObservableにするといいです</p>&mdash; とりすーぷ (@toRisouP) <a href="https://twitter.com/toRisouP/status/882927803183661056">2017年7月6日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

@toRisouP さんに教えていただきました！ありがとうございます！
<font color="red">↑ 2017/7/6 追記</font>

- こういうラッパーは`Subscribe()`されない限り処理を行わないべきか
  - ↑のコードはメソッド呼び出し時に`SocialWorker`のメソッドを呼ぶようになってる
  - <font color="red">↑他のストリームと組み合わせたときに待たないと困る（2017/7/6 追記）</font>

<del>がよくわかりません。主に慣習的な部分？</del>

<del>「一般的にはこうです」というのを知ってる方がいたら是非教えてください。</del>