【Flutter】ネイティブ初心者が、パッケージ+SwiftでTwitterへの投稿機能を実装してみた
FlutterでSNSへのシェア機能を作る場合、shareというパッケージを使うと簡単にできる。
ただ、一つ問題というか気になる点がある。
それは、iOSだと画像などの添付ができないこと。
iOS | Android |
---|---|
AndroidだとTwitterの画面そのものが開く感じで、画像等の添付ができる。
別に必須ではないが、iOSでテキストしかシェアできないのは微妙なので、なんとかしたかった。
どうやったか?
調べてみたところ、ネイティブのコードをFlutterから呼び出すことが可能とのこと。
参考記事↓
アプリ開発はFlutterが初めてで、swiftやkotlinは全く書けないが、さらっと読んだ感じ難しくはなさそうだったので、トライしてみることに。先述の通り、Androidはパッケージを使うだけでOKなので、iOS(swift)のみ処理を書いてみた。
ただ、いざ実装を進めてみると、参考記事はswiftではなくobjective-cで実装されていることに気づいた。
特に設定した覚えはないが、自分のアプリのios関連のファイルはswiftになっている。
これをobjective-cに変えて問題が起きても嫌だったので、swiftでの実装にトライすることに。
調べたところ、objective-c→swiftに変換してくれる神ツールがあったので、それを使ってみた。
ただこれだけでは不十分で、修正も必要だった。
ネイティブの経験が無いせいだろうが、ちょっと苦労したので、最終的なコードと簡単な解説をまとめておく。
swiftのコード
ios/Runner/AppDelegate.swift
を以下のように変更する。
おそらく丸々コピペで動くはず。
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;
let channel = FlutterMethodChannel(
name: "com.sample.app/share",
binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { [weak self] call, result in
if call.method == "tweet" {
let text = call.arguments as? String
self?.openActivity(text)
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func openActivity(_ text: String?) {
let encodedText = text?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if let encodedText = encodedText,
let url = URL(string: "https://twitter.com/intent/tweet?text=\(encodedText)") {
UIApplication.shared.open(url)
}
}
}
name: "com.sample.app/share"
の部分は、任意の名前でOK
「アプリのID/任意の名前」とするのが一般的ぽい。
if call.method == "tweet" {
let text = call.arguments as? String
self?.openActivity(text)
} else {
この部分で、Flutter側からtweet
が呼ばれたら、openActivity()
を実行するようにしている。
tweet
は任意の名前でOK、Flutter側の呼び出しは後ほど。
let 変数 = call.arguments as? 変数の型
とすることで、呼び出し側で渡した引数を取得できる。
引数がない場合は、この部分は不要。
openActivity()
の中身はというと
func openActivity(_ text: String?) {
let encodedText = text?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if let encodedText = encodedText,
let url = URL(string: "https://twitter.com/intent/tweet?text=\(encodedText)") {
UIApplication.shared.open(url)
}
}
引数でわたされたtext
を変換し、それを使ってTwitterのURLを開くイメージ。
この辺の仕様は知らなかったので、以下の記事を参考にした。
これでswift側の実装は完了。
ただ、このURLを開く機能はiOS10以降でないと対応していないため、ビルドエラーが出る場合は、対応バージョンの引き揚げが必要。
Flutter側の実装
先ほどネイティブ(swift)で書いた処理を、Flutter側で呼び出すための記述。
final channel = MethodChannel("com.sample.app/share");
// 実際の呼び出し
await channel.invokeMethod('tweet', text);
name: "com.sample.app/share"
で設定した値を使って、
final channel = MethodChannel("com.sample.app/share");
とする。
これにより、swift側のコードを呼び出す準備ができる。
あとは、
await channel.invokeMethod('tweet' ,text);
とすることで、引数text
を渡して、swift側の↓この部分が実行されるイメージ。
if call.method == "tweet" {
let text = call.arguments as? String
self?.openActivity(text)
} else {
今回は試していないが、いくつか処理を実装する場合はここの条件分岐を増やして、
呼び出し側でawait channel.invokeMethod('任意の名前');
とすればよさそう。
iOSの時のみswiftを呼び出し
ここまでで実装はほぼ完成。
ただ冒頭でも書いた通り、androidの場合はそのままパッケージの機能を使えばいいので、以下のようにOSによる分岐をはさむ。
スルーしていたが、channel.invokeMethod()
はFutureを返すので、async-await
や.then()
を使う必要がある。
import 'package:share/share.dart';
Future<void> share(String text) async{
final channel = MethodChannel("com.sample.app/share");
// iosならswiftから呼び出し
if (Platform.isIOS) {
await channel.invokeMethod('tweet', text);
} else {
await Share.share(text);
}
}
これはこれで、iOSの場合はTwitter以外にshareできないという問題点があるのだが、今回はTwitterに特化するかわりに、iOSでもテキスト以外も添付できるようにするという選択をした。
以上、ネイティブの経験がなくても情報が豊富なお陰でなんとかなった。
パッケージだけで済むのであれば、非常に簡単なのでそれがベストだが、この程度であればアレンジも難しくないので、参考までに。
その他参考記事
Discussion