Unity
UniRx
SocialWorker

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

はじめに

【Unity】SocialWorkerでLINEにテキスト投稿ができない問題の対応【SocialWorker】 の記事でも紹介してますが、SocialWorker はUnityからSNSに手軽にシェアを行うことができる素晴らしいアセットです。

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

ソースコード

LINEへの投稿については 【Unity】SocialWorkerでLINEにテキスト投稿ができない問題の対応【SocialWorker】 の記事で解説している問題への対応を含めています。

2017/7/6 追記

実装が色々まずかったので修正しました。

  • 各メソッド名の最後に"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"とつけるべきか

@toRisouP さんに教えていただきました!ありがとうございます!
↑ 2017/7/6 追記

  • こういうラッパーはSubscribe()されない限り処理を行わないべきか
    • ↑のコードはメソッド呼び出し時にSocialWorkerのメソッドを呼ぶようになってる
    • ↑他のストリームと組み合わせたときに待たないと困る(2017/7/6 追記)
がよくわかりません。主に慣習的な部分? 「一般的にはこうです」というのを知ってる方がいたら是非教えてください。