酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

SwiftUI で検索モードを解除する。dismissSearch() は searchableモディファイアを設定したViewの 子View でないと使えない

本文はあとで書く。

developer.apple.com

dismissSearch() を実行すれば、検索モードがオフになり、検索バーに入力していたテキストもキーボードも消える。

  • Sets isSearching to false.
  • Clears any text from the search field.
  • Removes focus from the search field.

当初は、以下のように実装した。しかし dismissSearch() を実行してもうまく動かなかった。

struct ParentView: View {
    @State private var searchText = ""

    @Environment(\.dismissSearch) private var dismissSearch
    
    var body: some View {
        VStack {
            Text(searchText)
            Button(action: { dismissSearch() } ) {
                Text("Dissmiss search")
            }
        }
            .searchable(text: $searchText)
    }
}

dismissSearch() は searchable モディファイアを設定した、子View でないと有効ではなかった。

struct ParentView: View {
    @State private var searchText = ""

    var body: some View {
        ChildView(searchText: searchText)
            .searchable(text: $searchText)
    }
}

struct ChildView: View {
    @Environment(\.dismissSearch) private var dismissSearch

    let searchText: String

    var body: some View {
        Text(searchText)
        Button(action: { dismissSearch() } ) {
            Text("Dissmiss search")
        }
    }
}

Misskey .py を使って画像を添付したステータスを misskey.io へ投稿する

Python を使って、画像を添付したステータス「Hello, Misskey!」を misskey.io へ投稿する方法を紹介する。

Mastodon.py のインストール

Python で Misskey にメッセージを投稿するには Misskey.py というパッケージを使用する。Python から Misskey API に簡単にアクセスするためのツールである。

github.com

このパッケージは pip を使ってインストールできる。

pip install Misskey.py

Misskey.py を使ってステータスを misskey.io へ投稿する

この例では api_base_urlmisskey.io を指定しているが、皆さんが利用しているサーバーのAPIベースURLに変更して欲しい。

import os
import sys
import time
from misskey import Misskey

misskey_access_token = 'アクセストークン'
misskey_api_base_url = 'misskey.io'

misskey = Misskey(
    misskey_api_base_url,
    i = misskey_access_token
)

# 引数から画像を含むディレクトリのパスを取得する
image_dir_path = sys.argv[1]

# 画像をアップロードする
misskey_media_ids = []
for filename in sorted(os.listdir(image_dir_path)):
    if filename.endswith('.png') or filename.endswith('.jpg'):
        file_path = os.path.join(image_dir_path, filename)
        with open(file_path, "rb") as f:
            media = misskey.drive_files_create(f)
            media_id = media['id']
            print(f"アップロードされたファイル: {file_path}, メディアID: {media_id}")
            misskey_media_ids.append(media_id)

#print(f"{misskey_media_ids}")

# 画像のアップロード後、すぐにステータスを投稿しようとするとステータスコード 422 が返ってくるので10秒待機
time.sleep(10)

# 投稿を作成する
status = 'Hello, Misskey!'
misskey.notes_create(status, file_ids=misskey_media_ids, visibility='public')

ターミナルで、アップロードしたい画像ファイルが入っているディレクトリパスを指定して実行しよう。

python post_media_misskey.py /Users/ch3cooh/Desktop/images/20230412

上記のスクリプトを実行した。成功すると下図のようにノートを投稿できているはずだ。

関連記事

atproto を使って画像を添付したステータスを Bluesky へ投稿する

Python を使って、画像を添付したステータス「Hello, Bluesky!」を Bluesky へ投稿する方法を紹介する。

atproto のインストール

Python で Bluesky にメッセージを投稿するには atproto というパッケージを使用する。Python から Bluesky (AT Protocol) に簡単にアクセスするためのツールである。

github.com

このパッケージは pip を使ってインストールできる。

pip install atproto

atproto を使って画像を添付したステータスを Bluesky へ投稿する

この例では bluesky_apibsky.social を指定しているが、皆さんが利用しているサーバーのAPIベースURLに変更して欲しい。

atproto ではステータスの投稿自体は send_post() を使って簡単に送信できるようになっているが、画像の投稿に関しては高レベルなAPIは提供されておらず、低レベルの XRPC を直接操作しなければいけない。素人には扱いがかなり難しいものとなっている。

import os
import sys
import time
from datetime import datetime
from atproto import Client, models

bluesky_api = Client('https://bsky.social/xrpc')
bluesky_user_name = 'ch3cooh.bsky.social'
bluesky_password = 'パスワード'

bluesky_api.login(
    bluesky_user_name,
    bluesky_password
)

# 引数から画像を含むディレクトリのパスを取得する
image_dir_path = sys.argv[1]

# 画像をアップロードする
bluesky_images = []
for filename in sorted(os.listdir(image_dir_path)):
    if filename.endswith('.png') or filename.endswith('.jpg'):
        file_path = os.path.join(image_dir_path, filename)
        with open(file_path, "rb") as f:
            upload = bluesky_api.com.atproto.repo.upload_blob(f)
            bluesky_images.append(models.AppBskyEmbedImages.Image(alt='Img alt', image=upload.blob))

# 投稿を作成する
status = 'Hello, Bluesky!'
bluesky_embed = models.AppBskyEmbedImages.Main(images=bluesky_images)

bluesky_api.com.atproto.repo.create_record(
    models.ComAtprotoRepoCreateRecord.Data(
        repo=bluesky_api.me.did,
        collection=models.ids.AppBskyFeedPost,
        record=models.AppBskyFeedPost.Main(
            createdAt=datetime.now().isoformat(), text=status, embed=bluesky_embed
        ),
    )
)

ターミナルで、アップロードしたい画像ファイルが入っているディレクトリパスを指定して実行しよう。

python post_media_bluesky.py /Users/ch3cooh/Desktop/images

成功すると下図のようにステータスを投稿できているはずだ。

関連記事

atproto を使ってステータスを Bluesky へ投稿する

Python を使って、Bluesky に「Hello, Bluesky!」と投稿する方法を紹介する。

atproto のインストール

Python で Bluesky にメッセージを投稿するには atproto というパッケージを使用する。Python から Bluesky (AT Protocol) に簡単にアクセスするためのツールである。

github.com

このパッケージは pip を使ってインストールできる。

pip install atproto

atproto を使ってステータスを Bluesky へ投稿する

この例では bluesky_apibsky.social を指定しているが、皆さんが利用しているサーバーのAPIベースURLに変更して欲しい。

import os
import sys
from atproto import Client

bluesky_api = Client('https://bsky.social/xrpc')
bluesky_user_name = 'ch3cooh.bsky.social'
bluesky_password = 'パスワード'

bluesky_api.login(
    bluesky_user_name,
    bluesky_password
)

# 投稿を作成する
status = 'Hello, Bluesky!'
bluesky_api.send_post(status)

上記のスクリプトを実行した。成功すると下図のようにノートを投稿できているはずだ。

関連記事

Swift で上付き文字・下付き文字を表示させる

iOSアプリで「H₂O」や「x²」のような上付き文字や下付き文字を表示する方法について調査する機会があった。

さくさんは、2010年頃(iPhone OS 3.xの時代)からiOSアプリの開発を行っているが、上付き文字や下付き文字を扱うのは今回が初めてだった。他の多くの方もあまり使わないのか、Swiftで上付き文字や下付き文字を扱う方法について説明している記事を見つけることができなかった。

上付き文字・下付き文字とは

Unicodeのコードポイントを指定すれば、上付き文字や下付き文字を表示できるだろうことは理解していたが、その「小さな文字が横についている状態」を何と呼ぶのかを調査するのに苦労した。

Wikipediaによると、上付き文字や下付き文字は、基準となる文字の上部または下部に書かれる付記文字のようだ。英語では、上付き文字は「superscript」、下付き文字は「subscript」と呼ばれる。

頻繁に使用するであろう数字の上付き文字や下付き文字は以下の表にまとめた。

通常文字 上付き文字 下付き文字 Unicode (上付き) Unicode (下付き)
0 U+2070 U+2080
1 ¹ U+00B9 U+2081
2 ² U+00B2 U+2082
3 ³ U+00B3 U+2083
4 U+2074 U+2084
5 U+2075 U+2085
6 U+2076 U+2086
7 U+2077 U+2087
8 U+2078 U+2088
9 U+2079 U+2089
a U+1D43 U+2090

文字列リテラルとして定義する場合

プログラム内で上付き文字や下付き文字を直接的な文字列リテラルとして扱いたい場合、以下のように定義することができる。

let str1 = "H\u{2082}O" // 下付き文字 2 を含む文字列 ("H₂O")
let str2 = "x\u{00B2}" // 上付き文字 2 を含む文字列 ("x²")

文字列リソースとして定義する場合

iOSアプリでは Localizable.strings を用いることでローカライズが可能だ。しかし、上付き文字や下付き文字を文字列リソースとして定義する際には、先ほど述べた文字列リテラルをそのままコピー&ペーストして使うことはできない。記述方法が異なるため、注意が必要である。

"key_1" = "H\U2082O";
"key_2" = "x\U00B2";

サンプルコード

この記事を作成する過程で利用したSwiftUIのサンプルコードを以下に示す。

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing: 16) {
            Text("H\u{2082}O")
                .font(.system(size: 48))
            
            Text("x\u{00B2}")
                .font(.system(size: 48))
            
            Text("key_1")
                .font(.system(size: 48))
                .foregroundColor(.green)
            
            Text("key_2")
                .font(.system(size: 48))
                .foregroundColor(.green)
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

上記のコードを実行した結果は以下の通り。

Flutterで、Firebase Authentication の認証プロバイダーの増減を監視する

Firebase Authentication を使った Flutter アプリを開発している。ログインプロバイダーが増減したときのイベントを取得したい。

たとえば、ユーザーアカウントに Apple アカウントが接続されている時には「Appleアカウントと連携中!」と表示し、接続解除時には「Appleアカウントと未接続!」と表示したい。

認証プロバイダーの増減を監視する

よく紹介されている authStateChanges() を監視する方法ではうまく動かなかった。昔のバージョンではイベントが発火していたのかもしれないが、現行のバージョンでは linkWithProvider()unlink() を呼んでもイベントは発火しなかった。

ログインプロバイダーの増減を監視するためには userChanges() を使う必要があった。

final authProvider = Provider((ref) => FirebaseAuth.instance);

final userChangesProvider = StreamProvider.autoDispose((ref) {
  var auth = ref.watch(authProvider);
  return auth.userChanges();
});

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userChanges = ref.watch(userChangesProvider);

    return MaterialApp(
      home: Scaffold(
        body: /* 省略 */,
    );
  }
}

userChanges() の存在を知らなかったので遠回りになってしまったが、どのデータが変更されたときにどの Stream が流れるのかについては、以下のドキュメントに明記されている。

firebase.google.com

表にまとめた。

  authStateChanges() idTokenChanges() userChanges()
対応イベント ・リスナーの登録直後
・ユーザーがログインしたとき
・現在のユーザーがログアウトしたとき
・リスナーの登録直後
・ユーザーがログインしたとき
・現在のユーザーがログアウトしたとき
・現在のユーザーのトークンが変更されたとき
・リスナーの登録直後
・ユーザーがログインしたとき
・現在のユーザーがログアウトしたとき
・現在のユーザーのトークンが変更されたとき
・FirebaseAuth.instance.currentUser が提供する次のメソッドが呼び出されたとき:
 ・reload()
 ・unlink()
 ・updateEmail()
 ・updatePassword()
 ・updatePhoneNumber()
 ・updateProfile()

だめなケース

Widget が rebuild されない。

final authProvider = Provider((ref) => FirebaseAuth.instance);

final authStateChangesProvider = StreamProvider.autoDispose((ref) {
  var auth = ref.watch(authProvider);
  return auth.authStateChanges();
});

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final authStateChanges = ref.watch(authStateChangesProvider);

    return MaterialApp(
      home: Scaffold(
        body: /* 省略 */,
    );
  }
}

Misskey .py を使ってステータスを misskey.io へ投稿する

Python を使って、misskey.io に「Hello, Misskey!」と投稿する方法を紹介する。

Mastodon.py のインストール

Python で Misskey にメッセージを投稿するには Misskey.py というパッケージを使用する。Python から Misskey API に簡単にアクセスするためのツールである。

github.com

このパッケージは pip を使ってインストールできる。

pip install Misskey.py

Misskey.py を使ってステータスを misskey.io へ投稿する

この例では api_base_urlmisskey.io を指定しているが、皆さんが利用しているサーバーのAPIベースURLに変更して欲しい。

import os
import sys
from misskey import Misskey

access_token = 'アクセストークン'
api_base_url = 'misskey.io'

misskey = Misskey(
    api_base_url,
    i=access_token
)

note = 'Hello, Misskey!'
misskey.notes_create(note, visibility='public')

上記のスクリプトを実行した。成功すると下図のようにノートを投稿できているはずだ。

関連記事