「てがきはてなブログ」がリリースされていたので、早速使ってみました。iPadで絵日記がかけてすごい!
普段は手で字を書かないので、まったくうまく書けなかった。あと日付を間違っている。そういうのも含めて、よい体験だった。
SwiftUIはWWDC20で大きく更新された。アプリ全体をSwiftUIで作る方法が確立されたほか、新しい抽象がいくつも導入されている。どれも非常に興味深いが、本記事ではその中からActionパターンを見出し、紹介する。
WWDC20ではSwiftUIに、以下の4つの「Action」が導入された。
また対応するプロパティがEnvironmentValuesに追加されている。
ドキュメントに使い方が載っている。
struct SupportView : View { @Environment(\.openURL) var openURL var body: some View { Button(action: contactSupport) { Text("Email Support") Image(systemName: "envelope.open") } } func contactSupport() { openURL(mailToSupport) } }
@Environment(\.openURL) var openURL
としてEnvironmentから取得しているのが、OpenURLAction
構造体の値である。これをcontactSupport()
関数の中で、openURL(mailToSupport)
という風に呼び出している。
構造体の値を関数のように呼び出しているが、これは「SE-253 Callable values of user-defined nominal types」で議論され、Swift 5.2で導入された機能だ。このような値をcallable valueという。callAsFunction
メソッドを持っている構造体の値やクラスのインスタンスは、関数のように呼び出すことができる。関数のシグネチャはcallAsFunction
メソッドのシグネチャと一致する。またcallAsFunction
メソッドはオーバーロードできる。
4つのActionが、「Action」と名付けられていることには意味がある。Actionの特徴は、以下のようになる。
XxxAction
のように)callAsFunction
メソッドを持っている)EnvironmentValues
のプロパティはActionの名前からAction接尾語を除いたものになる(OpenURLAction
はopenURL
になる)最後だけは「おそらく」と書いたが、Actionを参照型で作って状態を共有するようなケースはおおよそ考えられないし、またそうあるべきではないと思われるから、値型(つまり構造体など)で作るのが普通だろう。
これらの特徴を備えたActionは必然的に、コンテキストの親(祖先)が提供する機能を、子(子孫)から呼び出す、というユースケースになる。
Actionとして抽象化できそうな処理があれば、独自のActionを作ることができる。
Actionとなる構造体を作るのは簡単だ。
struct PrintStringAction { func callAsFunction(_ string: String) { print(string) } }
これをEnvironmentに入れるには、以下のようにKeyを作って、EnvironmentValues
を拡張する。
struct PrintStringActionKey: EnvironmentKey { static var defaultValue: PrintStringAction { return PrintStringAction() } } extension EnvironmentValues { var printString: PrintStringAction { get { return self[PrintStringActionKey.self] } set { self[PrintStringActionKey.self] = newValue } } }
これだけで、@Environment(\.printString) var printString
として利用できる。
(この例はActionとして抽象化するのが適切ではない……。)
「Action」という語は、伝統的なCocoaにおいてはTarget-Actionパターンに登場し、さらにUIKitにはUIActionが登場している。あるいはFluxでも使われる。このように、文脈に依存して意味が少しずつ異なるが、SwiftUIではここまで説明したようなものが「Action」パターンである。
ということで、SwiftUIにおけるActionパターンを見出した。パターンというのは、再利用可能な設計のプラクティスである。
Actionパターンは、SwiftUIにおいて必要不可欠な抽象化になっていくだろう。
WWDC20のKeynoteをみて、ドキュメントを乱読し、Platforms State of the Unionをみて、ちょっと寝て、仕事をしている。今はめちゃくちゃ眠たい。仕事休んだらよかった。
一昨日、ちょっとふざけた感じで、各メディアの予想と筆者の考えをまとめて公開した。
今年はiOS 14の早期ビルドがリークしていたと言われている。そのせいか、各メディアの予想はかなり網羅的だったと言える。筆者の予想も全体的にはいい線だったと思う。
ほぼ正解で震えてる https://t.co/HqYkEARRC9
— giginet (@giginet) 2020年6月22日
「SwiftUI 2」と書いた内容は、ウインドウをSwiftUIで作れるとか、穏当に実現している。SwiftUIのパラダイムにはあまり大きな変化がないが、それでも注目すべきことがあるので、いずれ紹介したい。
「ARM Mac」は、各メディアの予想と一致している部分が大きい。2021年ではなく2020年末に最初の製品が出荷されるであろうことは朗報だ。また移行キットも提供される。「iPad Proをその目的で使えるとおもしろい」と書いたが、移行キットにA12Zが搭載されるので、当たらずとも遠からずというところか。
「新しい可能性」と称して、ホーム画面のウィジェットに言及しているが、これも各メディアの予想通りである。「実装はApp Extensionになるだろうけど、全体的なアーキテクチャはApple Watchに近い可能性」と書いているが、実際、Apple Watchのコンプリケーションに似たAPIだと思う。ついでにコンプリケーションもウィジェットも、SwiftUIに寄っていった。
一番の驚きは、次世代のmacOSとなるBig Surが、ついにmacOS 11にナンバリングされ、ビジュアルデザインが刷新されたことだ。知る限り、ビジュアルデザインの変更はどのメディアでも予想していなかったし、自分でもまったく予測不可能だった。
思えばmacOSのタイトルバーは、この何年かのアップデートで、徐々に形を変えていた。iOSアプリがアプリ・セントリックな(つまりドキュメント・セントリックではない)パラダイムを採用する中、macOSにおいてもシングル・ウインドウなアプリケーションがマジョリティになっていった。結果として、ウインドウのタイトルバーの意味が相対化されたのだろう。
こうしてBig Surでは、ついにタイトルバーのないウインドウが普通になった。タイトルバーのあった場所を、まるでUIKitのナビゲーションバーのようなツールバーが占めている。このようにすることで、ウインドウはまるでiOS/iPadOSのアプリのようになる。だからCatalystでmacOSに移植されたアプリも、一級市民となった。
Dockに並ぶアプリのアイコンも、iOS/iPadOSのアプリに合わせるように角丸になった。しかしそれでも何かを主張するように、iOSアプリとは違うディテールや立体感を与えられている。マルチウインドウが前提のGUIシステムでは、奥行き表現の重要性が相対的に高い。macOSのこの奥行きが、少なくとももうしばらくの間、macOSをmacOSに留めるのかもしれない。
最高。
最初はDrag & Dropのために作られたと思われたUIInteractionだけど、マウスポインタのサポートからApple Pencilでの文字入力にまで使われるようになって感動する。抽象化のお手本っぽい。 https://t.co/AwSPRvXlVp
— Hiroki Kato (@cockscomb) 2020年6月22日
2020年のWWDCは、新型コロナウイルスによる感染症拡大のため、ほかの様々なイベントと同様に開催形式が変わる。現地時間の22日(日本時間では23日)、KeynoteとPlatforms States of the Unionが行われ、その後に様々なセッションビデオが公開される予定だ。
Appleプラットフォームの開発者としては、WWDCは一年で最大の楽しみである。昨年はCatalystやSwiftUIが発表され、たいへん盛り上がった。今年は何が発表されるのか、否が応でも期待が高まる。
今年はとうぜん、SwiftUIの大幅なアップデートが出てくるだろう。画面いっぱいに「SwiftUI 2」の文字が大写しになるのが待ち遠しい。SwiftUIでアプリを作ろうとするとすぐに気づくのだが、SwiftUIはまだ機能不足である。
WKWebView
をラップしたWebView
を作るとする。ブラウザの「戻る」機能を作るために、WebViewが戻れるのかを知りたい。これにはWKWebViewのcanGoBack
プロパティを使えばいいはず。実際に戻るにはgoBack(_:)
メソッドを呼び出す。こういうのが、SwiftUIでは素直に実装できない。Reactであれば、Ref
という仕組みがあって、実際に作成されたコンポーネントへの参照が得られるが、SwiftUIにはない。TextField
のイニシャライザのように、onXxx
というコールバックを渡すとか、なんらかのシグナルをPublisherとして渡すとか、そういうことになってしまう。これは面倒なので、このようにViewが内包する状態を参照したり、あるいはViewに何かシグナルを送ったり、そういう機能が追加されると嬉しい。
SwiftUIがカバーする範囲がもっと拡がる必要もある。macOSのアプリをSwiftUIで作った経験から言えば、例えばウインドウそのものをSwiftUIで作れるとか、ウインドウのツールバーとか、そういう部分もカバーされていてほしい。ただし、現状のCocoaにはResponder-chainを活用した部分が多く、SwiftUIでこれを解決するのは骨が折れるだろう。
なんにしても、SwiftUIが非常におもしろい、未来の約束されたフレームワークであることは疑いようがない。宣言的なUIフレームワークであることはもちろん、実際のプレゼンテーションと、コード上の表現が意図的に分離されているところに、その本質がある。コード上では同じButton
でも、macOSとiOS/iPadOS、watchOS、tvOSで、それぞれ見かけが異なる。このことは、プラットフォーム毎に最適化されたUIを提供する上で都合がいい。あるいはこの先の、まったく新しいプラットフォーム(もちろんARグラスだ)においても、SwiftUIは役に立つことだろう。
そしてもう一つ、ポストInterface Builderの時代の到来だ。Interface Builderは、今やXcodeの一機能であるが、もともとは独立したアプリケーションとして、Project Builder(Xcodeの前身)を補完するものだった。最近はあまり聞かれないが、Interface BuilderはRADツールと呼ばれていた。SwiftUIの時代では、SwiftUIのコード自体がRADツールであろう。SwiftによるDSLめいた記述でUIを宣言できる。そしてこれは、例えばXcode for iPadのような開発環境において、UI開発の本流となるだろう。
iPad Proが、「ほとんどのノートパソコンより高速」であると宣伝されるようになってしばらく経つ。少なくともベンチマークの結果からは、この文句に嘘はない。
Macに搭載されると噂されているApple A14ベースの独自のプロセッサは、12コアで、既存のMacBook Airに搭載されているIntelのプロセッサよりも高性能とされる。A14プロセッサはTSMCの5 nmプロセスで製造されると言われている。現在のIntelは、14 nmもしくは10 nmプロセスでプロセッサを作っており、A13までの7 nmプロセスにも到達していない。IntelとARMを単純には比較できないとはいえ、プロセスルールが半分であれば、Intelプロセッサよりも高い性能を得られるというのにも信憑性がある。
開発者からみたとき、ARMへの移行はどれくらい大変なのか。かつてPowerPCからIntelへの移行時に行われたように、Rosettaのような互換レイヤーが提供される可能性もある。そうでなくても、単純なソフトウェアであれば、ソースコードから再コンパイルするくらいで対応できることが多いだろう。バイナリを直接扱っている場合、バイトオーダーやアラインメントによっては対応が必要になるかもしれない。少し厄介なのは、コンパイル済みのSDKやライブラリを使っている場合で、それらの対応を待つ必要がある。最も困難なのは、細かなチューニングが必要なアプリケーションだろう。
消費者向けにARMのMac端末が発売されるのは2021年という噂である。しかし開発者向けには、それに先立ってARM Macの移行キットが提供される可能性もある。PowerPCからIntelへの移行時には、移行キットがリースで提供された。あるいはiPad Proをその目的で使えるとおもしろいが、飛躍しすぎだろうとも思う。
iOS 14は、前年の失敗から、安定性とパフォーマンスを重視したリリースになると噂される。それでも戦略上、新機能がまったくないということにはならない。
iOSは、その当初からサードパーティに対しては大きな制約の中での開発を強いている。それはセキュリティのためであったり、あるいは端末のリソースを奪い合わないように、という目的がある。反面で、技術的に解決が可能になり、かつそれが必要だと認められれば、新しいAPIが開放されてきた。例えば初期の頃の、アプリのバックグラウンドでの動作を制限する代わりに、Push Notification Serviceを提供する、というのがそれだ。
iOS 14でも、いくらかのAPIが解放されることが期待される。その一つが、ホーム画面のウィジェットだ。iOSにはこれまでにもToday Widgetがあるが、すごく便利かというと、そうでもないと思う。そもそもiOSのホーム画面は、本格的なオーバーホールが必要な時期をとっくに過ぎている。初めてiPhoneが出た頃、私たちが1日に使うアプリの数はたかが知れていた。しかし近年、何もかもがスマートフォンで行える時代においては話が違う。ホーム画面にウィジェットを並べることができたら、Apple Watchのコンプリケーションのように、そのとき必要な情報を一望できるかもしれない。実装はApp Extensionになるだろうけど、全体的なアーキテクチャはApple Watchに近い可能性もある。
もう一つ、デフォルトのアプリを変えられるようになるという噂がある。ChromeをSafariの代わりにデフォルトにできたら、喜ぶ人も多いだろう。ついでにChromiumのようなものも許可してくれると嬉しい。Xcode for iPadの噂も含め、特にiPadOSをデスクトップクラスにしようという流れがあると思う。当然、現在の制約を大きく緩めるときが来ていると思う。
Appleに関する様々な情報を扱うメディアのうちいくつかが、WWDCで発表される可能性のある内容を予想している。予想と言っても、リーク情報をもとにしているものもあり、精度にはグラデーションがある。
以下の記事を参照して、大まかに一覧にした。
@
でメンション#shotoniphone
チャレンジが写真アプリに統合される2020年5月末に、 SwiftをAWS Lambdaで動作させるプロジェクトが発表された。swift-server/swift-aws-lambda-runtimeがそれである。ということで、AWS CDKでAPI GatewayとSwiftのLambda Handlerを作ってみた。
AWS Lambdaは、AWSのFaaS。提供されているランタイムを使えば、ソースコードをアップロードするだけで、関数が実行できる。提供されているランタイムはNode.jsやPython、Ruby、Java、Go、.NET Coreである(徐々に拡充されていった)。そして2018年のre:Inventで、Lambda LayerとLambda Runtime APIが発表され、swift-aws-lambda-runtimeでは、このRuntime APIを利用している。
Lambda Runtime APIを乱暴に説明すると、こうだ。bootstrap
という実行ファイルを用意しておくと、自動的にこれを起動してくれる。bootstrap
は内部でイベントループを回す。イベントループの内部では、HTTPでイベントを取得し、それを処理して、結果をHTTPで送る、というのを繰り返す。
swift-aws-lambda-runtimeは、イベントループを回して、イベントを取得して結果を返す、というところをやってくれる。
swift-aws-lambda-runtimeの主要な開発者であるFabian Fettさんのチュートリアルを参考に進める。
Getting started with Swift on AWS Lambda
まずはSwift Package Managerでpackageを作る。
$ swift package init --type executable
API Gatewayを使いたいので、Package.swift
でAWSLambdaEvents
の依存も追加。
import PackageDescription let package = Package( name: "Handler", platforms: [ .macOS(.v10_13), ], products: [ .executable(name: "Handler", targets: ["Handler"]), ], dependencies: [ .package( url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", .upToNextMajor(from: "0.1.0")), ], targets: [ .target( name: "Handler", dependencies: [ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-runtime"), ] ), ] )
API Gatewayのリクエストを受けて適当なJSONを返すのは、以下のようになる。
import AWSLambdaEvents import AWSLambdaRuntime import Foundation struct Response: Codable { let message: String } Lambda.run { ( context, request: APIGateway.Request, callback: @escaping (Result<APIGateway.Response, Error>) -> Void ) in context.logger.debug("\(request)") let encoder = JSONEncoder() do { let response = Response(message: "OK") let json = try encoder.encode(response) callback( .success( APIGateway.Response( statusCode: .ok, headers: ["Content-Type": "application/json"], body: String(bytes: json, encoding: .utf8) ) ) ) } catch { callback(.failure(error)) } }
Lambda Runtimeの説明で書いたようなイベントループとかそういうのは、すっかり抽象化されている。
あとはこれをbootstrap
という実行ファイルにして、Lambdaにアップロードすればいい。
実行ファイルはAmazon Linux 2で作る。Dockerの公式なSwiftイメージに、Amazon Linux 2のものが用意されているので、今回はswift:5.2-amazonlinux2
を使う。
#!/bin/bash set -eu executable=$1 swift build --product $executable -c release target=.build/lambda/$executable rm -rf "$target" mkdir -p "$target" cp ".build/release/$executable" "$target/" cp -Pv \ /usr/lib/swift/linux/libBlocksRuntime.so \ /usr/lib/swift/linux/libFoundation.so \ /usr/lib/swift/linux/libFoundationNetworking.so \ /usr/lib/swift/linux/libFoundationXML.so \ /usr/lib/swift/linux/libdispatch.so \ /usr/lib/swift/linux/libicudataswift.so \ /usr/lib/swift/linux/libicudataswift.so.65 \ /usr/lib/swift/linux/libicudataswift.so.65.1 \ /usr/lib/swift/linux/libicui18nswift.so \ /usr/lib/swift/linux/libicui18nswift.so.65 \ /usr/lib/swift/linux/libicui18nswift.so.65.1 \ /usr/lib/swift/linux/libicuucswift.so \ /usr/lib/swift/linux/libicuucswift.so.65 \ /usr/lib/swift/linux/libicuucswift.so.65.1 \ /usr/lib/swift/linux/libswiftCore.so \ /usr/lib/swift/linux/libswiftDispatch.so \ /usr/lib/swift/linux/libswiftGlibc.so \ "$target" cd "$target" ln -s "$executable" "bootstrap" zip --symlinks lambda.zip *
swift build
して、Swiftのランタイムや標準ライブラリ(shared objectファイル)を集めてきて、実行ファイルをbootstrap
という名前でsymlinkして、ZIPにまとめている。
$ docker run --rm -it swift:5.2-amazonlinux2 ls /usr/lib/swift/linux libBlocksRuntime.so libicui18nswift.so.65.1 libFoundation.so libicuucswift.so libFoundationNetworking.so libicuucswift.so.65 libFoundationXML.so libicuucswift.so.65.1 libXCTest.so libswiftCore.so lib_InternalSwiftSyntaxParser.so libswiftDispatch.so libdispatch.so libswiftGlibc.so libicudataswift.so libswiftRemoteMirror.so libicudataswift.so.65 libswiftSwiftOnoneSupport.so libicudataswift.so.65.1 libswift_Differentiation.so libicui18nswift.so x86_64 libicui18nswift.so.65
あとはこれをDockerで動かす。
FROM swift:5.2-amazonlinux2 RUN yum -y update && yum -y install \ zip COPY build.sh /build.sh WORKDIR /src ENTRYPOINT ["/build.sh"]
ソースコードは/src
にマウントするつもりなので、Dockerfileはこれだけ。
$ docker build --tag swift-lambda-builder . $ docker run --rm --volume /Users/cockscomb/swift-lambda/handler:/src swift-lambda-builder Hanlder
あとはこういう感じで実行すると、マウントされたディレクトリ下に.build/lambda/Handler/lambda.zip
ができる。
パッケージングからデプロイの作業は退屈なので、Infrastructure as Codeって感じで、AWS CDKを使ってまとめてしまう。AWS CDKというのは、AWS CloudFormationをいい感じにしてくれるやつ。
CloudFormationは、YAMLとかで書かれたテンプレートをもとに、AWS上のリソース(ここではLambdaとか)を作成したり、更新したりしてくれる。CloudFormationで作ったリソースは、手で書き換えてはいけない。CloudFormationする時の単位をスタックと言う。
AWS CDKを使うと、CloudFormationのテンプレートをTypeScriptなどのプログラミング言語で書けるようになる。今回は使わないが、スタック間のリソースの依存関係もうまく表現できる。
CDKをセットアップしたら、以下のようなスタックを定義する。
import * as cdk from "@aws-cdk/core"; import * as lambda from "@aws-cdk/aws-lambda"; import * as apigateway from "@aws-cdk/aws-apigateway"; export class ApiGatewaySwiftStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const code: lambda.AssetCode = // TODO: ここをなんとかする const handler = new lambda.Function(this, "Handler", { code, handler: "Handler", runtime: lambda.Runtime.PROVIDED, }); new apigateway.LambdaRestApi(this, "Api", { handler, }); } }
Lambda Functionを作って、ランタイムをProvidedにして、API Gatewayにくっつけている。AWS CDKのAPI Referenceに、各パッケージの主要な使い方が載っているので、とっつきやすい。
import "source-map-support/register"; import * as cdk from "@aws-cdk/core"; import { ApiGatewaySwiftStack } from "../lib/api-gateway-swift-stack"; const app = new cdk.App(); new ApiGatewaySwiftStack(app, "ApiGatewaySwiftStack", { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, }, });
ここで作ったclass ApiGatewaySwiftStack
を、appの側でこういう感じでnew
してやればOK。
// TODO: ここをなんとかする
、と書いたところでさっきのlambda.zip
を作ってやりたいのだけど、どうしたものかと思っていたら、オフィシャルの@aws-cdk/aws-lambda-nodejs
パッケージで、Builder classを作ってDockerでなんかしているのを見つけたので、真似する。
import * as lambda from "@aws-cdk/aws-lambda"; import { spawnSync, SpawnSyncReturns } from "child_process"; import * as path from "path"; interface Options { dir: string; executable: string; } export class Builder { private static imageName: string = "swift-lambda-builder"; constructor(private readonly options: Options) {} private docker(args: string[]): SpawnSyncReturns<string> { const returns = spawnSync("docker", args); if (returns.error) { throw returns.error; } if (returns.status !== 0) { throw new Error( `[Status ${ returns.status }] stdout: ${returns.stdout?.toString().trim()}\n\n\nstderr: ${returns.stderr?.toString().trim()}` ); } return returns; } public build(): lambda.AssetCode { this.docker(["build", "--tag", Builder.imageName, path.join(__dirname, "../builder")]); this.docker(["run", "--rm", "--volume", `${this.options.dir}:/src`, Builder.imageName, this.options.executable]); return lambda.Code.fromAsset( path.join(this.options.dir, "./.build/lambda/", this.options.executable, "lambda.zip") ); } }
こういうDocker CLIを呼び出すものを作って、さっきのところに埋める。
import * as cdk from "@aws-cdk/core"; import * as lambda from "@aws-cdk/aws-lambda"; import * as apigateway from "@aws-cdk/aws-apigateway"; import * as path from "path"; import { Builder } from "./builder"; export class ApiGatewaySwiftStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const code = new Builder({ dir: path.join(__dirname, "../handler"), executable: "Handler", }).build(); const handler = new lambda.Function(this, "Handler", { code, handler: "Handler", runtime: lambda.Runtime.PROVIDED, }); new apigateway.LambdaRestApi(this, "Api", { handler, }); } }
あとはこれを使ってデプロイする。事前にAWS CLIの設定をしておくとよい。
$ npm run cdk deploy
AWSアカウントでまだCDKを使ったことがなければ、先に$ npm run cdk bootstrap
が必要かもしれない。
うまくいくと、API GatewayのURLが出力されるだろう。AWS ConsoleのCloudFormationを見ると、作成されたスタックが表示される。
適当にcurlしてみると、実際に動いている様子がわかる。
$ curl --include https://xxx.execute-api.ap-northeast-1.amazonaws.com/prod/ HTTP/2 200 content-type: application/json content-length: 16 date: Sun, 14 Jun 2020 13:25:02 GMT x-amzn-requestid: xxx x-amz-apigw-id: xxx x-amzn-trace-id: xxx x-cache: Miss from cloudfront via: 1.1 xxx.cloudfront.net (CloudFront) x-amz-cf-pop: xxx x-amz-cf-id: xxx {"message":"OK"}
上記の素朴なLambda Functionでは、コールドスタートとみられる場合で300から400 msの時間がかかった。一方でスタンバイからの実行では、2 ms程度だった。コールドスタートは、Lambda内部の初期化で150 ms、Lambda Functionの初期化が180 msというところ。それほど悪くはないと思う。メモリの消費は、メモリ容量を128 MBに設定したうちの51 MB。
コスト的には、Lambdaだけなら100万リクエストで1ドルかからないくらい、API Gatewayと合わせても5ドルくらいか。
ということで、SwiftでLambda Functionを作って、API Gateway経由で呼び出せるようにした。デプロイにはAWS CDKを使っている。完全なサンプルコードは以下。
実際にAWS LambdaをSwiftで開発するのかどうかというと、現時点では微妙なところである。しかしServer-side Swiftのエコシステムが、少しずつでも整っていくのは興味深い。
2020年5月7日、インクがにじんだような読みにくいテキストを作れる、Mac®︎向けの新しいアプリケーション、InkBleedがMac App Store®︎で販売開始となりました。InkBleedは、シンプルなインターフェイスにテキストを入力すると、インクがにじんだような画像をリアルタイムに生成します。画像を保存したり、コピーしたり、ドラッグしたりすることで、読みにくいテキストの画像を簡単に共有することができます。
InkBleedアプリケーションは、テキストを識別しにくくするためにmacOS®︎の最先端の技術を活用しています。Metal、Core TextとCore Graphics、Core Image、そしてユーザーインターフェイスにはSwiftUIを採用しています。
「インクがにじんでいると、テキストは判別しにくくなります。わかりやすいことが重要とされる現代で、わかりにくいことの豊さをお届けできることをとても嬉しく思います。インクのにじみのデジタル表現をぜひお楽しみください」と、ソフトウェアエンジニアリング担当、id:cockscombは述べています。
InkBleedのアイコンは、職場の同僚であり友人でもあるid:murata_sにデザインしてもらいました。美しいアイコンをDockに並べる喜びをお楽しみください。
InkBleedは本日からMac App Storeで有料で販売されます。InkBleedは、macOS Catalina 10.15以降を搭載し、Metalに対応しているMacコンピュータ1で利用することができます。
id:cockscombは、趣味の一環としてこのソフトウェアを開発しています。「紛らわしい文字列をもっと紛らわしくする」というエントリで説明したように、紛らわしいテキストをもっと紛らわしくすると、ちょっとかっこいい、という表現をアプリにしたものです。どうかご了承ください。アプリの売り上げはid:murata_sと山分けし、Apple製品を購入するために使います。
京都も緊急事態宣言の対象になった4月中旬、職場で原則在宅勤務のお達しが出た。正直に言えば、3歳と0歳の子供がいる中での在宅勤務には乗り気になれなかった。それでも、社会が急速に変化する中で、自分もまた変化しなくてはいけないと思った。
あまり広くない部屋に住んでいるので、以前からリビングの端に幅80センチの狭いデスクを置いている。
はじめ、12.9インチのiPad ProをSidecarにして、せめてものマルチディスプレイにした。SidecarはUSBケーブルを使うことで、Wi-Fi経由より安定して使える。またMagic Keyboardのおかげで、画面の角度が自由になる。
在宅勤務がある程度の期間続くだろうと見込んで、ディスプレイも注文した。本当は職場で使っている4Kディスプレイを持ち帰ることもできるのだけど、(どうせ前から欲しかったし、とか)いろいろと理由をつけて買ってしまうことにした。
一時的な在宅勤務のためとはいえ、長く使えるものを買いたい。4K解像度でIPSパネル、USB Type-Cで繋ぐことができ、24インチから27インチ、一桁万円台、という条件でいろいろ調べ、候補を3つに絞った。
一般的にはDell U2720Qが一番バランスに優れた選択肢だろう。LG 27UL850-Wも悪くはない。しかしApple製品との連携を重視して、筆者はLG UltraFine 4K Displayを選んだ。画質もすばらしい。皆さんは真似しないでください1。
合わせて ディスプレイアームも購入して、デスクに固定している。何年か前にデスクを買ったとき、ディスプレイアームのクランクが取り付けられるものを選んでいた。ディスプレイアームを使うことで、デスクをいくらか広く使える。小さいデスクでは重要なことである。そして何より、子供のイタズラでディスプレイがデスクから落下するのを防げる。
さらに有線LANも使えるようにBelkin USB-C to Gigabit Ethernet Adapter も導入。ディスプレイのUSBハブを活用している。
ディスプレイを購入したことで、生産性が劇的に向上した。ウインドウを並べて作業できる利便性は計りしれない。またUSB Type-Cのケーブル1本で、電源とディスプレイ、そして有線LANの全てにアクセスできる。便利そのもの。
ともあれ、家族の理解と協力のおかげでなんとか仕事ができている。このご時世にありがたい。私たちは、この不可逆的に変容してしまった社会で、この先も生きていかなければならない。先はあまり見えないけど、できることをやろうと思う。