面白きことは良きことなり

拙く未熟なiOSエンジニアの備忘録と戯言

OSSのようなFramework ProjectをPlaygroundで動作確認する方法

前説

OSS内にPlaygroundが含まれている場合、そのPlaygroundを弄って動作を確認することは容易です。しかし、Playgroundは、含まれていないけどPlayground上で動作を軽く確認したい!

軽く確認するためにTest Project作ってPodsやCarthageでOSS引っ張ってくるのはしんどいということ…ありませんか?

もしくは、オリジナルの神Frameworkを作ったから、Playground上で動作確認したい…そういうことありませんか?

そういった軽く動きをPlayground上で確認する方法をご紹介します。

材料

  • Playgroundで動作確認したいFramework Project (例 Alamofireや神Framework等)
  • Xcode (Xcode 8系以降ならほぼ同じ手順で可能なはずです。)

今回は、下記環境を使って説明していきます。 * Almofire 4.7.2 * Xcode 9.3

1. Workspaceを作る

既にFramework Projectの方で*.xcworkspace(画像赤い点線) が用意されている場合、それが使えるので「2.」へ。

f:id:aryzae:20180523235302p:plain

WorkspaceがないProjectは、Workspaceを作ります

  1. Workspaceを作りたいFramework Projectを開く
  2. MenuからFile>Save As Workspace...を選択 f:id:aryzae:20180523235028p:plain
  3. Workspaceを保存する f:id:aryzae:20180523235031p:plain

2. Playgroundを作り、Workspaceに追加する

Playgroundを作り、Workspaceに追加してProject上で参照できるようにします。

  1. *.xcworkspaceXcodeで開く
  2. MenuからFile>New>Playground...を選択 f:id:aryzae:20180523235035p:plain
  3. Playgroundを保存する f:id:aryzae:20180523235039p:plain
  4. 開いているWorkspaceに対してMenuからFile>Add Files to "Project Name"...を選択 f:id:aryzae:20180523235043p:plain
  5. 「2.」「3.」で作成したPlaygroundを選択 f:id:aryzae:20180523235048p:plain

3. Framework Projectをbuildと動作確認

今回Playground上で実行させるコードは下記で、簡単に説明すると英語のwikipediaからJSONで情報を取ってくる内容になっています。

import Alamofire

print("=== start ===")
// url
var url = "https://en.wikipedia.org/w/api.php"

// parameters
var parameters: Parameters = [
    "action" : "query",
    "format" : "json",
    "titles" : "oryzae"
]

Alamofire.request(url, method: .get, parameters: parameters)
    .responseJSON { response in
        print(response)
}

print("=== end ===")

しかし、この時点ではまだ下記のようにerrorが出て、Framework Projectで実装されたコードをPlayground上で実行できません。

f:id:aryzae:20180524001045p:plain

Playground上でimportできるようにFrameworkを作成

  1. 任意のSimulatorを選択してbuild
    Generic iOS Deviceの場合、build errorになるので必ずSimulatorを選ぶこと
  2. buildによりFrameworkが作成され、Playgroundでimport&実行が可能に

f:id:aryzae:20180524001533p:plain

EX. Playgroundで非同期処理も実行

Playgroundでerrorが表示されていないので、Alamofireも実行されている!やったね✌️…と、よくみるとAlamofireのresponse内のprintがログに表示されていない。

実はPlaygroundは、上から順に同期処理を行い、最後の行に到達したら処理を終了するため、非同期処理は実行されない。

非同期処理も行わせるためには、下記の// 追加とあるコードをPlaygroundに追加する必要がある。

import Alamofire
import PlaygroundSupport // 追加

// 非同期処理を実行させる
PlaygroundPage.current.needsIndefiniteExecution = true // 追加

print("=== start ===")
// url
var url = "https://en.wikipedia.org/w/api.php"

// parameters
var parameters: Parameters = [
    "action" : "query",
    "format" : "json",
    "titles" : "oryzae"
]

Alamofire.request(url, method: .get, parameters: parameters)
    .responseJSON { response in
        print(response)
}

print("=== end ===")

PlaygroundPage.current.needsIndefiniteExecutionは、名前の通り非同期処理の実行が必要かどうかを持つPropertyである。default値はfalseなので意図的にtrueにしてやる必要がある。

これで、非同期処理含めFrameworkの処理をPlaygroundで実行出来たね。

f:id:aryzae:20180524001933p:plain

iTunes Music Library.xmlのタグ名と項目の値のメモ

iTunes Music Library.xml

  • iTunes Music Library.xmlとは
  • 未入力の項目は、keyが存在しない
  • bool値は<true/>のタグで表される
タグ 補足
Track ID integer 5944
Size integer 36089400
Total Time integer 278293
Disc Number integer 1 Discの分子
Disc Count integer 2 Discの分母
Track Number integer 1 曲数の分子
Track Count integer 12 曲数の分母
BPM integer 100 属性から入力できる項目
Date Modified date 2018-05-05T10:32:15Z
Date Added date 2018-05-05T10:30:07Z
Bit Rate integer 1009
Sample Rate integer 44100
Rating integer 60 ☆による評価 ☆1つで20
Album Rating integer 60 ☆による評価 ☆1つで20
Album Rating Computed bool true
Loved bool true ♡による評価 Dislikedの反対
Disliked bool true ♡による評価 Lovedの反対
Compilation bool true CompilationのCheckbox
Artwork Count integer 1 Artworkの枚数
Persistent ID string AE6D45CB4E7D50E6
Track Type string File
File Folder Count integer -1
Library Folder Count integer -1
Name string 曲名 属性から入力できる項目
Artist string アーティスト名 属性から入力できる項目
Album Artist string アルバムアーティスト 属性から入力できる項目
Composer string 作曲者名 属性から入力できる項目
Album string アルバム名 属性から入力できる項目
Grouping string グループ名 属性から入力できる項目
Genre string ジャンル名 属性から入力できる項目
Kind string Apple Losslessオーディオファイル
Comments string コメント 属性から入力できる項目
Sort Name string きょくめい 読みがな
Sort Album string あるばむめい 読みがな
Sort Artist string あーてぃすとめい 読みがな
Sort Album Artist string あるばむあーてぃすとめい 読みがな
Sort Composer string さっきょくしゃめい 読みがな
Location string file:///Users/aryzae/Desktop/
01%20test%20name.m4a
長いので改行している

Mac AppでWindowからfileやdirectoryを選択してpathを取得するやり方

Mac Appの開発へ乗り出し

iTunesで曲を管理しているが、不便に感じていることがあるため、その辺の利便性をなんとかしたいと思い、Mac Appの開発を気ままに進めることにした。

ひとまずやりたいこと

  • directoryを参照して、その配下にあるfile(subdirectory配下も含む)を列挙

コードと自分向けへの説明

下記のWindowを表示し、そこからdirectoryを選択させたい

f:id:aryzae:20180504152845p:plain

  • NSOpenPanelのbeginメソッドで目的のWindowは表示できる
  • NSOpenPanelはPropetyによって、file選択、複数選択等を切り替えられる
@IBAction func openPanel(_ sender: NSButton) {
    let panel = NSOpenPanel()
    // fileを選択可能にするかどうか。defaultは、true 
    panel.canChooseFiles = false
    // 複数選択可能にするかどうか。defaultは、false
    panel.allowsMultipleSelection = false
    // directoryを選択可能にするかどうか。defaultは、false
    panel.canChooseDirectories = true
    // 開始
    panel.begin { response in
        // OK以外に`cancel`, `abort`, `stop`, `continue`があるが、`OK`, `cancel`以外は異常系
        guard response == .OK else { return }
        // closure内でのみ選択したdirectoryのpathが取れる
        // また、panelの持つurlは、file protocol(file://)がついている 
        self.readItems(url: panel.url)
    }
}

NSOpenPanelで選択したdirectory配下のfileを列挙する

  • 先ほどのコードでdirectoryのpathまでは取得できるようになったので、ファイルを列挙する
  • FileManagerのenumeratorメソッドで列挙可能
  • enumeratorincludingPropertiesForKeysで、列挙するfileの各種propertyも取捨できるのかと思ったが、後続のresourceValuesの時に指定したら取れているのでObj-C時代は必要だったが、今は不要?
  • enumeratoroptionsで、隠しファイルやsubdirectory配下も含めるかを指定可能
  • enumeratorのclosure部分はerror handlerなので、必要に応じて書く
func readItems(url: URL?) {
    guard let url = url else { return }
    guard let directoryPath = URL(string: url.path) else { return }
    // includingPropertiesForKeysの意味あるのか?
    let enumerator = FileManager.default.enumerator(at: directoryPath,
                                                    includingPropertiesForKeys: [.isDirectoryKey],
                                                    options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants]) { (url, error) -> Bool in
        // closuerは、errorハンドルのみ
        print(error)
        // trueならerrorでも継続、falseなら停止
        return true
    }
    // 列挙
    for element in enumerator! {
        guard let url = element as? URL else { return }
        print(url)
        let resourceValue = try! url.resourceValues(forKeys: [.isDirectoryKey, .nameKey, .isHiddenKey])
        // forKeysで指定していないものは、nilになる
        print(resourceValue.isDirectory)
        print(resourceValue.name)
        print(resourceValue.isHidden)
        print(resourceValue.isPackage)
        print(resourceValue.localizedName)
    }
}

03/18(日)に突発的に見知らぬ場所へ行きたくなり、宮城へ行った(後編)

キツネ村を出た後…

30分くらいあれば回りきれる園内を3時間程堪能したところで、駅へ戻るバスの時間になったので退園した。

これを逃すと500円の乗車賃で済むところが、タクシーを呼んで数倍のお金を払うことになるので…。

で、バスで行きの時に乗った場所、白石城跡へ戻ったところで12時過ぎということもあり、昼ご飯を食べれるところを探し始めた。新幹線で1駅、地方沿線でも数駅で仙台駅まで行けることもあり、とりあえず仙台に電車で向かいつつ、何を食べるかと次どこに行くかを検索した。

初 仙台

13時になるころに仙台駅に着いた。

f:id:aryzae:20180404221619j:plain

お腹もかなり減っており、ガッツリ地方名産品食べるしかないなってことで、ありきたりながら牛タンを利休でいただいた。

f:id:aryzae:20180404221638j:plain

仙台港も近いので、新鮮な魚類を寿司で食いたい気持ちもあったが、それは夜に食べることにした。

仙台うみの杜水族館

次に観光する場所は色々調べたけど、電車しか足がない自分には選択肢が結構限られていた。元来生き物が好きな節があったので、「仙台うみの杜水族館」の名前を見ても聞いたことなかったし、ピンと来なかったが、逆にどんな魚類が飼育されているか気になって向かった。

f:id:aryzae:20180404221850j:plain

記憶違いかもしれないけど、2011/03/11の時に津波で生物がほぼ全滅した場所だったか?(調べたところ若干違い、「マリンピア松島水族館」というのがあったが、津波と老朽化の影響で2015年に閉館し、その後意志を継いで出来たのがこの水族館らしい。)

水族館で魚メインだけど他の生物も…

入ってすぐの場所にでかい水槽に多様の魚類が一緒くたに存在していてなかなか圧巻だった。

youtu.be

アジやらイカやら食卓で馴染みのある魚類からマンボウやらオオカミウオダイオウグソクムシ等テーマがあるのかよくわからないけど、多くの生物がいた。

f:id:aryzae:20180404223120j:plain

海水生物以外にも淡水生物でアリゲーターガーピラルク、コイ、イワナとかもいたりする。そしてなぜかいるメガネカイマン。

f:id:aryzae:20180404223548j:plain

魚介類、爬虫類以外だと、ツメナシカワウソや各種ペンギン、アメリカンビーバーもいる。

youtu.be

f:id:aryzae:20180404224048j:plain

カワウソだとコツメカワウソが割とネットで見かけてもてはやされているが、ツメナシカワウソは数倍でかくて驚いた。そして飼育員のお姉さんが可愛かった。


自分が滞在時間中に、イルカのショー?をやっていたみたいだけど、個人的にイルカは興味の対象外+子連れの家族も多く空いて見れるチャンスと館内を巡っていた。

大きい方ではないが、展示されている種類が豊富だったこともあって、回りきる頃には夕方になっていた。

時間に余裕あれば、温泉でも浸かってから帰ることも考えていたが、突発的に遠征したため、この日が日曜日ということもあり翌日の仕事を考えて帰路につくことにした。

ずんだと寿司

夕飯は寿司の気分で完全に決まっていたので、行ければ漁港付近で寿司屋へと思っていた。しかし、水族館の最寄駅から帰り道と港は逆方向だったので、諦めた。

諦めたというより、港から仙台駅まで遠くないから、駅前でも鮮度良い寿司ネタは十分あるだろうと判断し、調べたところ"鮨勘"というお店が自分の懐事情に合いつつ美味いネタが食べれそうだったので、向かった。

時間も時間なだけに、寿司は持ち帰りで、あとは駅構内で売っていたずんだシェイクを携え新幹線へ乗り込んだ。

f:id:aryzae:20180404225154j:plain

f:id:aryzae:20180404225200j:plain

ずんだと寿司の食い合わせはちょっとアレだが、どちらも美味しかった。

特に寿司はシャリ少なめネタ厚めで満足度が高く、また食べたいくらいだ。

突発的な日帰り旅行を行なったが、気ままで縛られない快適な旅だった。次どこいくか等のリサーチだけは時間かかるため、最低限食べ物と観光地、あとその場所の地理関係くらいはざっくり把握しておいたほうが、より効率的に移動や観光できたかもしれない。

まぁたまには非効率で無駄と思える時間を過ごすのも悪くはないとも思う。

次はGWあたりどこかいけたらなーと思う。観光客で混雑するから、GW明けとかでいけたら最高かもな。

03/18(日)に突発的に見知らぬ場所へ行きたくなり、宮城へ行った(前編)

ことの始まり

03/18(日)、数日前からふとした衝動でぶらりどこか適当な遠くへ行きたいという気持ちに駆られ、ノープラン気味にどこか行こうと思い立った。

で、どこ行こうか考えた結果、動物ではキツネと猛禽類が好きな自分としては、以前に掛川花鳥園でフクロウを堪能したので、国内唯一の蔵王キツネ村に行って見たいと思っていたので、行くことに決めた。残りは本当にノープランで朝1に出て新幹線に乗って宮城県白石蔵王駅に向かった。

f:id:aryzae:20180403011508j:plain

キツネ村入場

白石城跡から蔵王キツネ村まで試験的にバスが運行されており、その試験期間最終日だったのでタクシーで向かわずに済んだのは良かった。(タクシーだと3000〜4000円かかるらしい)

バスの乗る乗客は全部で12〜3人くらいだったが、うち10人くらいは中国人の観光客で、こういうややマイナーな行き先にも来るぐらいだからどこ行ってもいるんだろうなー。

20分かからないくらいだったか、バスで移動し、無事蔵王キツネ村に到着。こちらが入り口…

f:id:aryzae:20180403011821j:plain

……

こちらが入り口。

f:id:aryzae:20180403011832j:plain

意外に自家用車で来てたりタクシーで来てる人も多かった。蔵王といえばスキー場や樹氷でも有名だし、近くには温泉もあるので、そこからの流れで来やすいのもあるのかな。

入場料は大人1000円。まぁ良心的?

チケットを買うときに注意事項として受付のおばちゃんから説明を受ける。曰く「キツネが放たれてる檻に人間が入って行くことになる。近づいて来てもしゃがんだり、手を出さないでください。噛まれて怪我をします。また、しゃがんでスマホやカメラを構えると噛んで壊したりいたずらすることがあります。園内歩いていると後ろからキツネが近づいて来ることがあります。そのときは目を合わせてください。」のようなことを曖昧な記憶ながら言われた。

キツネとの戯れ

ざっくりおおきく分けて園内は3つのスペースがある。

  1. 檻の中に飼われていて、キツネの種類が明記されているエリア
  2. キツネが柵で囲われた広いスペースで放し飼いされており、餌やり(有料)ができるエリア
  3. キツネ以外のなぜかいるモルモットやうさぎ等のエリア
1. 檻の中に飼われていて、キツネの種類が明記されているエリア

f:id:aryzae:20180403012938j:plain

f:id:aryzae:20180403012949j:plain

f:id:aryzae:20180403013010j:plain

2. キツネが柵で囲われた広いスペースで放し飼いされており、餌やり(有料)ができるエリア

f:id:aryzae:20180403013117j:plain

f:id:aryzae:20180403013127j:plain

日差しが気持ちいいぐらいで寝てるやつらばかりだったけど、耳だけこちらに向けてやや警戒してるやつもいたりで可愛かった。園内に100頭以上のキツネがいるらしく、所狭しとそこらじゅうにキツネがいて、時には喧嘩してる場面に遭遇する。

3. キツネ以外のなぜかいるモルモットやうさぎ等のエリア

割愛


1日に1〜2回キツネを抱っこできる時間が設けてあり、自分が滞在中もあったので、なかなか来ることないしせっかくだからと抱っこしてきた(600円)

f:id:aryzae:20180403013811j:plain

蛍光色のは、雨合羽で抱っこしてる時に糞尿することがあるために着せられる。尿はとても臭いらしい。

一部のキツネは抱っこ中にジッとしてるのが嫌なのか、抱っこしている人間が嫌なのか暴れるのもいた。自分が抱っこしたのは、なんか顔からして悟ってる感じで微動だにしなかった。

キツネ村滞在の感想

狭い檻の中に閉じ込められるようにしてただ寝て過ごすキツネがいたり、放し飼いエリアで自由に過ごしているものの餌やりばで餌の取り合いによる喧嘩で激しくもみくちゃに暴れたりしていて、キツネ達から自然界にいるよりもストレスがかかっているように感じられた。

自然界のキツネを詳細に知ってるわけじゃないけど、金稼ぎの道具としてキツネがいいように扱われてるように見える部分があり、気持ちが引く印象を受ける部分もあった。

キツネの中には喧嘩で怪我をして首元がぱっくり裂けて肉が見えてるやつもいたり、頭に怪我の跡が残ってるやつもいた。

f:id:aryzae:20180403014802j:plain

キツネはやっぱり好きだと思うけど、キツネ村に再度行きたいかと聞かれたら即答にしくいくらいには闇を感じた。

また、同じ観光客がキツネが喧嘩してるのを見てキャッキャいいながら笑ってたり、無邪気にかわいい〜って声を上げてるのにも寒気がした。


あぁ自宅でキツネ飼いたい…。 庭とかあるならホンドキツネでもアカキツネでもシロキツネでもいいし、室内ならフェネックとか飼いたい…でもフェネックお高い…。

いつかコキンメフクロウとフェネックを一緒に飼って共演させたいなぁ…(下手したらどっちかが捕食しそうで怖いからできない気もするけど)

03/18(日)に突発的に見知らぬ場所へ行きたくなり、宮城へ行った(後編)へ続く