Android Kotlin BaseAdapterでListViewを表示

やりたいこと
・定義したクラスのオブジェクトのリスト表示がしたい
・ネット上の画像を表示したい

RestAPIリクエストしてレスポンスを表示するようなありがちな形をイメージしてる
また、画像の表示にはPicassoというのが使ってる人が多そうなので使ってみる

以下のクラスのインスタンスのリストをListViewに表示する。imageUrlにはURL文字列を格納

// User.kt
class User(
  val id: Int,
  val name: String,
  val imageUrl: String,
  val comment: String
)

app/build.gradle

dependencies {
    ・・・
    implementation 'com.squareup.picasso:picasso:2.71828'
}

MainActivity

package com.example.list

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.example.list.Adapter.UserAdapter
import com.example.list.Model.User
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    var users = mutableListOf<User>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

	val imageURL = "https://example.com/path/to/img.jpg"
        val commnet = "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト"

	users.add(User(1, "ユーザー1", imageURL, commnet))
        users.add(User(2, "ユーザー2", imageURL, commnet))
        users.add(User(3, "ユーザー3", imageURL, commnet))
        users.add(User(5, "ユーザー4", imageURL, commnet))
        users.add(User(6, "ユーザー5", imageURL, commnet))

        listView.adapter = UserAdapter(this, users)
    }
}

res/layout/activity_main.xml

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

res/layout/user_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="3sp">

    <ImageView
        android:id="@+id/thumbnail"
        android:padding="5sp"
        android:layout_width="100sp"
	android:layout_height="100sp" />

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
	android:layout_toRightOf="@+id/thumbnail"
        android:textColor="#000"
        android:textStyle="bold"
	android:textSize="16sp"
        android:layout_marginTop="5sp"
        android:layout_marginLeft="5sp"/>

    <TextView
        android:id="@+id/comment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/name"
        android:layout_toRightOf="@+id/thumbnail"
        android:textColor="#000"
	android:textSize="14sp"
        android:padding="5sp"/>
</RelativeLayout>

Adapter/UserAdapter.kt

package com.example.list.Adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import com.example.list.Model.User
import com.example.list.R
import com.squareup.picasso.Picasso
import com.squareup.picasso.PicassoProvider
import kotlinx.android.synthetic.main.user_item.view.*

class UserAdapter(val context: Context, val users: List<User>): BaseAdapter() {

    val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

    override fun getCount(): Int {
        return users.count()
    }

    override fun getItem(position: Int): User {
        return users[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
	val view = layoutInflater.inflate(R.layout.user_item, parent, false)
	Picasso.get().load(users[position].imageUrl).into(view.thumbnail)
	view.name.text = users[position].name
	view.comment.text = users[position].comment
	return view
    }
}

次回コピペで使えるようにメモした。以上

【Android】Kotlin Moshi使い方メモ JSONをオブジェクトに変換

はじめに

moshiでjsonをオブジェクトに変換するコードをメモしておく。今回扱いたいのはクラスは以下のようなクラス
・別クラスのListのプロパティがある
・null許容のプロパティがある
・Date型のプロパティがある

swiftでcodableと同じようなことをやりたかった

実装

app/build.gradle

dependencies {
    ・・・
    implementation 'com.squareup.moshi:moshi:1.8.0'
    implementation("com.squareup.moshi:moshi-kotlin:1.8.0")
    implementation 'com.squareup.moshi:moshi-adapters:1.8.0'
}

データクラス

class User(
        val id: Int,
        val name: String,
        val comment: String?,
        val images: List<UserImage>,
        val created: Date
)

class UserImage(val url: String)

MainActivity

package xxx.yyy.zzz

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import java.lang.reflect.Type
import java.util.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val json = """
            [{
                "id":1,
                "name":"user1",
                "images":[
                    {"url":"http://example.com/path/to/img1"},
                    {"url":"http://example.com/path/to/img2"}
                ],
                "created": "1985-04-12T23:20:50.52Z"
            }, {
                "id":2,
                "name":"user2",
                "images":[
                    {"url":"http://example.com/path/to/img1"},
                    {"url":"http://example.com/path/to/img2"}
                ],
                "created": "1985-04-12T23:20:50.52Z"
            }]
        """.trimIndent()

	val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).add(Date::class.java, Rfc3339DateJsonAdapter()).build()
        val type: Type = Types.newParameterizedType(List::class.java, User::class.java)
        val campaigns = moshi.adapter<List<User>>(type).fromJson(json)

        campaigns?.forEach {
            Log.d("", "%d, %s, %s, %s".format(it.id, it.name, it.comment, it.created))
        }
    }
}

以上です

androidでタブバー、BottomNavigationViewの使い方メモ

BottomNavigationViewを使ってタブバーを設置する手順をメモしておく

app/build.gradle

まずはサポートライブラリーを追加

dependencies {
    ・・・
    implementation 'com.android.support:design:28.0.0'
}
menu/item_bottom_navigation.xml

タブの基本的な設定を行う
アイコンは自分で用意した画像を使うように

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/tab1"
        android:icon="@drawable/tab2"
        android:title="タブ1" />
    <item android:id="@+id/tab2"
        android:icon="@drawable/tab2"
        android:title="タブ2" />
</menu>
values/dimens.xml

タブの文字サイズを設定

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="design_bottom_navigation_text_size">10sp</dimen>
    <dimen name="design_bottom_navigation_active_text_size">10sp</dimen>
</resources>
values/color.xml

タブバーの背景色を定義(colorTabBar)
タブの色も変更(colorPrimary)※今回はこれで十分だた

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#050f52</color>
    ・・・
    <color name="colorTabBar">#f0f8ff</color>
</resources>
layout/activity_main.xml

タブバー追加
タイトルを常に表示(labelVisibilityMode)
タブバーの背景色とエフェクトの無効化(itemBackground)

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    // ここにタブを選択したときのViewを配置

    <android.support.design.widget.BottomNavigationView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:labelVisibilityMode="labeled"
        app:itemBackground="@color/colorTabBar"
        app:menu="@menu/item_bottom_navigation" />

</android.support.design.widget.CoordinatorLayout>
MainActivity.kt

タブが選択されたときに表示するfragmentセット

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bottom_navigation.setOnNavigationItemSelectedListener { item ->
            val transaction = supportFragmentManager.beginTransaction()
            when(item.itemId) {
                R.id.tab1 -> {
                    transaction.replace(R.id.frameLayout, MyFragment1()).commit()
                }
                R.id.tab1 -> {
                    transaction.replace(R.id.frameLayout, MyFragment2()).commit()
                }
            }
            true
        }

        supportFragmentManager.beginTransaction().replace(R.id.frameLayout, MyFragment1()).commit()
    }
}

以上です

cordova githubからcordova-androidをインストール

# githubからplatform add
$ cordova platform add https://github.com/apache/cordova-android

# ローカルからplatform add
$ git clone https://github.com/apache/cordova-android /path/to/cordova-android
$ cordova platform add /path/to/cordova-android

こんなことできたのか...以上です

rails5 rspecでテスト書きながらapi開発、circleciも使ってciぽいことしてみる

はじめに

今回やりたいことはざっくり以下
・rails5でrest api実装
・devise_token_authを使って認証機能を実装
rspecでテストコード実装
・circleciで動かす

それぞれ少ししか触れないがなんとなくciぽい雰囲気がわかるように手順メモしておく

事前準備

なにはともあれrailsプロジェクト作成
今回のテストはrspecで書くので--skip-test-unitオプションを指定してtestディレクトリを作らないようにする

$ rails new spec_test --api --skip-test-unit


db作成。ローカルで動かすだけなのでとりあえずsqlite

$ bundle exec rake db:create

ここまでできたらとりあえず動かしてみる。以下のコマンドでサーバー起動して「http://localhost:3000」にアクセスしてページが表示されればok

$ rails s

rspec

bundleインストールしてセットアップ。
factory_botとdatabase_cleanerはたぶん今後最低限使いそうなので一緒にいれておく
factory_botはテストデータの作成に、database_cleanerは登録したテストデータがゴミとして残らないように削除するために使う

$ vi Gemfile
gem 'rspec-rails'
gem 'factory_bot_rails'
gem 'database_cleaner'
・・・

$ bundle install --path=vendor/bundle
$ bundle exec rails g rspec:install

以下を記述しておけば「rails g controller」とかしたときにrailsが自動でrspecのひな形を作ってくれる。
今回はrequest specしか作らないので以下のようにした。

$ vi config/application.rb

・・・

config.generators do |g|
  g.test_framework :rspec,
                   fixtures: false,
                   view_specs: false,
                   helper_specs: false,
                   routing_specs: false,
                   controller_specs: false,
                   request_specs: true
  g.fixture_replacement :factory_bot, dir: "spec/factories"
end

adtabase_cleanerでrspec開始前にテーブルをクリア(truncate)。
各exampleごとにトランザクションを貼ってロールバックする形となる。
今回はexampleごとにテストデータを準備することにしたのでこの形。
参考)https://qiita.com/shoichiimamura/items/25942acc1d1bd78ef9c3

$ vi spec/spec_helper.rb

## 追加
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'

RSpec.configure do |config|

  ・・・

  ## 追加
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end

以下はfactory_botを使うときのおきまりの記述ぽい、たぶん
テストデータを作るときに「FactoryBot::create」と書かないといけないところを「create」と書けるようになる

$ spec/rails_helper.rb
RSpec.configure do |config|

  ・・・

  config.include FactoryBot::Syntax::Methods
end

とりあえずrspecの設定は完了したので1度実行してみる。テストはないがエラーが出ないことを確認しておく

$ ./bin/bundle exec rspec --format documentation
No examples found.

Finished in 0.1503 seconds (files took 1.34 seconds to load)
0 examples, 0 failures

devise_token_auth

今回は以下を実装、テストすることとする
・メールアドレス+パスワードをパラメータでサインアップ
・メールアドレス+パスワードをパラメータでログイン
・ログイン済みでないとアクセスできないエンドポイントの実装
ログイン済みかどうかの判定方法はログインに成功したときにレスポンスヘッダーに認証情報が含まれるので以降のリクエストではその値をリクエストヘッダーに含める形となる


bundleインストール

$ vi Gemfile
gem 'jbuilder', '~> 2.5'
gem 'rack-cors'
gem 'devise'
gem 'devise_token_auth'
・・・

$ bundle install --path=vendor/bundle

以下のコマンドでuserモデルが認証の対象となり、userモデルが作られる

$ rails g devise_token_auth:install User auth

また、今回はsignup後にメールアドレスが存在するかのチェックまでは行わないので以下のようにconfirmableの部分をコメントアウトして無効とする

$ vi db/migrate/xxx_devise_token_auth_create_users.rb
・・・
      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

・・・

    # add_index :users, :confirmation_token,   unique: true

・・・

$ ./bin/rake db:migrate

実装

devise_token_authを使うと以下のapiが自前で実装しなくても作られるので今回はそれのテストコードを書く。
signin : POST /auth/sign_in
signup : POST /auth

もちろんsignin、signup以外の機能も作られるがいっぱいあるのでここでは扱わない

まずはFactoryBotで生成するテストデータはspec/factories以下に定義する

$ vi spec/factories/users.rb

FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "test_#{n}" }
    sequence(:email) { |n| "test+#{n}@example.com" }
    password { 'password' }
  end
end

コードの中「create(:user)」 と記述すればこれを使って以下のようなテストデータを生成してくれる
nの部分は連番でcreateしたrspect内でcreateした回分インクリメントされていく

{ name: "test_0", email: "test+0@example.com", password: "password" }
signupのテスト(spec)

request specはrequests以下に実装していく。
コードみればなんとなくわかりそうだが、リクエストしてレスポンスが期待値どおりか検証するコードを書いていく
「let!(:user) {create(:user)}」で定義したタイミングでFatoryBotでuserのテストデータが1件作られる
なので、実際に/auth/sign_inにリクエストするときにDBに必ず存在するユーザーでログインのテストする形となる

requests/auth_spec.rb

require 'rails_helper'

RSpec.describe 'Auth', type: :request do

  let!(:user) {create(:user)}

  describe 'POST /auth/sign_in' do
    context '正常' do
      before do
        post '/auth/sign_in', params: {email:user['email'], password: 'password'}
      end

      example 'HTTPステータスが200であること' do
        expect(response.status).to eq(200)
      end

      example 'レスポンスが正しいこと' do
        json = JSON.parse(response.body)
        expect(json['data']['email']).to eq(user['email'])
      end
    end

    context '異常' do
      before do
        post '/auth/sign_in', params: {email:user['email'], password: 'passwordxxx'}
      end

      example 'HTTPステータスが401であること' do
        expect(response.status).to eq(401)
      end

      example 'レスポンスが正しいこと' do
        json = JSON.parse(response.body)
        expect(json['success']).to eq(false)
      end
    end
  end

end

テストを実行する

$ bundle exec rspec --format documentation spec/requests/auth_spec.rb
Auth
  POST /auth/sign_in
    正常
      HTTPステータスが200であること
      レスポンスが正しいこと
    異常
      HTTPステータスが401であること
      レスポンスが正しいこと

上記のようになり期待通りの動作であることが確認できた

signinのテスト(spec)

Signupと同じようにテストを書く

RSpec.describe 'Auth', type: :request do

  ・・・

  describe 'POST /auth' do
    context '正常' do
      let(:params) {{email:'test@example.com', password:'password'}}
      before do
        post '/auth', params: params
      end

      example 'HTTPステータスが200であること' do
        expect(response.status).to eq(200)
      end

      example 'レスポンスが正しいこと' do
        expect(JSON.parse(response.body)['status']).to eq("success")
      end
    end
  end
end

テストを実行する

$ bundle exec rspec --format documentation spec/requests/auth_spec.rb
Auth

  ・・・

  POST /auth
    正常
      HTTPステータスが200であること
      レスポンスが正しいこと
認証が必要なapi

devise_token_authでの認証処理は
ログインに成功するとレスポンスヘッダーにuid、client、access-token返す
認証が必要なapiへリクエストする際に取得したヘッダーをリクエストヘッダーに含めてリクエストする

認証に成功すればcurrent_userに認証に成功したuserモデルがセットされるのでログイン中かどうかの判定ができる
/usersにリクエストして認証済みなら本人の情報をかえすapiとする

controllers/users_controller.rb

class UsersController < ApplicationController
  def index
    if current_user.nil?
      render status: 401, json: {status: 401} if current_user.nil?
    else
      render json: current_user
    end
  end
end

config/routes.rb

Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'auth'
    get '/users', to: 'users#index'
end

続いてテストコード

/usersにリクエストする前にログインのapiへリクエスト後に取得したヘッダーを付与してリクエストするようにする

spec/requests/users_spec.rb

require 'rails_helper'

RSpec.describe 'Users', type: :request do

  let!(:user) {create(:user)}

  def auth_headers
    post '/auth/sign_in', params: {email:user['email'], password: 'password'}
    { 'uid'=>response.header['uid'], 'client'=>response.header['client'], 'access-token'=>response.header['access-token'] }
  end

  describe 'GET /users' do
    context '未ログインの場合' do
      example 'HTTPステータスが401であること' do
        get '/users'
        expect(response.status).to eq(401)
      end
    end
    context 'ログインずみの場合' do
      before do
        get '/users', headers: auth_headers
      end
      example 'HTTPステータスが200であること' do
        expect(response.status).to eq(200)
      end
      example 'レスポンスが正しいこと' do
        expect(JSON.parse(response.body)['id']).to eq(user['id'])
      end
    end
  end
end

テスト実行

$ bundle exec rspec --format documentation spec/requests/auth_spec.rb
・・・

結果書かないけどSuccessになるはず

circleci

あとはcircleciの設定すればgithubにプッシュするタイミングでテストを行ってくれる
circleciの挙動確認するために毎回プッシュしなくてもローカルにcircleciをインストールすれば動作確認可能なのでcircleciをインストールする

circleci localインストール

$ curl -o /usr/local/bin/circleci https://circle-downloads.s3.amazonaws.com/releases/build_agent_wrapper/circleci && chmod +x /usr/local/bin/circleci
circleci update

localでcircleci

.circleci/config.yml

version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.4.1-node-browsers
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          key: bundle-{{ checksum "Gemfile.lock" }}
      - run:
          name: bundle install
          command: bundle install --path=vendor/bundle
      - save_cache:
          key: bundle-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle
      - run:
          echo -e "export RAILS_ENV=test" >> $BASH_ENV
      - run:
          name: test
          command: bundle exec rspec --format documentation

ローカルでcircleci実行

$ circleci build .circleci/config.yml
githubへ登録

.gitignoreにvendor/bundle以下を追加してremote urlセットしてコミットしてプッシュ

.gitignore

# bundle
vendor/bundle
circleciで実行

githubとcircleciの画面からポチポチ連携するだけなので省略

ダメだ。。力尽きた。。以上です

参考)
[Rails] rspec + factory_bot + database_cleaner で、APIのテストを書く - Qiita
Rails5へのRspec導入から実行確認まで - Qiita

ios uiwebviewとwkwebviewのローカルストレージの保存先パス

UIWebView

NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* storagePath = [appLibraryFolder stringByAppendingPathComponent:@"Caches"];
// storagePath -> /var/mobile/Containers/Data/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Caches

WkWebView

NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* storagePath = [[NSString alloc] initWithString: [appLibraryFolder stringByAppendingPathComponent:@"WebKit"]];
storagePath = [storagePath stringByAppendingPathComponent:@"WebsiteData/LocalStorage"];
// storagePath -> /var/mobile/Containers/Data/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/WebKit/WebsiteData/LocalStorage

参考
https://github.com/MaKleSoft/cordova-plugin-migrate-localstorage/blob/master/src/ios/MigrateLocalStorage.m

swift 普通のアプリを作るのにつかったライブラリまとめ

はじめに

普通のアプリってなんだと突っ込まれそうだけど...

APIへリクエストしたり
・リスト表示したり
・画像を表示したり
・入力フォーム作ったり

アプリ開発してると自分のスキルだとやはりサードパーティのライブラリに頼りまくることになる。できるだけCocoapodsは使いたくなかったのでCarthageだけで利用可能なライブラリを探した。今回使いたいと思って調べたライブラリは大抵Carthageで利用可能だった。なのでCocoapodsは使わないですんだ。時間が空くとまた同じような目的でライブラリ探しからはじまりそうなのでメモしておく(URLとStarは2019/1/31現在)。

UI系

SwipeMenuViewController

URL : https://github.com/yysskk/SwipeMenuViewController

Star : 612

スワイプでタブ間の画面遷移するUIを実装するために使用。よく見るUIだけど自前で作るとけっこう大変。

ImageSlideshow

URL : https://github.com/zvonicek/ImageSlideshow
Star : 1063

画像のスライドショー系のUIを実装するために使用。スワイプで前後の画面に移動できるのと、画像タップでフルスクリーンになるのもナイス。

SlideMenuControllerSwift

URL : https://github.com/dekatotoro/SlideMenuControllerSwift
Star : 3179

スライドメニューを実装するのに使用。スライドメニューは大抵のアプリで設置されているのでこの手のライブラリは必須かも

TagListView

URL : https://github.com/ElaWorkshop/TagListView
Star : 1687

ブログやインスタの投稿みたいに記事にタグつけしたい場合の入力UIを実装するのに使用。これも自前で実装すると意外に大変だと思うので助かりました。

SVProgressHUD

URL : https://github.com/SVProgressHUD/SVProgressHUD
Star : 11465

WebAPIへリクエストしてレスポンスが返ってくるまでにクルクルするやつを表示するのに使用。更新系のボタンでは2度押し対策にもなる。

ESTabBarController

URL : https://github.com/eggswift/ESTabBarController
Star : 3416

タブバーを装飾するのに使用。真ん中のタブだけ大きくしたり丸くしたりとかいった用途で使わせていただきました。

LUExpandableTableView

URL : https://github.com/LaurentiuUngur/LUExpandableTableView
Star : 94

折り返し可能なテーブルビューを実装するのに使用。スクラッチで実装してもそこまで手間でない気はするが自分で実装すると意外と細かいバグが直すのが...

通信系

Alamofire、AlamofireImage

URL : https://github.com/Alamofire/Alamofire
Star : 30049

APIにリクエストするのに使用。リクエスト、レスポンスヘッダーも簡単だし画像のアップロードも可能。また、AlamofireImageで非同期に画像を表示するのも簡単に実装できる。Alamofireは鉄板なのかな。

SwiftWebSocket

URL : https://github.com/tidwall/SwiftWebSocket
Star : 1150

WebSocketを使うのにシンプルなものを探したら自分の中ではこれが1番使いやすかった。

Reachability.swift

URL : https://github.com/ashleymills/Reachability.swift
Star : 5733

基本的にオンラインで使用するアプリなのでオフラインの場合はユーザー操作させないようにするのに使用した。ネットワーク状態がかわったタイミングをハンドリングできるのでそこに処理を記述するだけで簡単に使えた。

ユーティリティ系

SwiftyJSON

URL : https://github.com/SwiftyJSON/SwiftyJSON
Star : 17975

JSONを簡単に扱うために使用。Codableが出たせいか前ほど使わなかった気がするけど便利。

SwiftDate

URL : https://github.com/malcommac/SwiftDate
Star : 4555

Swift(Objective Cも同じかも)では日付扱うのが何気に大変な印象。そこでこれ使ったらとても便利だった。

Validator

URL : https://github.com/adamwaite/Validator
Star : 1251

入力チェックで使用。コード量もそんなに多くないし軽量。簡単に扱える印象で使いやすかった。

その他、SDK

Bugsnag

URL : https://github.com/bugsnag/bugsnag-cocoa
Star : 121

クラッシュレポート。色々ありそうだけど今回はこれ使った。

Mixpanel

URL : https://github.com/mixpanel/mixpanel-iphone
Star : 808

ユーザーのイベントを解析するやつ

以上です