あぼぼノート

頭の中空っぽ

(未解決)既存プロジェクトでXcode PreviewsにUIが表示されないのでワークアラウンドを試している

iOS #2 Advent Calendar 2020

本当は解決したら書こうと思ったのですが、年を越しそうだったので現状のメモとして記しておきます。

環境

  • Xcode12.0
  • Swift5.3
  • .swiftファイルが700程度あるプロジェクト
  • Build PhasesでSwiftLint, swift-formatなどなどをRun Scriptとして動かしてる
  • Deployment Info targetはiOS12以上

事象

Xcode Previewsでプレビューが表示されない

f:id:aboy_perry:20201225110737p:plain

SwiftUIファイルはなんでも良いと思いますがテンプレートから生成後、コンパイルエラー回避でiOS13以降の修正をしています。

import SwiftUI

struct SwiftUIView: View {
    @available(iOS 13.0.0, *)
    var body: some View {
        Text( /*@START_MENU_TOKEN@*/"Hello, World!" /*@END_MENU_TOKEN@*/)
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    @available(iOS 13.0.0, *)
    static var previews: some View {
        SwiftUIView()
    }
}

メモ

エラーとワークアラウンド

Updating took more than 5 seconds

f:id:aboy_perry:20201225113324p:plain

プレビュー表示の際AppDelegateの application(_:didFinishLaunchingWithOptions:) を通るらしいので、たしかに結構処理が書かれていたので中身を起動できる程度にほぼほぼコメントアウト

ref https://koze.hatenablog.jp/entry/2020/03/09/090000


Automatic preview updating paused

f:id:aboy_perry:20201225113601p:plain

Run Scriptが悪さしてるかもとのことなのでBuild PhasesのRun Scriptのうち起動できる範囲(SwiftLintやswift-formatなど)の「For install buids only」にチェック。

ref https://stackoverflow.com/questions/58490010/swiftui-automatic-preview-updating-paused-always


Cannot preview in this file - Connection interrupted: send previewinstances message to agent

なんかfirebase-ios-sdkが悪さしてる説があるのでバージョンアップしてみる(ただ前段でdidFinishLaunchingWithOptionsの処理をコメントアウトした際にFirebaseApp.configure()もコメントアウトされている)。

f:id:aboy_perry:20201225113957p:plain

ref https://developer.apple.com/forums/thread/662370

ref https://github.com/firebase/firebase-ios-sdk/issues/6574


RemoteHumanReadableError: Connection request timed out

f:id:aboy_perry:20201225114059p:plain

よし、ワカラン


いったんフォーラムで見つけた策を試してみる。 Build Active Architecture Only をNOにして、 Excluded Architectures に arm64 を指定。

変わらず。

ref https://developer.apple.com/forums/thread/657913?answerId=628433022#628433022

というわけで未解決です

同じ問題にぶつかったぜ〜という方や、もし「これじゃね?」と知ってる方がいたら情報お待ちしております 🙏

『プロダクトマネジメント―ビルドトラップを避け顧客に価値を届ける』を読んだ

プロダクトとは価値を顧客に運ぶことで、プロダクトマネージャーはプロダクト戦略をたてる。なぜこのプロダクトをつくっているのか、どんなアウトカムを生み出すのかをチームが忘れないようにする。

アウトプットではなくアウトカム志向で、というのはなるほど〜と思った。

組織やプロダクトマネージャーのアンチパターン(というかプロダクト主導じゃないパターン)が書かれてあってあの会社はどれに当てはまるかな、あの人はどうかな、とか考えながら読める。

プロダクトマネージャー、プロジェクトマネージャー、プロダクトオーナーの違いがわかる。

後半はもっと面白い。価値を顧客に運ぶためにチームメンバーやステークホルダーがどう思考してどう実験していくかのストーリーが面白い。プロダクトにとっての顧客に価値を運ぶことに直接関係のないことが一切出てこないのが最高だった。スクラムイテレーション〜やレトロスペクティブ〜みたいな話が出てこずひたすらアウトカム志向でどう顧客の課題を知りどう実験していくかに集中できる本。

www.amazon.co.jp

『エンジニアの知的生産術』を読んだ

社会人にとってやる気は貴重なリソース、というのはマジでそうだなと思った。

3,4章を読んで、自分には本をあまり時間をかけずに一度読んで、その後他の本を読むときとか何かしらのアウトプットをするときに何となくキーワードをもとに引っ張ってこれるくらいでいいかなと思った。何度も読む前提の読み方。情報は必要なタイミングで必要になるだろうから。輪読会でいくらメモしたり議論しながら読んでも1ヶ月後にはほとんど覚えてないので。とにかくたくさん自分の頭の中の棚に置いておく必要があるなと。

5,6章あたりはKJ法よさそう。付箋にとにかく書き出してボトムアップで分類していくのはなるほどと思った。

最後に学ぶ戦略というかT型人材とかπ型人材に触れてておぉ〜ってなった。あと未来に向かって点は繋げられない、過去を振り返って点を繋ぐしかできないから今やってるのを信じろみたいな話もおぉ〜てなった。

www.amazon.co.jp

複数のspiderで異なるpipelineを通す

scrapyは使用するpipelineを全て定義する必要があり、普通に書くとどのspiderでも定義した全てのpipelineを通るようになっている。これをそれぞれのspiderで、指定したpipelineだけを通るようにする実装のメモ。

例として、slack_bottweet_botの2つのspiderを定義する。

import scrapy


class SlackBotSpider(scrapy.Spider):
    name = 'slack_bot' # この値を判定につかう

    def parse(self, response):
import scrapy


class TweetBotSpider(scrapy.Spider):
    name = 'tweet_bot' # この値を判定につかう

    def parse(self, response):

slack_bottweet_botそれぞれ専用のpipelineを1つ、共通のpipelineを1つ定義したとすると、以下のように専用のpipelineのprocess_item内で、spider.nameの値をチェックすれば良い。

class SlackPipeline:
    def process_item(self, item, spider):
        if spider.name not in ['slack_bot']:
            return item

        print('SlackPipeline')
        yield item

~~~省略~~~

class TweetPipeline:
    def process_item(self, item, spider):
        if spider.name not in ['twitter_bot']:
            return item

        print('TweetPipeline')
        yield item

~~~省略~~~

class SaveFilePipeline:
    def process_item(self, item, spider):
        print('SaveFilePipeline')
        yield item

settings.pyには全てのpipelineを列挙する必要がある。

ITEM_PIPELINES = {
    'my_crawler.pipelines.SlackPipeline': 300,
    'my_crawler.pipelines.TweetPipeline': 400,
    'my_crawler.pipelines.SaveFilePipeline': 500,
}

これで、slack_bot実行時はSlackPipeline->SaveFilePipeline、tweet_bot実行時はTweetPipeline->SaveFilePipelineを通すことができる(厳密には全てのpipelineのprocess_item()は呼ばれるので、処理をスキップすると言った方が正しい)。

$ scrapy crawl slack_bot
SlackPipeline
SaveFilePipeline

$ scrapy crawl tweet_bot
TweetPipeline
SaveFilePipeline

参考:https://groups.google.com/d/msg/scrapy-users/msKQ7UaYh_E/ee8WSMPRpq0J

Pythonで25時のような表記をdatetimeに変換する

そのままdatetimeにしようとするとエラーが発生する。

import datetime


dt_str = '2020/06/07 25:05'
d = datetime
    .datetime
  .strptime(dt_str, '%Y/%m/%d %H:%M')

# ValueError: time data '2020/06/07 25:05' does not match format '%Y/%m/%d %H:%M'

なので、日付部分をdatetime、時刻部分をtimedeltaとして生成し、両者を足せばOK。

import datetime


dt_str = '2020/06/07 25:05'
d_str = dt_str[:10]  # 2020/06/07
t_str = dt_str[11:]  # 25:05
hour = float(t_str[:2])  # 25
minute = float(t_str[3:])  # 05

d = datetime
  .datetime
    .strptime(d_str, '%Y/%m/%d')  # 2020-06-07 00:00:00

delta = datetime
    .timedelta(hours=hour, minutes=minute)  # 1 day, 1:05:00

dt = d + delta  # 2020-06-08 01:05:00

Python3.7

PythonでTwitter自動投稿botをつくるときに調べたもの

ウェブサイトを定期的にスクレイピングし、自然言語処理をしてその結果をもとにTwitterに投稿するbotをつくった。その際に必要だった技術、調べたもののメモ。

クローラー

scrapy

Pythonクローラーがつくれるフレームワーク。大変お世話になりました。

scrapy-doc-ja.readthedocs.io

qiita.com

今回でいうと自然言語処理をするパイプラインとTwitterに投稿するパイプラインの2つを実装した感じ。

cssセレクタ一覧

scrapy-doc-ja.readthedocs.io

相対パス絶対パスに変換

import scrapy

class MySpider(scrapy.Spider):
    def parse(self, response):
        relative = response.css('a::attr(href)').extract_first()
        absolute = response.urljoin(relative)

ref https://docs.scrapy.org/en/latest/topics/request-response.html#scrapy.http.Response.urljoin

scrapyのロギング最低レベル変更

settings.pyに追記

LOG_LEVEL = "INFO"

ref https://doc-ja-scrapy.readthedocs.io/ja/latest/topics/settings.html#std:setting-LOG_LEVEL

Python言語系

文字列を○文字まで切り取る

# 'あいうえお'
five_str = 'あいうえおかきくけこ'[:5]

文字列をdatetimeに変換

import datetime

date_str = '2020.06.01 12:00'
date = datetime.datetime.strptime(date_str, '%Y.%m.%d %H:%M')

ref Pythonで文字列 <-> 日付(date, datetime) の変換 - Qiita

JST現在時刻をdatetimeで取得

import datetime

dt_jst = datetime.datetime.now(
    datetime.datetime.timezone(datetime.datetime.timedelta(hours=9))
)

ref Pythonで現在時刻・日付・日時を取得 | note.nkmk.me

datetimeの差を求める

タイムゾーンを考慮したdatetimeと、考慮していないdatetimeは計算できない。

import datetime

dt = datetime.datetime.now()
dt1_jst = datetime.datetime.now(
    datetime.datetime.timezone(datetime.timedelta(hours=9))
)
dt2_jst = datetime.datetime.now(
    datetime.datetime.timezone(datetime.timedelta(hours=9))
)


# TypeError: can't subtract offset-naive and offset-aware datetimes
diff = dt - dt1_jst

# OK
diff = dt1_jst - dt2_jst
# diffはtimedeltaオブジェクト
diff.total_seconds() # 0.0

ref python - Can't subtract offset-naive and offset-aware datetimes - Stack Overflow

ref datetime --- 基本的な日付型および時間型 — Python 3.8.3 ドキュメント

docstring

Googleが公開しているdocstringガイドか、numpyというのが主流らしい。

ref styleguide | Style guides for Google-originated open-source projects

ref numpydoc docstring guide — numpydoc v1.1.dev0 Manual

ref [Python]可読性を上げるための、docstringの書き方を学ぶ(NumPyスタイル) - Qiita

カレントディレクトリの絶対パスを取得

import os

path = os.getcwd()

ref os --- 雑多なオペレーティングシステムインタフェース — Python 3.8.3 ドキュメント

自然言語処理

janome.Tokeniserでユーザー辞書を使う

janomeは簡略辞書というフォーマットを使えたため、比較的簡単にユーザー辞書を追加することができました。

from janome.tokenizer import Tokenizer

# uidc.csvの中身の例↓
# あつ森,カスタム名詞,アツモリ
# とたけけ,カスタム名詞,トタケケ
t = Tokenizer(
    udic='udic.csv',
    udic_type='simpledic'
)
t.tokenize(text)

ref Welcome to janome's documentation! (Japanese) — Janome v0.3 documentation (ja)

特定品詞のみ抽出

from janome.tokenizer import Tokenizer

for token in t.tokenize(text):
    if (token.part_of_speech.startswith('名詞,固有名詞')
        or token.part_of_speech.startswith('名詞,一般')
        or token.part_of_speech.startswith('形容詞,自立')
        or token.part_of_speech.startswith('カスタム名詞')):
            print(token.surface)

Twitter API

Twitter API 利用申請手順

全て英語で書かなければならず、さらに○○文字以上の制約がある項目もあり少々面倒。DeepL翻訳にお世話になりました。

www.deepl.com

ref 2020年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説 | 新宿のホームページ制作会社 ITTI(イッティ)

PythonTwitter APIクライアント

いろいろ見つかったが今回はこれを使用。

ref https://pypi.org/project/twitter/

Heroku連携系

秘匿情報をHerokuのConfig Varsから注入

Twitter APIの認証情報などはソースコードにベタ書きしたくないため、Herokuの機構をつかって環境変数にいれます。Pythonから環境変数を取得する方法は下記のとおり。

import os

os.environ.get('SECRET_VALUE', 'default value')

ref https://devcenter.heroku.com/articles/getting-started-with-python#define-config-vars

Heroku Schedulerで定期実行

Herokuのアドオンで追加できる。Dyno Sizeが無料だと10分ごと、○時間ごと、○日ごと、の3パターンが使える。

ref https://devcenter.heroku.com/articles/scheduler

Flutterで自作パッケージをpub.devに公開する

基本的にDeveloping packages & pluginsを参考に進めればOK。

パッケージを生成する

テンプレートには二種類ある。今回はDart packagesとして作成するので以下のコマンドで生成。

flutter create --template=package {package_name}

生成されたら、lib/{package_name}.dartを編集する。

サンプルプロジェクトを追加する

--template=packageで生成したプロジェクトにはtestしか含まれてないので、必要に応じてサンプルプロジェクトを追加する。

パッケージのカレントディレクトリで以下のコマンドでexampleというプロジェクトを生成する。

# in {package_name}
flutter create example

生成されたら、example/pubspec.yamlのdependenciesに自作パッケージを追加。

dependencies:
  flutter:
    sdk: flutter

  {package_name}:
    path: ../

その後example/main.dartに自作パッケージのサンプル実装を記述する。

README.md, CHANGELOG.md , pubspec.yaml, LICENSEの編集

pub.devに公開するにあたってこれらのファイルを正しく編集する。

基本的にそれぞれデフォルトで例が書かれてあるので参考に。LICENSEは他のパッケージを参考に。

CHANGELOG.md

## [0.0.1] - 2019-11-04

* first release

pub.devに公開

以下のコマンドでpub.devにpublishできる。

flutter packages pub publish

途中認証のためにURLをひらく必要がある。認証に成功すればこんな感じ。

f:id:aboy_perry:20191104165801p:plain
pub.dev認証完了

認証完了後、pub.devにアップロードが成功すると晴れて自作パッケージが公開される。

pub.dev