ブログ

  • 長野プロジェクト:2019年ダイジェスト②

    長野プロジェクト:2019年ダイジェスト②

    ワタリドリは長野に拠点を作ることにしました!
    今の場所はそのままにおいておくつもりで、
    長野は私達が住む場所、開かれた場所、旅人が休める場所。
    私達の生活の一部が人が自由に出入りする場になると良いな。

    前回までのあらすじ

    1. 場所を探す
    2. 場所探し、振り出しに戻る
    3. ここに決めた!
    4. 私たちの場所が確定しました
    5. 青木村の夏祭り
    6. 長野プロジェクト:2019年ダイジェスト①

    夏祭りから時間が経ってしまったので、2020年10月までをダイジェストでお届け!その②!!
    前回書いた2019年秋以降のお話です。

    ダイジェスト「理想の間取り」

    最初はカフェから始めて、落ち着いたらゲストハウスも始めようという最初の計画。
    なのでカフェスペース、ゲストハウススペース(ドミトリー、個室、共用部分)、私たちの住居スペースが必要です。
    間取りにはそれぞれ必要な要素を詰め込み、理想の間取りを描いてみる。
    ログハウスは暗くなりがちだから、吹き抜けも欲しいし、北側の景色をゲストに満喫してほしい。
    ちょっと面白さも欲しいよね。
    建築に関する法律なんて全然わからないけど、いろいろ無視して理想の間取りが完成!

    ダイジェスト「業者さん決定&打ち合わせ」

    たくさんの業者さんに振られたけど、無謀な理想の間取りから見積も出してくれて根気よく私に付き合ってくれた2社から決めました。
    打ち合わせでは間取りをベースに理想としていること、叶えたいこと、伝えながら、間取りをリライトしていただく。
    最初は飲食店許可をとって、その後、簡易宿所の営業許可も取りたい。
    ハーフビルドもしたいんだけど、、、、
    それを踏まえて考えて欲しいと言う無茶振り。
    でもね、間取りがなんとなく決まりそうになった数回目の打ち合わせでちゃぶ台返し。
    許可関連やもろもろで結局、ハーフビルドは諦め、スピードを優先に。
    ゲストハウスは組み込まず、本気でやりたくなったら別棟をセルフビルドで建てることになって、間取りもやり直しに。

    ダイジェスト「引き続き打ち合わせ」

    カフェ&住居の間取りを作り直して、再度打ち合わせ。
    この頃からメールではなくて、LINEに。
    ExcelじゃなくてGoogleスプレッドシートでやりとり。
    (初めて使ったっぽい)
    間取りが決まったらログ材の発注です。
    はるばる海を渡ってくるので、早めに発注しておきたい。
    金策が確定していないけど、どうにかするだろうと見切り発車!

    ダイジェスト「お金の話」

    庶民の味方、住宅ローンを組めたら助かりますよね。
    カフェはワタリドリの事業だから、事業費にするのもアリです。
    どちらにしても、住宅ローンが組めるとされている住居部分が50%以上になるように間取りを調整。
    しかし、住宅ローンの会社も探すのは、困難を極めました。
    詳しい話はトリがいつかまとめてくれるのを待つとして、要するに田舎の土地は資産価値がないから住宅ローンはお断りってやつ。
    都市銀行はもちろん、ネット銀行も軒並みNG。
    普通に最初の審査で落としてくれるならいいものの、一生懸命資料を描いて提出した結果NGだと、流石にへこみます、、
    そんな私たちを救ってくれたのが地銀さん。
    地銀さん、ありがとう!!本当に助かりました!!

    雑草が伸びてきたので、一度草刈りもしましたよ。
    “

    _
    ここまで2020年の夏前くらいのお話。
    続きはまた今度。

  • 長野プロジェクト:2019年ダイジェスト①

    長野プロジェクト:2019年ダイジェスト①

    ワタリドリは長野に拠点を作ることにしました!
    今の場所はそのままにおいておくつもりで、
    長野は私達が住む場所、開かれた場所、旅人が休める場所。
    私達の生活の一部が人が自由に出入りする場になると良いな。

    前回までのあらすじ

    1. 場所を探す
    2. 場所探し、振り出しに戻る
    3. ここに決めた!
    4. 私たちの場所が確定しました
    5. 青木村の夏祭り

    夏祭りから時間が経ってしまったので、2020年10月までをダイジェストでお届け!

    ダイジェスト「雑草との戦い」

    2019年のお盆時期に行った時、雑草が元気よく育っていたので、これは草刈りしないとまずい。ってことで、草刈りをした2019年9月。
    初めて会ったお隣さんで夕食をいただいたり、翌日は草刈りを手伝って頂いたり、偶然来た元オーナーさんも法面をあっという間に綺麗に刈り取ってくれたり、下のお家がお茶に誘ってくれたり。
    いろいろな人に助けられた初めての草刈りでした。

    ダイジェスト「ログハウスを建てたい」

    最初は、古民家で、、自分たちでリフォームして、、、田舎暮らしの妄想は広がる。
    いろいろ回った結果、古民家ってすごい朽ちていて、これを住める状況に持っていくのにかかる費用と時間を考えると、、、、w
    古民家の基礎やら歪みを直すのに、持ち上げてあんなことこんなことして、、と費用がかかります。
    気にしないならいいんだけど、誰かに遊びにきてもらうことを考えると、出来る限り安全に寄せたい。
    後、そもそも、DIYすらやったことない私たちができるの??問題。
    性格的に、とりあえず自分たちでやってみるために調べるところから始めるので何やるにも時間がかかるんです。
    結果的にお金はやり方次第だけど、新築の方が住むまでに時間がかからない。
    自分たちのやりたいことを考えると結論は「新築」でした。
    とは言え、普段、よく目にする在来工法じゃ面白くないし、裏は山だし、ログハウスが似合いそう。「ログハウスを建てたい!」

    ダイジェスト「施工業者探し」

    ログハウスを取り扱っているところは、そんなに多くないです。
    行ったり来たりを考えると現地に近い方がいいし。
    なので、長野周辺で探してみました。
    探し方はログハウスを取り扱ってそうなところへ資料請求、お問い合わせ。
    電話は好きじゃないから、問い合わせしてフォームやメールアドレスがきさいされていないところはパス。
    並行してざっくり理想の間取りを書く。
    そこからやりとりを初め、生き残った業者さんに決定。
    最初はハーフビルドを考えていたこともあったし、私が細かいことを質問するので音信不通となる業者さんも結構いるんですよね。。(ごめんなさい)

    ダイジェスト「保健所へGo」

    最初はゲストハウスにしたかったんです。
    ゲストハウスにすると食事を出すなら飲食店営業許可も必要になる。
    そうすると、地域の人がお茶しに来れる様にしたくなる。
    じゃ、1回はカフェにしよう。
    ゲストハウス&カフェをいきなり始めるよりも、最初はカフェの方が始めやすいかもね。
    カフェが落ち着いたらゲストハウスも始めよう。
    そんな流れで、とりあえず、ゲストハウスを視野に入れたカフェを始める為にはどうしたらいいか、、、そんな時は保健所です。
    保健所にアポとって、いろいろ教えてもらいます。
    ここでも細かい事聞いて、担当さんを困らせてごめんなさいw
    その後、建築事務所と消防署も訪問。
    ちなみに、建築事務所はほとんど相手にしてくれず。
    消防署はゲストハウスに必要な防火設備とか丁寧にお話しをしてくれました。
    ありがたし。

    ここまで2019年の秋くらいのお話。
    続きはまた今度。

  • アプリ開発日記 v1.0.0リリース

    アプリ開発日記 v1.0.0リリース

    初回リリース!

    アプリをリリースしたぞ!
    ということで記念に開発日記を書いてみる。

    経緯

    去年のアドベントカレンダーでアプリ開発を開始した。
    この状態だとほぼ機能はないしtwitter連携するメモ帳みたいな状態になっていた。

    v0.0.1の状態

    最初はそのまま旅ログっぽいアプリを作っていたのだが、なんか作っている最中に違うなぁ。これは作っても使わないような気がするなぁ。

    と思いある程度形にしたものの途中で方向転換した。

    なんというか旅行中にブログかけてもいいしtwitter投稿とかも出来ていいんだけど、一言で言うと「機会が少ないし、使わないし、それに力注ぐのきつくないか」と言う感じ。

    そんなこんなで3ヶ月くらい作っていた時にコロナもやってきて、いよいよこれ旅ブログみたいなサービス・アプリだと無理だな。ってなった。

    方向転換

    「自分が欲しいな」と思って作るものでも
    「実際には自分は使わない」ってなることが過去に何回もあった。

    どっかの誰かが言ってました。

    アプリとかwebで人の行動をunlockすることはできるが、新しい行動様式に変容させるのはとても難しいし時間のかかることで、例えばUberだってもともと業界的にはタクシーのhiringみたいなのはありアクセス方法が変わっただけなのだ。

    という話をしていた。

    自分は「作ること」が好きで「使うこと」とかにあまり興味はないし、それで世の中どうのこうの変わって欲しいとかのwillもそんなにない。っていうのを再認識した。

    一方で種の状態ではあるが投稿するっていうものには、面白いなと思っている自分がいることも発見した。twitterには投稿するし、こうやって頻度は多くないがblogも書く。fbもやっているし、slackでながなが解説したり、github issueもマメに書く方だ(多分)。noteやmediumも興味はあるが、今このblogあるし書くところが散るのも微妙なのでやらず。

    ということで、この種を抽象化して、何かしらの投稿ができるサービスにしよう。となる。

    リリースまで

    「誰に使って欲しいか」とか「何を解決したいのか」とか特に持っていない状態の自分が「何か全然分からないけど投稿できるsomething」を作り始めたのだ。

    誰に使って欲しいのかよく分かっていないけど、セグメント切るのも嫌だし、
    どんな投稿されるのかもよく分かっていないので、特化型blogにするもの嫌だし、

    ということが重なって、少しpopなUIでテキストだけ投稿できるようなものを作った。
    機能はよく言えば超シンプル、悪く言えばほとんど何もない。

    app storeに出してユーザーコンテンツ投稿系でどれくらい審査めんどくさいのかも知りたかった。作りながら考えつつ、どういう風に進めるのかを模索していけばいいかと思って開発した。まずはstoreに出すことが最優先だ。という感じで進めた。

    https://apps.apple.com/us/app/watari/id1515516335?l=ja&ls=1

    できたー。

    v1.0.0感想文

    リリースして2,3日経つが、まぁ自分でも全然使わないw

    「使う」っていう行動変容するまでのパワーがこのアプリにはまだない。機能不足なのか、ユーザー間のインタラクション(反応)が無いからなのか。何かを投稿しようとしてもテキストONLYなので写真投稿しようとした瞬間に挫折するし、投稿する意欲が他のサービスと比較してもやはり落ちる。義務感として投稿することはできるかもしれないが。

    こういうのって綺麗にまとまっていないけど仕事でやるとマネージャーみたいな人が、「最低限サービスがサービスとして価値を出せる」ところのラインまで作ってるから多少はユーザーが定着するのだろう。

    あとはサービスに色がない。
    当たり前なんだけど、過去の投稿とかないし回遊する方法がない。
    作ってないから。

    このアプリ(サービス)では何を投稿すべきなのか、何が嬉しいのか。とかさっぱり分からない。さっぱり分からないように作っていったのだからそりゃ分からない。

    自分で自分のアプリを解析しているが恐ろしいものだ。

    v1.1.0に向けて

    これもどっかの誰かが言いました。
    「アプリとかwebとか強者総取りの時代だ」
    みたいな。

    まぁそうなのかもしれないけどそうあって欲しくはないし、同じようなことができる似たようなサービスなんてそれでも出てくるので、別に自分たちが入っていっても悪いなんてことはない。

    自分みたいなものを作るのが好きな人々の遺伝子は、だからといって作るのをやめたりはしない。(私の場合は)使って欲しいな、ユーザー増えればいいなと影では思いつつも、その優先度よりも自分がものを作り上げる。っていう方にパワーがいっている。

    もう一個これもどっかの誰かが言ってました。
    「自分の課題を解決するものを作れ」
    と。

    これもあんまよく分かってない。っていうかめちゃ難しい。「自分の課題」ってよく分からない。

    前述の通り「自分が使うと思っていて開発しても使わないことがよくある」というのを繰り返してきた自分としては、あまりその見極めの能力が高くないんだろうなぁと思わざるを得ない。というか自分のアイデアが非常に優れているという盲信によって作り出される存在しないユーザーを設定してしまうことがとても多いのだ。

    なのでそれの合否を必死に考えてるよりは、「世の中にあるものを少し変えてアクセスできるようにしてその過程で得たものを使って改良する。」的なやり方の方が自分にあってそうだなと思う。

    私は妻と二人で会社をやってはいるが、根幹はモノづくりの人間。であり、マーケティングでもないし、人脈が太いとかでもないし、有名でもない。

    自分にできることってなんだろうなと考えると、ひたすらに開発して改良してっていうのが打てる手になりそうだ。それはある意味理想系の一つで「ただひたすらに機能開発すること」に注力すればいい世界に生きていたいのだ。

    だから、作って作って、さらに作ってたまに壊して、やっていこうかと。

    そのお題として
    「モバイルで圧倒的に使いやすいブログというか記事投稿できる何か」
    を作っていこう。

    v1.1.0はこうなる?

    触ってみて、popなUIやっぱ違うわ。ってなった。のでもうちょいスタイリッシュにしたい。
    あと、やっぱ写真は必要だなって思う。

    思ったより大変だったこと

    作るのはまぁ大変だったんだが想定内で、想定外に大変だったのは

    • app store 法人developerアカウント作成
    • fastlaneとreleaseフロー周り

    この二つが非常に体力というかカロリーを使う。。。

    app store法人developerアカウント

    これ申請するのにDUNS Numberってのが必要で、弊社の経緯で過去に申請していたこともあり重複登録になるから過去の法人developerアカウント掘り返せ。

    っていうお達しがきた。

    過去の記憶を頼りにいろいろやりとりをして累計で3週間くらい使ったかな。。。

    fastlaneとrelease周り

    app storeの登録とかprovisoning profileとか、スクショ撮ったりとか、fastlaneの仕様を読んで新しい用語の波にさらわれ続けたりとか。

    概念理解するのに1週間くらい書いたり調べたりしていたかな。ようやく落ち着いてdev, stg, prodくらいのリリースとdebug実行がよろしくできるようになった。

    flutter flavor + iOSのbuild scheme + match(provisioning profile関係) + 連携先のweb切り替え

    で、「アレ?私は今、どこの何のレイヤーを触ってるんだこれ?」みたいになっていた。

    ついでに

    使ったモノは

    • flutter
    • swift
    • nextjs(ts)
    • firebase(firestore, auth)
    • GAE
    • (fastlane & match)

    最後に

    機能追加とかこういう方向にいってくれると嬉しい。とかあれば教えてください!

  • 私の作りたい未来の話

    私の作りたい未来の話

    先日、LEGO®︎SERIOUSE PLAY®︎ファシリテーターが集まる練成会に参加。
    「LSPを活用して作りたい未来」を作りました。
    言語化が苦手な私でも、ブロックで表現してみると、うまく言葉に出来てなかったことまでも言葉になるから不思議。


    これは「LSPを活用して作りたい未来」を上から見た図。


    これは左から見た図。


    これは右から見た図。

    解説

    どんな作品か説明します。


    1)トリです。コーディングしています。
    2)青木村に作った私たちの場です。
    3)お金。稼ぎ。
    4)自分らしさやワクワクを手にした人たち。
    5)自分らしさやワクワクを手にしようとする人。
    6)私。
    7)毎日を楽しんでいない人。

    ストーリー

    数年後、私とトリはすでに長野県青木村にいます。
    ちゃんと稼ぐこともできて、楽しい日々。
    トリは好きなプログラミングでワタリドリのサービスを作ったり、トリの技術を必要としている人を助ける。
    私はLSPなどのスキルを持った魔法使いみたいな感じで、手にはフラッグと虫メガネ。
    自分を見失ってモヤモヤウツウツしている人(ガイコツ)を「こっちだよーー」って呼びかけたり応援したりする。
    で、「失くしてしまった「ワクワク」や「自分らしさ」を一緒に探す。ここはLSPの出番ね。
    キラキラを見つけた人は自分のやりたい未来へ向かっていく。
    その時、誰かと一緒に進んでもいいし、一緒じゃなくたっていい。
    自分のwillからの選択をして、ワクワクする人がたくさんいる未来になっている。

    考察

    何度か私の未来を作ったことがあるけど、その舞台はいつもワタリドリでトリがいる。
    ワタリドリの事業でもある、青木村プロジェクトも、自社サービスも、ワークショップも、全部が作りたい未来に繋がっているみたい。
    「鳥のように自由に」というビジョンは、私たちがそうでいることが大前提で、ワクワクキラキラする人を増やしたいってこと。
    だって、その方が毎日が楽しく生きていけると思うから。

    もう一つ、気がついたことがある。
    私の作る作品には娘が出てこない。
    娘には幸せになってほしいと心の底から思っています。
    じゃぁ、なんでブロックで表現されないのか。
    私、娘が好きすぎて、、、ちゃんと子離れしないとヤバイって思っていますw
    いつまでも私のエゴで手元に置いてコントロールするのではなくて、娘はワクワクキラキラと自由に飛んていてほしい。
    そういう願いがあるから、娘は出てこないのだと思う。

    足りないもの

    年末にやった振り返りでも、練成会の時も出てきた言葉。
    「仲間がほしい」

    もちろん、トリは大切なパートナーで、大切な仲間。
    なんだけど、私たち、得意分野が違っていて、トリの得意分野はエンジニアリング。
    私がやりたくてやっているワークショップは基本ノータッチで、私もそれで良いと思っています。

    自主企画のワークショップを1人で運営するのはとても寂しいの、、、
    だから、、ワークショップ仲間がほしーーいー!

    おまけ「最近の嬉しかったできごと」

    着物イベントで2回ほどランチをご一緒した方から嬉しいことを言われました。

    「こうした方がいい、というアドバイスではなくて、会話の中で大切なことに気づかせてくれる。」
    「話していると元気がもらえる」

    1回目にお会いした時に、彼女が家族とケンカしたって話の流れで、私が「怒りって二次感情って言われてるらしいよー」って言ったらしい。

    私、アドラー心理学やアンガーマネジメントで読んだ「怒りは二次感情である」って話が好きです。
    それを知っているだけで、いろいろなことがわかって楽になりました。
    簡単に説明するとこんな感じ。

    怒りは二次感情であって、一次感情として別の感情が根底にある。
    例えば、
    ・優しくしてもらえなくて寂しかった
    ・私の気持ちをわかってくれなくて悲しかった
    ・嫌われているかもしれないという恐れ
    一次感情をうまく処理できなくて「怒り」を相手に向けてしまう。

    そんな話をした結果、彼女は一次感情に気がついてモヤモヤが解消されたらしい。

    あと、「I(私)メッセージ」で話すようにしています。
    私がどう思ったか。どう感じたか。
    その方が自分の考えを押しつけるのではなくて、気づきとそれを選択する余地ができると思うのです。
    人から指摘されるより、「自分で気づき自分で選択する」の方が自分のものになりやすい。

    ワークショップでも日常でも、「教える」「教えてもらう」じゃなくて、「感じる」「気づく」を大切にしたいです。
    そして、これも「鳥のように自由に」に繋がっている気がしています。

    長くなりましたが今日はこのへんで。

    2月のワークショップはこちら♡
    2月だから!LEGO®ブロックで描く2020年の目標〜Lego® Serious Play®メソッドと教材を活用したワークショップ

  • 【ベトナム】雨季のダナンのんびり1日目

    【ベトナム】雨季のダナンのんびり1日目

    一昨年はフィリピン。
    去年はタイ去年はタイ。
    3年連続で初冬にアジアに行ってるようです。
    今回はベトナムのダナンとホイアン。

    ダナンについてざっくり

    ベトナムの中部にあるリゾート地で、ハノイとホーチミンとダナンで、ベトナム三大都市です。
    世界遺産が多い地域で、北にはフエ、南にはホイアンの町並みとミーソン遺跡があります。

    移動に若干時間がかかるので、ダナンを起点に移動するのはあり。
    もしくはホイアンに滞在して、ダナン観光は初日と最終日にするのもありかな。
    そうするとフエの移動がちょっと大変だから、フエ滞在も検討ですね。

    2019年10月からは羽田-ダナンの直行便も就航されたので、さらに行きやすくなったかなーと。

    乾季と雨季があるから、目的により行く時期は選んだ方が良いです。
    12月は雨季で雨か曇りの日が多いし、波が荒いので海水浴には不向き。
    というか、海は無理。入れない。

    長袖がちょうどよいくらい、涼しいです。
    この時期は暑くないのが最大のメリット。
    そういう意味で、雨対策は必要だけど、観光には向いてそう。

    行くの羽田空港で教えてもらったのだけど、1ヶ月以内にベトナムに行ったことがある場合は、ビザが必要らしい。
    知らなかったー。

    真夜中の出発からスタート

    ベトジェットエアで羽田空港を午前2時半に出発します。
    トリの仕事が終わってから羽田へ21時に到着し、23時半からチェックイン。
    シーズンじゃないし、深夜便のLCCは人が少なめです。
    出発ゲートに到着したら人がいなくて、めっちゃ不安でした、

    ちなみに、ベトジェットエアのCAさんの制服がショートパンツでかわいい。


    座席はやっぱり少し狭いのは仕方なし。
    寝るだけだし、目が覚めたらダナンです。

    英語のアナウンスは聞き取れる部分が増えてきたのが嬉しい。

    到着後はイミグレーションカードを書くこともなく、あっさり入国。
    セキュリティがザルすぎるけど大丈夫???

    とりあえず、Amazonで買ったSIMに差し替えて、いざ、雨のダナンへ〜

    まったり過ごす1日目

    ダナンに行くと決めた日からせっせと情報収集。
    ベトナムに住んでいた人や行ったことある人にいろいろ教えてもらい、名所も調べるんだけど、、、いつもノープラン。


    空港のカフェでって作戦会議です。

    決まったこと

    1. とりあえず、朝ごはん食べよう
    2. 今日はまったり
    3. 6〜8日のどこかでホイアンとミーソン遺跡
    4. 最終日もまったり

    朝ご飯食べる場所も決めずに、とりあえずGrabで車を呼んでダナン駅まで行ってみる。
    Grab、便利!


    駅はこんな感じ。
    電車に乗ってみたいけど、今回は乗らない。
    ここから散策出発。
    早速、駅前にあったお店で朝ごはん。

    犬や鳥がいるお店。

    レインコート着てスクーター多い。

    ご飯のあとは、ハン市場とスーパーマーケット「BigC」。
    市場やスーパーマーケットは必ず立ち寄りたい場所。
    お土産を買うのにもお勧めです。

    BigCのカートがかわいい。

    海外のスーパーはフルーツや野菜が山のように積んであって好き。

    ドラゴンフルーツ山!


    白菜&キャベツ山!


    雑貨の階はクリスマスモード。

    ホテルのチェックインまだ時間があるので、BigCの近くのカフェでお仕事します。

    今回、荷物を軽くするために私のパソコンはお留守番です。
    私はスマホとノートでお仕事。

    そろそろランチタイム。
    お腹も空いたし、そろそろホテルに向かいましょう。
    ホテルまで4キロ。
    雨がやんだから、のんびり歩いて、途中、ランチ取りつつ、2時間くらいで到着するかな?
    タクシーでビューンと行くのは楽だけど、お散歩はいろいろな発見があって楽しいです。

    平日の昼間だけどコーヒー飲んでいる人がたくさんいる!
    とか

    ダナンにユ○○ロ!?
    同時期に別の場所にベトナム1号店オープンするって噂は聞いたけど、、
    とかww

    明るい時間にドラゴンブリッジを見てしまった、、、
    21時に火を吹くらしい。

    やっとホテルに到着。
    チェックインが済み、ホテルで一休み(結構寝ていたw)してから、夜ご飯を食べにお散歩。

    途中、可愛い子熊ちゃんに会いました。

    ダナンの印象

    そこそこ良さげなホテルが立ち並ぶ海沿い。
    アジア系の観光客多め。

    珍しく今回の旅は1つのホテルで滞在します。
    エクスペディアのセールでめっちゃ安かった四つ星。
    ホテルのお隣はゴリゴリとホテルを建設中なものの、中は快適空間。

    ダナンはリゾート地って聞くけど、思っていたよりもキラキラかリゾートではないかも。。
    まだ開発途中で、建設中の建物も多い。種種雑多。
    そんな昼間の印象。
    夜やシーズンになるとまた違う雰囲気になるかな?

    雨季の海はこんなに波が荒いよ。

  • watari開発 – Advent201925 – Merry Christmas!

    final stage: サンタクロースになろう!

     
    あなたはプレゼントをもらう側の人間だろうか。
    それとも、与える側の人間だろうか。


     
    「プロダクト開発」を考える上で毎日自問していたことがある。

     

    「どうやったら、熱量を込められる、良いモノが作れるのだろう」

     

    自分で作ったモノの中でも熱量を込めて作れたものはあまり多くはない。
    書いているのが好きだからひたすらに書いていたら気付いたら出来上がっていた。
    という状態になることの方が多い。

    しかし、先人たちはよくこう言っている。

     
     

    「自分自身の、尻に火がついている課題を解決するモノを作れ」

     
     

    と言われてもだ・・・、私には良く分からないのだ。
    良く分からないということが良く分かっているのでずっと自問しているのだ。

    そして、実は世の中のモノに対して困っていることはほぼないと思い始めた。

    だから多くのものが「それあったらいいな」かのように私の目には映る。
    そのせいか困っていても尻に火がつくほどではない。

     

    AirPod Proが出た。買おうかなと思っている。
    でも別に自宅にもイヤホンは転がっている。

    mercariが出た。ちょっとものを売ってみる。
    でもそれが存在しなかった時にはBOOK OFFにでも売ればよかったのだ。

    Netflixが流行っている。もちろんそこでしか見れない動画もある。
    でも無ければ無いで見ないだけで、本もあるしコーディングもしている。

     

    世の中にはたくさんのサービスが存在する。
    もちろんそれを使うユーザーもそれ以上に多くいる。

    私も何かを使って生きている。
    別に裸一貫でジャングルにいるわけでは無い。

    だから何かしらに自分も困っているはずだと思っていた。
    無理やりに課題を見つけて解決しようとしていた。

     

    でもどうやら違うようだ。

     

    人にはそもそも「富を与える力」と「好奇心」が備わっている。多分。

    富は自分が与えられる何かであって有限では無いし金銭でも無い。
    自分が相手に与えられるモノであればなんでも富だ。

    人の話を聞く、舞台で演劇をする、プロダクトを作る、プロダクトを提供する、
    料理をする、ただそこにいるだけ、でも富となり得るかもしれない。

    好奇心は未知なるモノに対する興味・関心であり、
    人が発展する要因として大切なモノの一つだ。

    自転車で転ぶ恐怖と乗れた時のワクワクとした好奇心。
    出不精な自分と変わった土地に旅行する時の好奇心。
    辛い状況下でも耐えうるくらいの打ち勝つ心。

     

    それら2つの要素の掛け算によって

    「〇〇さんの辛い状況をどうにかしてあげたい、自分なら何かできる」
    「〇〇さんに楽しそうな状況を作ってあげたい、自分なら何かできる」

    という想いが作られる。
    〇〇さんは自分に置き換えても良いし、妻、兄弟、親、友人、誰でも良い。

     

    プロダクト開発だと途端に分かりにくくなるが、

    「妻に内緒でプレゼント」
    「旅行先からの友人へのお土産」
    「悩んでいる兄弟の相談に乗る」
    などを考えると分かりやすいのではないだろうか。

     

    自分を含めた誰かに対する
    「〇〇さんの辛い状況をどうにかしてあげたい」
    「〇〇さんに楽しそうな状況を作ってあげたい」
    というポジティブな想いが

    長く疑問だった
    「どうやったら、熱量を込められる、良いモノが作れるのだろう」
    への自分なりの解であり、考えるスタート地点と捉えて良さそうだ。

     

    「こうしてあげたい」が与える側の思想。
    「あったらいいな」は受け取る側の思想。

    こういうモノあったらいいな。便利だ、問題解決できそう。ではなくて、
    自分にとって楽しそうな状況を作ってあげたいからこういうモノを作ろう。

    といった感じで考え始めるととてもシンプルで分かりやすい。

     

    watariに対して考えると、
    別に私達自身は「旅ブロガー」や「バイヤー」になりたいわけではない。
    記事を量産して見せるのであれば旅をしながらブログをひたすら書けばいい。

    のだけど少し違うのだ。

    端的に言うと
    多くの人が旅に出るきっかけとなってほしい。その方が生きていて楽しいから。
    そのためのシステムを構築するスキルやマインドが自分の富。
    ではないかなと。

    なので、まずは旅のログを簡単に書けるようにサービスを構築する。
    相手は閲覧者ではなく私を含めた旅行者自身なのだ。

    と言う話を塚にしたら自分へのしっくり感があった。

    ECサイトで物販をしたいのではなく、
    商品をネット上で販売したい人を助けるためにECマーケットプレイスを構築する。

    みたいな話だ。

    これからも引き続き開発をしていこう。


    今日はクリスマス。

    誰かに何かをプレゼントする時にきっと考える
    「こうしてあげたい」とか「喜んでもらいたい」という気持ち。

    この気持ちを持てるのであれば、
    誰でもプロダクトやサービスの開発はできるのだと思う。

    その気持ちさえ忘れなければ
    誰かのためのサンタクロースとして
    価値のあるものを作りあげることができるのではないだろうか。

    という感じで締めさせていただこう。

    アドベントカレンダー2019 終わり

  • watari開発 – Advent201924 –

    stage24: 画像をtwitterに上げる

    ようやくここまできた。ときはすでにクリスマスイブ。さっそく実装を進めていこう。

    swifterに連携する

    これまでの実装でほとんどパーツは揃っているはず。投稿すべき画像は手元にあり、コード上で扱うことができ、twitterへ投稿するためのライブラリも導入済み。ということでswifterを使って投稿する改修を入れる。

    // ImageView
    
    +import Prephirences
    +import SwifteriOS
    +import Keys
    
    ...
    
    +    let keychain = KeychainPreferences.sharedInstance
    +    let watariKeys: WatariKeys = WatariKeys()
    
    ...
    
    +                    if let credential = self.keychain.dictionary(forKey: "twitter"),
    +                        let oauthToken = credential["accessToken"] as? String,
    +                        let oauthTokenSecret = credential["secret"] as? String {
    +                        let swifter = Swifter(consumerKey: self.watariKeys.tWITTER_CONSUMER_KEY,
    +                                              consumerSecret: self.watariKeys.tWITTER_CONSUMER_SECRET,
    +                                              oauthToken: oauthToken,
    +                                              oauthTokenSecret: oauthTokenSecret)
    +                        swifter.postTweet(status: "テスト投稿 from アドベントカレンダー", media: image.pngData()!, success: { _ in
    +                            print("succeeded.")
    +                        }, failure: { error in
    +                            print("failed.")
    +                            print(error)
    +                        })
    +                    }

    実装本体はsave imageのところに入れる。

    実行!

    してみたところ。

    SwifterError(message: "HTTP Status 413: Payload Too Large, Response とのこと。

    調べてみると、

    https://developer.twitter.com/en/docs/media/upload-media/uploading-media/media-best-practices

    にある通り Image size <= 5 MB, animated GIF size <= 15 MB なので5MB以下じゃないといけないらしい。リサイズしないと。

    リサイズ

    let canvas = CGSize(width: 300, height: CGFloat(ceil(300/image.size.width * image.size.height)))
    let resized = UIGraphicsImageRenderer(size: canvas, format: image.imageRendererFormat).image {
        _ in image.draw(in: CGRect(origin: .zero, size: canvas))
    }

    これで大丈夫なはず。(適当に300pxに縮小)

    結果

    post部分を以下のように修正して、

    swifter.postTweet(status: "テスト投稿 from アドベントカレンダー", media: resized.pngData()!, success: { _ in
        print("succeeded.")
    }, failure: { error in
        print("failed.")
        print(error)
    })

    実行すると

    https://twitter.com/toriumi0118/status/1209124126993965057

    まとめ

    明日は少し作りながら思うところもあったのでそちらを書くことにするため、今日は作ったもののまとめをしていこう。

    今回のテーマは「プロダクト開発」だった

    課題感の模索、課題の選択と集中、解決した状態の妄想、解決策の構想、実現方法の選定、機能リストのリストアップ、目標数値の決定、環境構築、実装、開発環境整備
    を行ってきた。

    企業だともっと複雑なフローになるだろうし、一つ一つに様々なレイヤーの人が参加していて意思決定も一朝一夕というわけにはいかない。それぞれ考える軸が異なるし、頭の切り替えも必要だ。得意不得意もある。

    プロダクトを作り切るということ

    今回は「自分の課題(っぽいもの)」を解決するために実装に着手した。当初は全然間に合うであろうと未来の自分に期待していたのが、そんなこともなく旅行先にも全く間に合わず・・・ではあったが、やはり継続して開発するというのは必要なことだ。千里の道も一歩から。ぜんぜん未達だけど自分の問題が解決する製品が完成するまで地道にこのプロダクトをコーディングしていくしかない。

    プロダクトを作りきるというのはとてもカロリーを使うことで、自分が諦めた時がプロダクトの終了する時になる。一方で、どこまで作ればいいのか分からない、いつリリースすればいいのかも悩ましい。どこまで作ったら出せばいいのか。そんな悩みとも戦いながら少しずつでも作り上げていくための自分への発破の意味でアドベントカレンダーとした。

    +アルファで必要なこと

    今回話が全然できなかった内容としては、コードのクオリティとかデザイン、またマーケティング(伝える)の部分がある。サービスやプロダクトのリリース基準にはクオリティとデザインが、サービスを開発したあとはすぐにマーケティングが大きく関わってくることになるが、今回はそこまで至らなかったために特に話はしていない。ではあるがある程度以下のような話になっていくかと思う。

    クオリティとデザイン

    プロダクトやサービスの特性は大きく以下のような優先度になる。
    機能 > デザイン > ストーリー

    上位を満たせば下位のものは後からでもいい。みたいな理解をしている。要するにこの世に一つしかない機能であれば全然ダメなデザインでも、全くストーリーがなくても使ってくれる人は現れるだろう。

    しかし昨今は、機能的な優位性は速攻で真似される時代になっているため、どのプロダクトもきれいな見た目(デザインをしている)ことが多い。また、macが出てきてからパソコンはどれも同じような見た目になった。SNSだってsnapchatが流行ってfacebook storyが出てきて、顔を加工するカメラだって無限にある。これくらいまでになると機能的な優位性はほぼ差異はなく、デザインはどれも綺麗で、、、となる。すると「自分が、チームが、企業が、何を想って作っているのか」が大切な要因になる。

    私は判断が01になりがちではあるが、この辺りはバランスで、現状のwatariはまったく機能がない。のでまずは主機能を作る。それでまぁ適当なデザインを付けて一度出す。のが目標である。もちろん01ではないので、実装しながらもデザインの概要を考えたり、自分はなぜこんな毎日コードを書いてるのかを自問しながらストーリーを掘り下げる。

    今回のアドベントカレンダーでは機能実装重視だったので

    1. textの投稿機能
    2. 画像の投稿機能
    3. twitter連携

    が出来上がったところだ。まだ全然機能的には足りていない。が、あとはwebサイトを作ってデータを連携して記事になるようにしてみようと思う。

    本質的な痛みの発見・解決まではまだまだ道は長い(と思う)。しかし、自分のサービスを自分で構築して回していければとても楽しいことではないか。とも思っているので、やりながら考えながら進めていければ良い。

    マーケティング

    全然専門ではないので、どうしようかなと思っている部分ではある。

    先日読んだ本にわかりやすく載っていたので噛み砕くと、サービスには「集客、接客、増客」というのがある。マーケティングは「集客」に当たる。こと、アプリやサービスだとデザインをどうするとか機能をどうするとかの話になりがちだが、これらは全て「接客」部分だ。路地裏にカフェをオープンしたら一番にやることは駅前でビラ配りだろう?アプリも同じだ。

    ということで、きっと出来上がったら誰かに使ってもらうように頼んだり、プレス出したり、勉強会で発表したりしていくのが良いのかなぁ。投稿にサービスのURLを含めるというのもありかもしれない。

    その時がきたら考えよう。

    次回は

    クリスマスらしい話をして締めとしよう。

  • watari開発 – Advent201923 –

    watari開発 – Advent201923 –

    stage23: 画像を投稿できるようにする

    お次は撮影した画像をlocalに投稿できるようにしよう。

    ローカルの画像を表示する

    まずは、今リストに表示しているところに選択した画像が出るようにしよう。

    先日修正したところを一箇所戻す。

        func makeUIViewController(context: Context) -> UIImagePickerController {
            let imagePickerController = UIImagePickerController()
            imagePickerController.sourceType = .photoLibrary
    //        imagePickerController.sourceType = .camera
            imagePickerController.delegate = context.coordinator
            return imagePickerController
        }

    こうする。

    で、さらに選択後に保存ボタンを押すとCoreDataに保存できるように変更していく

    Entitesの追加

    file

    EntryImage の追加。bodyはbinaryを保存できるようにしておく。(これ多分設計ミスってる気がする。後で直そう。多分 Entryにして全部一つで管理した方が楽)

    ImageViewの改修

    import CoreData
    ...
        @Environment(\.managedObjectContext) var managedObjectContext
        @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    ...
                Spacer()
                Button("save image", action: {
                    if let image = self.image {
                        let entryImage = EntryImage(context: self.managedObjectContext)
                        entryImage.body = image.pngData()
                        entryImage.createdAt = Date()
                        do {
                            try self.managedObjectContext.save()
                        } catch {
                            print(error)
                        }
                        self.image = nil
                        self.presentationMode.wrappedValue.dismiss()
                    }
                })

    これで画像を保存することができるようになる。

    ContentViewの改修

    Listの表示内容を変更する。

    // ContentView
    
    ...
    +    @FetchRequest(fetchRequest: getEntryImages()) var entryImages: FetchedResults<EntryImage>
    ...
    // Listの中に
                        ForEach(self.entryImages, id: \.self) {entryImage in
                            VStack {
                                createImage(entryImage)
                                Text("\(entryImage.createdAt!)")
                            }
                        }.onDelete {indexSet in
                            let deleteEntry = self.entryImages[indexSet.first!]
                            self.managedObjectContext.delete(deleteEntry)
    
                            do {
                                try self.managedObjectContext.save()
                            } catch {
                                print(error)
                            }
                        }
    ...
    // 最後の方に
    
    func getEntryImages() -> NSFetchRequest<EntryImage> {
        let request: NSFetchRequest<EntryImage> = EntryImage.fetchRequest()
        let sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: true)
        request.sortDescriptors = [sortDescriptor]
        return request
    }
    
    func createImage(_ entryImage: NSManagedObject) -> some View {
        if let data = entryImage.value(forKey: "body") as? Data {
            return AnyView(Image(uiImage: UIImage(data: data)!)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 100, height: 100, alignment: .top)
                .clipped()
            )
        } else {
            return AnyView(EmptyView().frame(alignment: .top))
        }
    }

    これで保存した内容を表示するようになる。

    結果

    次回は

    おそらくアドベントカレンダーの実装系は最後になりそう。
    twitterへ画像を投稿してみよう!

  • watari開発 – Advent201922 –

    watari開発 – Advent201922 –

    stage22: 写真をとる

    次は写真を撮れるようにしよう。

    plistの追加とコードの修正

    今回はそんなに修正箇所は多くない。

    1. plistの追加
    +       <key>NSCameraUsageDescription</key>
    +       <string>This app uses Camera to save images to create contents in the app.</string>
    1. コード修正

    昨日の ImageView.swift の ImagePickerController あたりに

        func makeUIViewController(context: Context) -> UIImagePickerController {
            let imagePickerController = UIImagePickerController()
            imagePickerController.sourceType = .photoLibrary
    +       imagePickerController.sourceType = .camera
            imagePickerController.delegate = context.coordinator
            return imagePickerController
        }

    これを追加してrebuildすればいい。

    結果

    これが出てきて、

    適当に撮るとこんな感じになる。

    次回は

    ここで撮影した画像を保存するように修正を加えよう。
    カメラ周りのUIとか変えたい気もするけど、、、

  • watari開発 – Advent201921 –

    stage22: 画像投稿機能

    さて、テキスト投稿の機能作成してからしばらく経ってしまったが、次は画像投稿機能だ。順を追ってやっていくことにしよう。

    画像の表示、写真の撮影、写真の保存、twitterへの投稿。
    こんな流れでやっていこう

    画像表示の方針

    localにある画像を表示できるようにしよう。そういえばiPhoneのストレージへのアクセスってどこまでできるのかな。あとアプリにだけ画像を保持しているようなサービスとかあるけど、どっちの設計の方がいいんだろう。

    https://gist.github.com/y-takagi/9f2cea659fb3f55b56aa04530bf0af39

    https://qiita.com/tetsufe/items/9ff5fe190ee190afa1bb

    https://stackoverflow.com/questions/29159432/how-to-save-photos-to-storage-within-app-rather-than-ios-camera-roll

    などをみると

    • CoreDataに突っ込む
    • Document/に突っ込む
    • フォトライブラリに突っ込む
    • (クラウド上のどこかに入れる)

    が打ち手としてはありそうだ。

    いったんフォトライブラリから呼び出してみるか。既に自分のローカルにある画像を上げる必要もあるし。フワッとした設計だけど、

    フォトライブラリ => 画像読み込み => 投稿 => アプリにデータを保存

    かな。こうするとフォトライブラリから画像が削除されたとしてもアプリ内には画像を保持しておくことができる。デメリットはデータ二重になるので圧縮とかしてあげないと肥大化がやばそう。

    よし、フォトライブラリから画像を読み込んで表示してみよう。

    ページの追加

    まず、選んだ画像をpreview的に表示するためのボタンとページを追加しよう。

    file

    Add New Fileをして、SwiftUI Viewを追加する。名前は ImageView.swift とした。

    まずはこの画面がbutton tapの際に表示されるようにしよう。

    // ContentView.swift
    
        var body: some View {
            NavigationView {
                VStack {
                    List {
                        ForEach(self.entryTexts, id: \.self) {entryText in
                            VStack {
                                Text(entryText.body!)
                                Text("\(entryText.createdAt!)")
                            }
    ....
    
                                                      oauthToken: oauthToken,
                                                      oauthTokenSecret: oauthTokenSecret)
                                swifter.postTweet(status: entryText.body!, success: { _ in
                                    print("succeeded.")
                                }, failure: { error in
                                    print("failed.")
                                    print(error)
                                })
                            }
                        })
                        NavigationLink("add image", destination: ImageView())
                        Button("twitter signin", action: {
                            self.provider.getCredentialWith(nil) { credential, error in
                                if error != nil {
                                    // Handle error.
                                }
                                if credential != nil {
                                    Auth.auth().signIn(with: credential!) { result, error in
    
    ....

    NavtionViewとNavigationLinkを追加。遷移先を新しく作ったImageViewにする。

    これで新しいViewを表示することができるようになる。

    フォトライブラリへのアクセス

    https://developer.apple.com/documentation/photokit/requesting_authorization_to_access_photos

    info.plist の NSPhotoLibraryUsageDescription に使用用途を記載する。

    +       <key>NSPhotoLibraryUsageDescription</key>
    +       <string>This app uses Photo Library to make content using images you stored.</string>

    で、ImageViewを以下のようにする。

    import SwiftUI
    import Photos
    
    struct ImageView: View {
        @State var image: UIImage?
        @State var showImagePicker: Bool = false
    
        var body: some View {
            VStack {
                if image != nil {
                    Image(uiImage: image!)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 300, height: 300, alignment: .top)
                        .clipped()
                }
                Button("pick up image", action: {
                    self.showImagePicker = true
                })
            }.sheet(isPresented: $showImagePicker) {
                ImagePickerController(image: self.$image)
            }
        }
    }
    
    struct ImagePickerController: UIViewControllerRepresentable {
        @Binding var image: UIImage?
    
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
    
        func makeUIViewController(context: Context) -> UIImagePickerController {
            let imagePickerController = UIImagePickerController()
            imagePickerController.sourceType = .photoLibrary
            imagePickerController.delegate = context.coordinator
            return imagePickerController
        }
    
        func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
    
        }
    
        class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
            var parent: ImagePickerController
    
            init(_ imagePickerController: ImagePickerController) {
                self.parent = imagePickerController
            }
    
            func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
                defer {
                    picker.dismiss(animated: true)
                }
    
                guard let image = info[.originalImage] as? UIImage else {
                    return
                }
    
                self.parent.image = image
            }
    
            func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
                picker.dismiss(animated: true, completion: nil)
            }
        }
    }
    
    struct ImageView_Previews: PreviewProvider {
        static var previews: some View {
            ImageView()
        }
    }

    これで、画像選択して表示するくらいはできそう。

    実行

    次回は

    写真撮影を行えるようにしていこう。