からかい上手の(元)高木さん(11)

本編にも出てきたキャラクターであるゆかりの結婚式の回で、ちーと西片がなぜか結婚式の練習をしたわけだけど、ちーの姿にほっこりしつつも自分の子供の結婚式を想像してもやっともした。まさに作中の西片と同じ。自分の境遇に近いのもあり、本編よりもスピンオフの本作の方が、西片に感情移入しがちだ。

SceneDelegate 時代にコードだけで iOS アプリを実装し始める手順

iOS 13 で SceneDelegate が導入されて、Storyboard を使わずコードだけで iOS アプリを開発し始める手順が変わっていたのでメモ。

まず、メインインタフェースを空にする。これは以前と同じ。

f:id:griefworker:20210214213822p:plain

Info.plist の UISceneStoryboardFile を削除。

f:id:griefworker:20210214213838p:plain

SceneDelegate にウィンドウを表示する処理を記述。以前は AppDelegate に書いていたものが、こっちに移動してきた感じ。

using Foundation;
using UIKit;

namespace HelloSceneDelegate
{
    public class Application
    {
        static void Main(string[] args)
        {
            UIApplication.Main(args, null, "AppDelegate");
        }
    }

    [Register("SceneDelegate")]
    public class SceneDelegate : UIResponder, IUIWindowSceneDelegate
    {
        [Export("window")]
        public UIWindow Window { get; set; }

        [Export("scene:willConnectToSession:options:")]
        public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
        {
            if (scene is  UIWindowScene windowScene)
            {
                var window = new UIWindow(windowScene: windowScene);
                Window = window;
                window.RootViewController = new UINavigationController(
                    new UIViewController
                    {
                        Title = "HelloSceneDelegate",
                    });
                window.MakeKeyAndVisible();
            }
        }

        [Export("sceneDidDisconnect:")]
        public void DidDisconnect(UIScene scene)
        {
        }

        [Export("sceneDidBecomeActive:")]
        public void DidBecomeActive(UIScene scene)
        {
        }

        [Export("sceneWillResignActive:")]
        public void WillResignActive(UIScene scene)
        {
        }

        [Export("sceneWillEnterForeground:")]
        public void WillEnterForeground(UIScene scene)
        {
        }

        [Export("sceneDidEnterBackground:")]
        public void DidEnterBackground(UIScene scene)
        {
        }
    }

    [Register("AppDelegate")]
    public class AppDelegate : UIResponder, IUIApplicationDelegate
    {

        [Export("window")]
        public UIWindow Window { get; set; }

        [Export("application:didFinishLaunchingWithOptions:")]
        public bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {
            return true;
        }

        // UISceneSession Lifecycle

        [Export("application:configurationForConnectingSceneSession:options:")]
        public UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
        {
            return UISceneConfiguration.Create("Default Configuration", connectingSceneSession.Role);
        }

        [Export("application:didDiscardSceneSessions:")]
        public void DidDiscardSceneSessions(UIApplication application, NSSet<UISceneSession> sceneSessions)
        {
        }
    }
}

からかい上手の高木さん(15)

100%片思いのオフィシャル資料集を買うために、手伝いをして貯めたなけなしの小遣いを、怪我している見ず知らずの子供を治療するための手当てセット購入に使った西片はカッコ良かった。悩む素振りすら見せず、買いに走る後ろ姿が印象的。読んでいて素で感心した。これは高木さんの中でも西片株が爆上がりだろうな。ストップ高だ。

HatenaBookmarkSharp - はてなブックマーク REST API C# クライアント

はじめに

HatenaBookmarkSharp っていう、はてなブックマーク REST APIC# クライアントを書いた。

developer.hatena.ne.jp

www.nuget.org

github.com

モチベーション

自分が現在作っているアプリではてなブックマーク REST API を呼び出したい場面があり、今のところ C# のクライアントが NuGet に存在しなかったので、ライブラリとして作って NuGet に放流した。

使い方

OAuth トークンを取得するための HatenaAuthorizer と、はてなブックマーク REST API を呼び出すための HatenaBookmarkClient を提供している。

var authorizer = new HatenaAuthorizer(
    consumerKey: consumerKey,
    consumerSecret: consumerSecret);

var requestToken = await authorizer.GetRequestTokenAsync(
    scope: Scopes.All);

var authenticationUri = authorizer.GenerateAuthenticationUri(
    requestToken.OAuthToken);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
    var escapedUrl = authenticationUri.ToString().Replace("&", "^&");
    Process.Start(new ProcessStartInfo("cmd", $"/c start {escapedUrl}")
    {
        CreateNoWindow = true,
    });
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
    Process.Start("xdg-open", authenticationUri.ToString());
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
    Process.Start("open", authenticationUri.ToString());
}

Console.Write("verifier:");
var verifier = Console.ReadLine();

var accessToken = await authorizer.GetAccessTokenAsync(
    oauthToken: requestToken.OAuthToken,
    oauthTokenSecret: requestToken.OAuthTokenSecret,
    verifier: verifier);

var client = new HatenaBookmarkClient(
    consumerKey: consumerKey,
    consumerSecret: consumerSecret,
    oauthToken: accessToken.OAuthToken,
    oauthTokenSecret: accessToken.OAuthTokenSecret);

var user = await client.GetMyAsync();

var tags = await client.GetMyTagsAsync();

var url = new Uri("https://tnakamura.hatenablog.com");
var createdBookmark = await client.CreateBookmarkAsync(
    new NewBookmark(url)
    {
        Comment = "[blog]Test",
    });

var bookmark = await client.GetBookmarkAsync(url);

var entry = await client.GetEntryAsync(url);

await client.DeleteBookmarkAsync(url);

実装

シンプルな HttpClient ラッパー。レスポンスの JSON をパースするのに、Newtonsoft.Json ではなく System.Text.Json 使ってみた。

はてなブックマーク REST API の認可が OAuth 2.0 ではなく OAuth 1.0 という部分はイケてない。あまり力を入れていないんだろうか。IdentityModel とか使うの厳しそうだったので、OAuth 1.0 クライアントは自前で抱えることに。neuecc 氏の AsyncOAuth をだいぶ参考にさせてもらった。

おわりに

今のところ、はてなブックマーク REST API のドキュメントにあるものには対応している。はてなブックマーク REST API 自体にほとんど変化がないので、当面はライブラリにバグが見つかったら修正するくらいの、ゆる〜い方針で開発する。

自分が使うために作ったとはいえ、はてなブックマークREST API を呼び出す需要はほとんど無さそうな気がしないでもない。

C3

バレンタインの名目で、福岡三越に出店している C3(シーキューブ)のチョコレートのティラミスカップといちごのティラミスカップを食べた。

チョコレートのティラミスは去年から食べたいと思っていて、いつ買おうかと思っている間に店頭から消えて、食べ逃していた。バレンタインの時期限定とはね。念願叶って1年越しにようやく食べることができた。いちごのティラミスも気になっていたので、せっかくだし両方食べてしまえ作戦。

f:id:griefworker:20210209200233j:plain

いちごのティラミスカップは見た目が可愛らしくて、非常に口当たりの軽いショートケーキっていう感じ。生クリームは辛くなってきたけど、マスカルポーネのクリームは最後まで美味しく食べることができた。もっと食べられそう。

1年待ったチョコレートのティラミスカップは期待以上だった。刻んだチョコレートをふんだんに使っていて、小さな板チョコも乗っていたりして、贅沢でリッチな味わいだった。しっかりした甘さとほろ苦さの共演。大人なチョコレートの味。1年待った甲斐があったな。特に、こんなにチョコレートが入っているとは思っていなかったので、満足度高い。通年で売ってくれたらいいのに。

関連ランキング:洋菓子(その他) | 西鉄福岡駅(天神)天神南駅天神駅

寄宿学校のジュリエット(1)〜(16)

積んでいた『寄宿学校のジュリエット』を全巻読み終えた。タイトルにジュリエットが含まれていて、かつ主人公がロミオでヒロインがジュリエットという点から分かるように、ロミオとジュリエットから着想を得たであろうラブコメ。この手のラブコメの舞台は現実世界の日本ぽい街が多いけど、このマンガの舞台は空想の世界の学園都市。魔法とかは無いけど。日本がヨーロッパにあったら、こんな世界感になったかもね。

ロミオとジュリエットが元になっているだけあって、2人は1話で速攻恋人関係になるわけだけど、それからがスタート。恋人関係を隠しながら学園生活を過ごし、途中親友の蓮季にバレたり、ロミオの実の兄に怪しまれたりといった窮地を2人で乗り越えていく。お互いがお互いに良い影響を与え合い、高め合っている感じが良いね。ラブコメのヒロインであるジュリエットが可愛いのは当然として、誇り高い、格好良さも併せ持っているのが、ちょっと新鮮だった。

お互いの所属する寮が敵対関係にあるため交際を隠しているけど、公にできるように学園を、ひいては世界を変えようと奮闘していく。予想通り最後まで隠し通せるなんてことは当然無くて、皆にバレて本作最大のピンチに陥るわけだけど。その絶対絶滅っぷりといったらもう。ただ、2人がそれまでに行ってきたことが実を結んで、土俵際からじわじわ盛り返し、最後には大多数を味方につけた終盤の展開は震えた。

その絶対絶滅のピンチにおいて一番重要な役割を果たしたのは、ロミオのライバル的立ち位置の丸だな。初登場時の第一印象は最悪だったのに、登場を重ねるうちに、意外と仲間思いの良いやつなのかもと思えるように。ジュリエットと一風変わった友情築いちゃったりもして。まぁあれだ。不良が良いことをしたら評価が変わるのに近いかも。

とにかく、ジュリエットがカッコ可愛いマンガだった。ロミオとジュリエットは悲劇だけど、ラブコミで悲劇なんて許されるはずもなく、予想通り、2人ゴールインしてめでたしめでたしハッピーエンド。周りのキャラも魅力的なのが多くて、ロミオの影は薄かったかも。まぁ、ラブコミの主人公の存在感なんてそんなもの。絵が可愛かっただけに、作者の次回作にも期待したい。

Git リポジトリに機密データをうっかりコミットしてしまったときの対応

Git リポジトリにうっかり鬼滅、じゃなかった機密データをコミットして、GitHub に push までしてしまった。幸いプライベートリポジトリだったので、直ちに影響はない。

ただ、完成したら public にする予定なので、このままではいけない。履歴が消えても影響無いくらいのコミット数なので、リポジトリを作り直すこともできるが、それだとせっかく GitHub に生やした草が無くなってしまう。それは避けたい。

Git のコミットから何とかして削除するしかないな。git filter-branch コマンドを使って歴史もとい履歴を改変。

git filter-branch --force --index-filter "git rm --cached --ignore-unmatch 機密データのファイルパス" --prune-empty --tag-name-filter cat -- --all

うっかりコミットしてしまいそうなら、.gitignore に追加したほうが無難。今回は環境変数を使うようにしたので、.gitignore に追加しなかった。

仕上げに、GitHub に強制 push して、GitHub 上の履歴も改変する。

git push origin --force --all

これで難を逃れた。違うリポジトリでまたやってしまう可能性はゼロではないので、やり方はブログにメモしておくけど、そう何度も実行したいことじゃないな。やらないで済むならそれが一番。.gitignore 大事。