hiragram no blog

iOSとか

クソアプリ開発は目的ではなく手段

クソアプリ Advent Calendarの16日目です。

先日個人で作ったSushiCameraというアプリをリリースした。

hiragram.hatenablog.jp

個人で作ったアプリがストアリリースまで行ったのは初めてだったので、やる前の気持ちや今の気持ちを雑に書く。


クソアプリ作るぞ〜という気持ちがあって作ったわけではなくて、「カメラ」「画像加工」あたりの技術に触りたかったのでそのために雑に作ったという感じで、大衆にウケようとか収益化しようみたいなことは一切考えてなかった。

もともと今まで関わってた(or関わってる)アプリがステルスリリースだったりファーストリリース前だったりして「私の作品はこれです」といえるものが無かったのもあって、せっかく作るなら他者から見える形で、1つのプロダクトとして最低限の体だけは整えようと思っていた。テーマにした「カメラ」「画像加工」以外の例えばUIとかシェア機能とかそういうのは本当に適当に作ったり全く作らなかったりした。

ある程度作っていろいろ好きに作り込んでみて、満足したら後は適当に整えてストアに申請。何度かリジェクトされたけどなんだかんだ通った。

自分のプロフィール(Wantedlyとか)にストアのURLを載せられるというのはなかなか気分がいい。ストアに出してからはもうコードをいじるモチベーションもそんなになくなってたのでほったらかしていたら、「SushiCameraをみてご連絡しました」という企業からメッセージが来たりした。

のちのち転職とか考えるときに効いてくればいいやーくらいに思ってはいたので、いきなりそういう連絡が来てマジかとなった。たまたまどこかで目に触れて連絡してみようとなったんだとしたら、かなり運が良かったと思う。正直これが自分のなかで成功体験としてかなり強く印象に残っていて、クソでもアプリとして出すことに意味はあるなあとより思うようになった。

自分のスキル/経験を端的に示す意味でも、その技術を扱ったシンプルなアプリを作って置いておくのはポートフォリオ的に使えてよい。それはアプリとしてはクソアプリの域を出なかったとしても、自分のアピールには十分つかえるだろう。

SushiCameraリリース以前は休日にずっとコードを書くみたいなことはあんまりなくて、せいぜい小さいスクリプトを書いて遊んだり、新しいライブラリをちょっと触ってみたりぐらいだったが、最近は結構プロダクトを作るためのコードを書く時間が増えたと思う。一昨日から新しい動画カメラアプリを作り始めた。これは習作というよりはちゃんと作り込んでみたい系のアプリだけど。

僕はクソアプリを作ったぞ〜わいわい〜みたいな楽しみ方は当人としても傍から見てもあんまりおもしろいと思っていないが、客観的にクソに見えるアプリでも、そこに自分にとって有意義なチャレンジとかアウトプットとしての意義がちゃんとあれば、どんどん作って出せばいいのではという視点を得られたよい経験だった。

アプリをつくる、サービスをつくる以外にコードのアウトプットとしてOSSなんかがあげられるけど、これは僕は過去に何度か挑戦しつつもコードを書く、技術力を身につける、ということ以外に考えなきゃいけないことが多くてなかなか大変だなという感じで何度も投げ出している。この辺は向き不向きがあると思うので、僕は今後も習作クソアプリとたまにちゃんとしたアプリをがんばってつくっていく感じの努力を続けていきたいと思う。

結局言いたいことは、クソアプリを出すことは自分の技術と経験をきちんと見える形で残しておくための手段であるということである。

registという単語が含まれていたらビルドエラーにするやつ作った

iOS その3 Advent Calendarの9日目の記事です。

最近、NGワード一覧みたいなのを定義しておいたらコンパイル時にそれがコード中に含まれていないかチェックして含まれてたらビルド落ちるように出来ないかな〜とか思って試してみた。

XcodeのBuild phaseの中でSwiftLintを使うと、warning/errorのある行をXcodeが黄色とか赤にハイライトしてくれるやつ、どうやってるのかなーと思っていろいろ見てみると

/path/to/file:line:何文字目か(?): error: ほげほげ

このフォーマットでエラー出力にprintして、非0なexit statusを返せばXcodeがハイライトしてくれるらしいということがわかった。

"register"を"regist"と間違えて書いてある奴を許せないので、registという単語を使っている行をビルドエラーにするツールをPerlでさくっと書いた。

#!/usr/bin/perl

use strict;
use warnings;

my $current = `pwd`;
chomp($current);
my $root = ".";

my $filelist = &get_files($root);

my $status = 0;
foreach my $file (@$filelist) {
  my @lines = split("\n", `cat $file`);
  my $i = 1;
  foreach my $line (@lines) {
    if ($line =~ /[rR]egist[^e]/) {
      print STDERR "$current/$file:$i:1: error: Registという英単語はない💢😡\n";
      $status = 2;
    }
    $i++;
  }
}

exit($status);

sub get_files {
  my $dir = shift;
  my $filelist = shift;
  opendir(DIR, "$dir");
  my @list = grep /^[^\.]/, readdir DIR;
  foreach my $file (@list) {
    if (-d "$dir/$file") {
      $filelist = &get_files("$dir/$file", $filelist);
    } else {
      if ($file =~ /.swift$/) {
        push @$filelist, "$dir/$file";
      }
    }
  }
  return $filelist;
}

XcodeのRun script phaseに

perl regist.pl

と追加してやると、コード中にあるregistの行で

f:id:hiragram:20161209010504p:plain

エラーとしてハイライトしてくれるようになった。

今後はもっと汎用的なNG単語フィルターツールとしてもうちょっと拡張したい。

iOSアドベントカレンダーなのにPerlしか書いて無くてすみませんでした。

アクセス修飾子周りでSwiftのバグっぽいのみつけた

Swiftのバージョンは3.0

ViewController.swift

import UIKit

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    print(TypeA.name)
  }

}

ProtocolA.swift

import Foundation

private protocol ProtocolA {

}

extension ProtocolA {
  static var name: String {
    return "aaa"
  }
}

struct TypeA: ProtocolA {

}

でビルドしようとするとこんなエラーが出る

Undefined symbols for architecture arm64:
  "protocol witness table for BugTestProj.TypeA : BugTestProj.(ProtocolA in _ED3794DE2981E83EA71158CCFF2975C6) in BugTestProj", referenced from:
      BugTestProj.ViewController.viewDidLoad () -> () in ViewController.o
  "static (extension in BugTestProj):BugTestProj.(ProtocolA in _ED3794DE2981E83EA71158CCFF2975C6).name.getter : Swift.String", referenced from:
      BugTestProj.ViewController.viewDidLoad () -> () in ViewController.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

ProtocolAはprivateなので、そこに定義されてるメソッド/プロパティには外からアクセスできないんじゃないかなーと思うんだけどどうなんだろう。

バグレポートは送った。

ビルドターゲットがフレームワークの時にCommonCryptoをimportしたい

この記事はSwift Advent Calendar 2016の2日目です。

TL;DR

結局イケてる解決はできませんでした

前提

  • UIにかかわらない部分を切り離すため、Embedded Frameworkの仕組みを用いてモデル層を別モジュールにした
  • API通信時にSHA256でトークンを作ってるところがある
  • 切り離し前はアプリターゲットのBridging-headerでCommonCrypto.hを読み込んでいた
  • フレームワークがターゲットのときにはBridging-headerが使えない(仕様)
  • どうしよう

やりかた1: modulemapを自分で書く

[Swift] FrameworkプロジェクトにCommonCryptoをインポートする

これを真似てやったんだけど、Header Search Pathsに$(SDKROOT)/usr/include/CommonCryptoを足してもビルド時にヘッダを見つけてもらえずにコケていた。しばらくコレにしがみついたけど解決しなそうなので次のやり方に。

やりかた2: modulemapに書いてあるヘッダのパスをBuild Phase内で動的に書き換える

やりかた1をいじいじする中で、相対パスで指定するとヘッダを見つけられずにコケるが、絶対パスで指定するとコンパイルが通ることがわかった。

そこで、フレームワークのビルド手順の一番最初に、modulemapに絶対パスを書き出すようなスクリプトを書いた(べた書きだと人によってパスが違うから)。

しかし、直列に実行されている(はず)のビルド手順で、modulemapに書き出した直後の手順でその書き出しが反映されていない事があり、(sleep 10とかやると通る)成功したり失敗したりするのでCIを通せずにボツにした。

やりかた3: modulemapに絶対パスを書き出すところを独立したターゲットにして一番最初に動くようにする

ソースもヘッダもない、Run scriptだけがあるターゲットを作って、すべてのモジュールがそれに依存するように設定した。

これにより、そのターゲットが一番最初にビルドされるようになり、modulemapの書き換えもうまく反映されるのでは、と思った。

が、「ビルドキャッシュがない状態では必ずビルドに失敗する」とかいう状態になったので諦めた。

やりかた4: SDKROOTからヘッダファイルをコピーしてきてプロジェクトに直接突っ込んだ状態でCommonCryptoのフレームワークをビルドする

結局これかという感じになってしまった。

相対パスで探せないなら探せる場所に物理的にコピーしてしまえばいいということで、CommonCrypto用のXcodeプロジェクトを作って、そこにヘッダをコピーしてきて、$ carthage bootstrap --no-skip-currentでビルドした。

Allow non-modular ほげほげみたいなのをYESにして、各種ヘッダをpublicにして、アプリのプロジェクトに組み込んだら、ビルド通った。

結論

結局いけてない解決になってしまった感はあるものの、余計なworkaround(CIを通すために、一旦ビルドキャッシュを作るための100%コケるビルドをしてから本番のビルドをする、みたいな)を解消できたのは良かったのかな、とおもう

SushiCameraとかいうアプリをリリースした

お盆くらいから暇な時間を見つけてはちょこちょこ作ってたカメラアプリがようやくストアレビューに通ったのでリリースした。

iOSの"写真"アプリはGIFの再生をサポートしていないので、レビュアーは静止画が保存されてると勘違いしてたようで、やれシャッター音を鳴らせだの撮影していることをユーザーに明示しろだのごたごたとかみ合わないやり取りをしていたのだが、これ書き出してるのアニGIFやでって教えてやったら速攻審査通った。

今まで個人の開発としてiOSアプリを出したことがなかったので、個人的には1つよい経験をしたなという感じがしている。 とりあえず世に出ているアプリが1個できたので、技術的な挑戦とか実験的な取り組みにどんどん使っていきたい。 直近で興味があるのは、アプリに広告を出すことと、課金周りの仕組みを触ってみること、そんで直接マネタイズが目的ではないもののあわよくばチャリンチャリンしたいなという感じ。

とりあえずフォントを描画する座標がずれるバグは認識したので適当に直す。