The Pragmatic Ball boy

iOSを中心にやってる万年球拾いの老害エンジニアメモ

linuxでSwiftのObjCBool.boolValueを呼ぶとerror

不具合

SwiftでObjCBoolのboolValueをこのように呼び出すと

var isDir: ObjCBool = false
let isExist = fm.fileExists(atPath: directory, isDirectory: &isDir)
XCTAssertTrue(isDir.boolValue)

osxだと大丈夫だけどlinux環境だとerrorになる・・

error: value of type 'ObjCBool' (aka 'Bool') has no member 'boolValue'
        XCTAssertTrue(isDir.boolValue)
                      ^~~~~ ~~~~~~~~~

原因

no memberなわけないだろと思ったら、ほんとにno memberだった・・・

ObjCBool: Add boolValue property by spevans · Pull Request #1223 · apple/swift-corelibs-foundation · GitHub

対策

4.1で治ってるので

#if !os(Linux) || swift(>=4.1)
  XCTAssertTrue(isDir.boolValue)
#else   
  XCTAssertTrue(isDir)
#endif

もしくは

たくさんの場所でboolValueを使っている場合はextensionで4.1未満の場合にboolValueを時前で実装してしまう

#if os(Linux) || !swift(>=4.1)
extension ObjCBool {    
    var boolValue: Bool { return Bool(self) }   
}   
#endif

electron-vueのproductionビルドで気をつけるところ

electron-vueでアプリを作ってみて、npm run devで開発環境で動かしたときは動いたけど、npm run buildで本番向けにビルドしたときは動かないみたいなことがちょいちょいあったので、それの対応方法です。

ファイルが見つからない

productionビルドの場合はwebpackでバンドルされてdist以下に配置されるので、devのときとディレクトリ構造が異なってきます。 なので__dirnameなどを使ってファイルのパスを指定していると駄目です。

下に記載のように__staticを用いる必要があります

Main Process · electron-vue

path.join(__static, 'image.png')

ファイルの保存ができない

nedbなどで相対パスでファイルに保存するとproductionビルドでは保存に失敗します。

これは、app.getPath()を使ってplatform固有の保存用のパスを取得する必要があります。

  const userData = app.getPath('userData');
  db = new Datastore({
    filename: `${userData}/db/log.db`,
  });

Macにインストールしようとするとセキュリティエラーがでる

作ったアプリをdmgファイルからインストールしようとすると、登録されていないアプリの警告ダイアログが表示されます。

これを出ないようにするには証明書をインストールしてビルドする必要があります。

Mac App Storeを通さずにアプリケーションを配布する手順

Apple Developer ProgramからDeveloper ID ApplicationとDeveloper ID Installerを作ってビルドするマシンに入れれば、あとはnpm run buildするとOK

Windows用のアプリが生成されない

デフォルトではMac版しかないので自分で追加する必要があります

package.jsonのscriptsにwindows向けのを追加します。

 "scripts": {
    "build:win": "node .electron-vue/build.js && electron-builder -w",

Macだとビルドするにはwineをインストールする必要がありますので、homebrewなどを使ってインストールしておきましょう。

Electronに見えないwebviewを埋め込んで処理させる

すでにWeb版が存在してるけどショボいのでネイティブアプリでいい感じにしたいときがあったりすることもあります。

これを、Electronで作ったネイティブアプリの画面上に見えないwebviewを置くことで実現します。

方法

見えないwebviewの埋め込み

Electronアプリにwebviewを埋め込むことは簡単でただタグを使うだけでできます。

次にこれを非表示にします。 非表示というと普通はdispaly: noneを使いますが、display: noneでは残念ながらwebviewの中身が実行されません。 そこでwidthとheightを0にしてやることで非表示にします。

webviewとアプリとの通信

アプリ(Electronのrendererプロセス)→webview

webviewエレメントのsendというメソッドがあるのでこれを叩くことで、webview側にイベントを送ることができます。

webview側でこのイベントを受け取るには、受け取る用のjsファイルを用意してpreloadを使って読み込ませます。 そしてその受け取る用のjsファイルの中でipcRenderer.onを使ってrendererプロセスからのイベントを受け取ります。

webview->アプリ(Electronのrendererプロセス)

webviewからrendererプロセスへのイベントの送信はipcRenderer.sendToHostを使います。

rendererプロセス側でこのイベントを受け取るにはipc-messageをaddEventListererで登録すればよいです。

具体例

electron-vueでやる場合の例です。

<template>
    <div>
        <webview
                id="webview"
                v-bind:preload="preloadJS()"
                src="https://facebook.com"
                style="width:0;height:0;"
        >
        </webview>
    </div>
</template>
<script>
import path from 'path';
import { remote } from 'electron';

let webview = null;

export default {
  data: function() {
    return {
      userId: '',
      password: '',
    }
  }
  mounted: function() {
    // webviewからのイベントの受信
    webview = document.getElementById('webview');
    webview.addEventListener('ipc-message', event => {
      if (event.channel === 'login') {
        switch (event.args[0]) {
          case "loading":
            break;
          case "loaded":
            break;
          case "success":
            console.log('ログイン成功');
            break;
          case "failure":
            console.log('ログイン失敗');
            break;
        }
      }
    });
  },
  methods: {
    preloadJS() {
      return `file:${path.join(__static, 'fb.js')}`;
    },
    login() {
      // webviewへのイベントの送信
      webview.send('login', this.userId, this.password);
    }
  },
// fb.js
const { ipcRenderer } = require('electron');

document.addEventListener('DOMContentLoaded', event => {
  // rendererプロセスからのイベントの受信
  ipcRenderer.on('login', (event, userId, password) => {
    // DOMをゴニョゴニョしてログインする
  });
});

document.addEventListener('readystatechange', event => {
    if (
      window.location.href ===
      'https://facebook.com/sign'
    ) {
      // rendererプロセスへのイベントの送信
      ipcRenderer.sendToHost('login', 'loaded');
    }
});

MacでIEとEdgeのテスト環境をつくる

MSからVirtualBoxのイメージが配布されているのでそれを使います。

1. VMのダウンロード

ここからVirtualBox用のVMをダウンロード

2. VMの起動

1でダウンロードしたzipを回答すると

  • MSEdge-Win10というフォルダ内にMSEdge-WIn10-disk001.vmdkとMSEdge-Win10.ovfというファイルが生成される

  • VirtualBoxを開いてファイル→仮想アプライアンスのインポートをクリック

  • インポートしたい仮想アプライアンスでMSEdge-Win10.ovfを選択

  • インポートをクリック

で完了です。

3. Macのローカルホストを参照する

EdgeやIEで10.0.2.2:80 とかを開くとlocalhostのページが見れるはずです。

4. hostsの設定

10.0.2.2とかをいれるのは面倒なのでhostsを使います。

まずpowershellを開いて

以下のコマンドを実行して、powershellを管理者権限で開きます

Start-Process powershell -Verb runAs

開いたpowershellで以下のコマンドを実行してhostsファイルを開きます

start notepad "C:\Windows\System32\drivers\etc\hosts"

hostsに設定を書いて終了です。

10.0.2.2 hoge.com

Prettierで.vueをフォーマット

Prettierのv1.10でVueのsingle file componentをformatできるようになりました。

それまではeslintででたエラーを人手でポチポチ直してたのでこの辺りが自動化できるのは最高です。

やることはすでにprettier導入済みであれば*.vueを対象のファイルにいれるだけです。

// package.json

"format": "./node_modules/.bin/prettier --write \"js/**/*.{js,vue}\"",

lint-stagedを使ってprecommit hookしてる場合も同様です

  "lint-staged": {
    "gitDir": "../",
    "*.{js,vue}": [
      "yarn format",
      "git add"
    ],

ReactでESCキーを押したときにモーダルを閉じる

このようにdivにonKeyDownをつけた場合、キーを押してもonKeyDownは呼ばれません。

<div
  onKeyDown={this.onKeyDown}
>

tabIndexをつけて要素にフォーカスを持つことができるようにすれば解決します。

<div
  onKeyDown={this.onKeyDown}
  tabIndex="0"
>

SwiftTweets 2018 Winterの資料のまとめ

SwiftTweets 2018 Winterの資料のまとめです。

https://swift-tweets.github.io/2018-winter

発表

どうやってSwiftのOSSをメンテナンスしていくか

https://twitter.com/ikesyo/status/954687472369201155

Swift Foundationにコントリビュートする

https://twitter.com/takasek/status/954696314171740160 https://qiita.com/takasek/items/01f7746bf444bd5c85c0

LT

iOSじゃないところでSwiftを使う

https://twitter.com/hiragram/status/954699064855076864

世界をプログラミングで満たしたい

https://twitter.com/koher/status/954702886935527424

2017振り返り

2017年の振り返りです。

仕事

これまで長らくiOSをやっていましたが、4月からフロントエンド担当になりYADOKARIというメディアを開発してます。

React.jsとかVue.jsを使ったりしてますが、ES6時代だとそんなにJavaScriptについての学習コストがかからないので iOSアプリのエンジニアからのコンバートは割と楽でした。

cssのほうがむしろハードルが高かった気がしてます。(精神的にも)

アウトプット

Swiftの排他制御についてあまり詳しい情報なかったので書きました。 明日から使えない!Swiftの排他制御 | Supership Tech Blog

プライベート

子供が一歳になりました。 今年は勉強会とかもいっさい参加せず、子育てに全てを費やしました。

勉強とかはいつでもできますけど、子育ては今しかできないので最優先です。

まとめ

今年はフロントエンドと子育ての一年だったなぁといった感じでした。

正直iOSはここ最近そんなに新しいことが少なくなってきてて学びはあんまりないなぁと思っていたのでちょうどよかったです。フロントエンドから学ぶことも多々ありますし。

来年も子供の圧倒的な成長を見習いつつがんばりたいです。

Vue.js Tokyo v-meetup #6 資料まとめ

v-meetup #6の資料のまとめ

イベントページ

Vue.js Tokyo v-meetup #6 - connpass

資料

speakerdeck.com

docs.google.com

github.com

docs.google.com

speakerdeck.com

potatotips #44 iOS資料まとめ

Togetter

togetter.com

資料

speakerdeck.com

speakerdeck.com

speakerdeck.com

speakerdeck.com

www.slideshare.net

speakerdeck.com

speakerdeck.com

speakerdeck.com

Jestで環境変数を設定する

JestでテストするときにwebpackのDefinePluginなどで環境変数をセットしている場合、テスト時にもセットしたいときがあります。

package.jsonのjestのところにglobalsを追加することで設定できます。

Configuring Jest · Jest

EXAMPLE

package.json

"jest": {
  "globals": {
    "ENDPOINT1": "http://api.example.com",
  }
}

ルートVueインスタンスのプロパティ

こんな感じにroot Vue instanceのpropsを使って初期値とかを与えたい場合があったりします。

  • html
<div id='root' props='initial-value'>
const vm = new Vue({
    el: '#root',
    props: ['initialValue'],
    data: {
        value: '',
    },
    beforeMount: function() {
        this.value = this.initialValue;
    }
});

が、これだとthis.initialValueはundefinedになります。

原因は、propsは親から子にデータを渡すためのもので、親のないroot vue instanceでは使えないようでした

Passing props to root instance · Issue #6440 · vuejs/vue · GitHub

対策1

真っ当な対策としてはroot vlue instanceのpropsを使って値を渡すのではなく、 rootに子コンポーネントを作って、子コンポーネントのpropsを使うです。

対策2

どうしてもrootで渡したい場合は、強引なやり方ですが、elementのattributesから取ってくれば一応とれます

const vm = new Vue({
    el: '#root',
    data: {
        value: '',
    }
    beforeMount: function() {
        this.value = this.$el.attributes['initial-value'].value;
    }
});

slackのbotでmentionが飛ばせなくなった対応

9/11からslackのusername指定が使えなくなりました。

A lingering farewell to the username | Slack

これにより、botなどでユーザーにメンション飛ばしていた場合にusernameを使っているとmetionが飛ばなくなります。

対応方法

<@username><@userID>に変更するだけです。

userIDを取得方法

以下からユーザーリストを取得してIDを取ってきます。

https://slack.com/api/users.list?token=<token>

Markdown Night 2017 Summer 資料まとめ

Markdown Night 2017 Summerの発表資料のまとめです。

イベントページ

connpass.com

ハッシュタグ

twitter.com

資料

なぜMarkdownは拡張されるのか

Markdownはなぜ拡張され続けるのか | bitjourney Kibela

esa Markdownの思想とデザイン

esa-pages.io

Markdownを拡張する話

speakerdeck.com

Qiita/Qiita:TeamにおけるMarkdownレンダリングの歴史

speakerdeck.com

TBD in Markdown Night

speakerdeck.com

MARKDOWNの本を一緒に作りたい

gitpitch.com

Vagrantでwebpackのwatchが動かない

Vagrant上でwebpackのwatchオプションを使ってファイルの変更を監視しても、変更を検知しないという現象に出くわしました。

こちらに(Webpack Watch in Vagrant/Docker )書いてあるように

webpackのconfigファイルに以下のようにpollingするようにする必要があります。

watchOptions: {
  poll: true
}