20190211のiOSに関する記事は14件です。

UITabBarItemのiconにオリジナルな画像を設定する方法(ノンコード)

はじめに

  • ノンコードです。
  • AssetsCatalogを利用します。

できること

  • アイコンを単色塗りつぶしではない画像オリジナルな状態で表示できます。

やりかた

  • Sampleとしてプロジェクト作成時に利用できるTabbed App(テンプレ)を利用しています。
  1. Assets.xccasetsファイルを開きます。(以降InspectorはAttribute Inspectorに設定)
    スクリーンショット 2019-02-11 19.08.09.png

  2. Firstを選択し、Attributes Inspector内の「Render As」をDefaultから「Original Image」に変更します。★1

スクリーンショット 2019-02-11 20.00.29.png
3. Main.StoryBoardを開いてFirst View Controllerを確認すると以下のようになっていれば設定OKです。

First View Controller  Second View Controller(未変更)
スクリーンショット 2019-02-11 19.19.33.png スクリーンショット 2019-02-11 19.19.57.png

★1 で何をしたか

FirstのImageに対してUIImageのプロパティrenderingModeに.alwaysOriginalを設定した状態にしました。
コードでも設定できます。リファレンスを参考にしてください。

公式リファレンス
withRenderingMode
https://developer.apple.com/documentation/uikit/uiimage/1624153-withrenderingmode
RenderingMode
https://developer.apple.com/documentation/uikit/uiimage/renderingmode

まとめ

この方法は、UINavigationBarItemのアイコンなど、単色で塗りつぶされてしまう箇所で利用可能です。
UITabBarItemのアイコンについては動的に変える必要がない場合等、この方法で静的に設定する方が良いと思います。
また、「画像をTabのアイコンに設定したい」というオーダー時は選択時の画像も合わせて用意してもらいましょう。
選択時の画像の設定もStoryBoardから設定可能で、TabBarItemのSelected Imageで設定可能です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebaseを活用してUniversal Linksで他人にデータを渡す

活用した状況

checka!というiOSアプリを開発しています。このアプリはTrelloのような、グループで使えるチェックリスト管理アプリです。このなかで、自分のグループに知人を招待するためにはID文字列を渡す必要がありました。その際にUniversal Linksを使えば簡単に実現できることはわかっていたのですが、

  • バックエンドを用意したくない
  • ドメインもめんどくさいからとりたくない
  • FirebaseHostingやAmazonS3のドメインそのままの感じは嫌だ

という感じでした。そのときにFirebase Dynamic Linksを使えば簡単に、しかもドメインもそれなりのもので実現することができた話です。

TL;DR

  • Dynamic Linksのドメインは *.page.link なのでそれなりのドメインにできる
  • アプリがインストールされている場合はアプリが開かれ、インストールされていない場合のリダイレクト先を設定できる
  • バックエンドいらずでお手軽Universal Linksができる

事前準備

Firebase側

Firebaseのセットアップを行ってください。下記が公式リファレンスなのでこれに従ってやれば詰まることはないかと思います。

https://firebase.google.com/docs/guides/?hl=ja

iOS側

Associated Domainsの設定が必要です。2つやることがあります。

1. AppIDにAssociated Domainsの設定をする

Apple Developerのサイトに行って、対象のApp IDを開いて下記のようにAssociated DomainsをEnabledにしてください。

スクリーンショット 2019-02-11 16.50.32.png

2. XcodeからCapabilitiesでAssociatedDomainsの設定をする

この部分で、AssociatedDomainsをONにしておいてください。Dynamic Linksの設定が終わったら追加で記入します。

スクリーンショット 2019-02-11 16.54.03.png

Dynamic Linksの設定

利用開始

Firebaseのコンソールを開き、対象のプロジェクトを選択して、左側のメニューから「Dynamic Links」を開いてください。この画面はころころ変わりそうな気もするのであれですが、下の画像のような内容が出てると思うので、「始める」を押してください。

スクリーンショット 2019-02-11 16.58.48.png

サブドメインの設定

サブドメインの設定を求められるのでいい感じのサブドメインを決めましょう。checka!の場合は、 checka.page.link です。そのままですね。この部分は早いものがちだと思うので、取られていたら仕方ないという感じですね。

スクリーンショット 2019-02-11 17.01.26.png

Dynamic Linkの作成

新規作成

こんな感じの画面になっていると思うので、「新しいダイナミックリンク」を押してください。

スクリーンショット 2019-02-11 17.04.14.png

パスの設定

Dynamic Linksは短縮URLの作成ができるので、今回の用途的には少し違和感があるかもしれません。URL接頭辞として、すきな文字列を入力しましょう。checka!のグループ参加の場合は /join_group しました。ちなみに、2019/02/11現在ではパスを切ることはできませんので、/group/join にはできませんでした、残念。

スクリーンショット 2019-02-11 17.12.27.png

ディープリンクの設定

今回は使わないので、ディープリンクURLはAppStoreのURLを入れて、リンク名もアプリ名を入れておきましょう。

スクリーンショット 2019-02-11 17.14.53.png

iOS用の設定

下の「ディープリンクをiOSアプリで開く」にチェックを付けて、セレクトボックスから対象のアプリを開きましょう。ここで選択されたアプリのbundleIDが、端末にインストールされているアプリのbunldeIDと異なっていると、Universal Linksによるアプリ起動ができないのでご注意ください。また、AppStoreIDやTeamIDの入力が必要になります。AppStoreIDは、checka!だとhttps://itunes.apple.com/jp/app/id1451433619 の数字の部分、TeamIDはAppleDevelopterのAppIDのPrefixの部分です。

スクリーンショット 2019-02-11 17.16.59.png

選択すると、下記のような項目がでてきます。ここでは、アプリがインストールされていない場合の挙動を設定できます。自分は上の「アプリのAppStoreページ」を選択しましたが、必要であればカスタムURLの設定を行うことができるようです。

スクリーンショット 2019-02-11 17.20.38.png

Android用の設定

Androidでも利用する場合は設定を行う必要があります。今回は割愛します。

詳細オプション

checka! では、一番下のチェックボックスにはチェックを入れています。ここのチェックを入れていない場合、インストールされていない端末で開くと、Firebase側の画面に一度アクセスして、そこにボタンがあり、そのボタンを押すとAppStoreへアクセスすることになってしまい、自分としては微妙でした。よって、チェックをいれています。

スクリーンショット 2019-02-11 17.26.33.png

完了

ここまででDynamic Linksの設定は完了です。ここまでやれば、https://[your subdomain].page.link/apple-app-site-association にアクセスすると、Universal Linksに必要な設定項目がJSONとして返却されているかと思います。

アプリの設定

Associated DomainsのAppLinks設定

先程のXcodeのCapabilitiesの設定を開き、Associated Domainsの項目の+を押して、applinks:[your subdomain].page.linkを追加しましょう。これがないとアプリで起動できません。

試す

Dynamic Linksで設定した項目に応じたURL https://[your subdomain].page.link/[your path] に、今までの設定がされたアプリをインストールした端末でアクセスしてみましょう。アプリが起動するはずです。

ハンドリング

Dynamic LinksのページにはSDKを使ってハンドリングしていますが、今回は不要です。 AppDelegate.swift で下記のような感じでハンドリングできます。

    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
            let incomingURL = userActivity.webpageURL else {
                return false
        }

        // incomingURLをゴニョゴニョする

        return true
    }

checka!では

https://checka.page.link/join_group?id=[id] でアクセスされた場合、パラメータに渡されたidを入力した状態でグループ参加の画面を開いくようにしています。ちなみに、この機能は2019/02/11審査に出したので、執筆時点ではまだリリースされていません。

おわりに

自分としてはとても楽をして、ユーザー体験を高めることができたのでとてもありがたかったです。よければTwitterフォローしてください。

Twitter: @_mogaming

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

three.jsをスマホアプリで動かして暖をとる(react-native-webglの導入手順)

はじめに

この記事では、react-native-webglというライブラリの導入手順について書きます。

react-native-webglは、React NativeWebGLを使うためのライブラリです。
これを使えばネイティブアプリでthree.jsが使えるようになるので、試しに作ってみました!

とりあえずスマホカイロにしてみましたが、誰かかっこいいアプリ作ってくれないかな・・・

iOS: https://itunes.apple.com/us/app/three-js-native/id1447928256
Android: https://play.google.com/store/apps/details?id=com.three

Simulator Screen Shot - iPhone 8 Plus - 2019-02-06 at 20.52.51.png Simulator Screen Shot - iPhone 8 Plus - 2019-02-06 at 21.02.12.png

  • アプリ内の4種類のシーンのうち、Octahedronをたくさん表示するものが一番あったまれます。
  • segments(ポリゴンの分割数)などのパラメータは、最大にするとメモリが足らずアプリが落ちることがありますので、徐々に上げてみてください。

React Nativeの準備

公式のgetting-started通りに準備しましょう。
今回はexpoを使わないので、”Quick Start”ではなく、”Building Projects with Native Code”のタブに従ってください。

react-native-webglのインストール

React Nativeの最新はこの記事の執筆時点で0.58ですが、このバージョンではビルドが通らないため、0.57にします。

# init
react-native init threejsNative
cd threejsNative

# react-nativeをv0.57に下げる
yarn remove react-native
yarn add react-native@0.57

# ネイティブのプロジェクトも新しく作り直す
rm -rf ios android
react-native eject

その後、react-native-webglのパッケージをインストールしてリンクします。

yarn add react-native-webgl
react-native link react-native-webgl

iOS

New Build SystemではGPUImageというプロジェクト周りでエラーが出てしまうため、Legacy Build Systemに変更します。

File -> Workspace Settings... -> Build System -> Legacy Build System

Screen Shot 2019-02-08 at 1.35.54.png
Screen Shot 2019-02-08 at 1.36.12.png

Android

issueを読みつつ諸々を書き換えます。

android/build.gradle
  buildscript {

    ext {

-     minSdkVersion = 16
+     minSdkVersion = 17

    }

    dependencies {

+     classpath 'de.undercouch:gradle-download-task:3.1.2'

    }
node_modules/react-native-webgl/android/src/main/jni/Application.mk
- APP_PLATFORM := android-9
+ APP_PLATFORM := android-16

- APP_STL := gnustl_shared
+ APP_STL := c++_shared

- NDK_TOOLCHAIN_VERSION := 4.9
node_modules/react-native-webgl/android/build.gradle
  task packageRNWebGLLibs(dependsOn: buildRNWebGLLib, type: Copy) {

-   exclude '**/gnustl_shared.so'
+   exclude '**/libc++_shared.so'

  }

  android {

-   buildToolsVersion '25.0.0'
+   buildToolsVersion '27.0.3'

  }

  dependencies {
-   compile "com.facebook.react:react-native:+"  // From node_modules
+   implementation "com.facebook.react:react-native:+"  // From node_modules
  }

exclude '**/libc++_shared.so'でビルドが通らない時はinclude '**/libc++_shared.so'でうまくいく場合があります。

three.jsを読み込む

あとは公式のexampleの通りです。

yarn add three
three.js
const THREE = require("three");
global.THREE = THREE;
if (!window.addEventListener)
    window.addEventListener = () => { };
require("three/examples/js/renderers/Projector");
export default THREE;
App.js
import React from "react";
import { View } from "react-native";
import { WebGLView } from "react-native-webgl";
import THREE from "./three";

export default class App extends React.Component {
  requestId: *;
  componentWillUnmount() {
    cancelAnimationFrame(this.requestId);
  }
  onContextCreate = (gl: WebGLRenderingContext) => {
    const rngl = gl.getExtension("RN");

    const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
    const renderer = new THREE.WebGLRenderer({
      canvas: {
        width,
        height,
        style: {},
        addEventListener: () => {},
        removeEventListener: () => {},
        clientHeight: height
      },
      context: gl
    });
    renderer.setSize(width, height);
    renderer.setClearColor(0x000000, 1);

    let camera, scene;
    let cube;

    function init() {
      // ここにcameraやsceneのコードを書く
      camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
      camera.position.y = 150;
      camera.position.z = 500;
      scene = new THREE.Scene();

      let geometry = new THREE.BoxGeometry(200, 200, 200);
      for (let i = 0; i < geometry.faces.length; i += 2) {
        let hex = Math.random() * 0xffffff;
        geometry.faces[i].color.setHex(hex);
        geometry.faces[i + 1].color.setHex(hex);
      }

      let material = new THREE.MeshBasicMaterial({
        vertexColors: THREE.FaceColors,
        overdraw: 0.5
      });

      cube = new THREE.Mesh(geometry, material);
      cube.position.y = 150;
      scene.add(cube);
    }
    const animate = () => {
      this.requestId = requestAnimationFrame(animate);
      renderer.render(scene, camera);

      // ここにアニメーションのコードを書く
      cube.rotation.y += 0.05;

      gl.flush();
      rngl.endFrame();
    };

    init();
    animate();
  };
  render() {
    return (
      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <WebGLView
          style={{ width: 300, height: 300 }}
          onContextCreate={this.onContextCreate}
        />
      </View>
    );
  }
}

three.jsの部分は通常とほとんど同じように書けます!

実行

iOS/Androidそれぞれでオブジェクトが表示されれば成功です。

react-native run-ios
react-native run-android

まとめ

react-native-webglを使って、スマホアプリでthree.jsを動かす方法について書きました。
three.jsを使ったかっこいいアプリがさらに増えていくと嬉しいですね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSアプリのデバッグ方法まとめ

とりあえずprintしてみる

基本ですね。

print(anyVariant) //変数の中身が見たいとき
print(type(of: anyVariant)) //変数の型が知りたい

中身が見たいときでなくても、そのロジック通ってるか見たいときにも使えます。
原始的なデバッグ法です。
あまりお行儀はよくない扱いみたいで、次に紹介するブレイクポイントを仕込むほうがまっとうなやり方でしょう。

ブレイクポイントを仕込む

Xcodeの機能で、ブレイクポイントというものがあります。
使い方は簡単です。
image.png
↑この例では、15行目にブレイクポイントを仕込んでいます。
行番号をクリックすると、その行にブレイクポイントが設定されます。
画像だと15行の番号が青くなってますね。

作成したプログラム動かすと、そのブレイクポイントに来たときに実行が一時停止して、
その時点の変数についての情報が見れます。

便利なんですが、アプリの動作を一度止めてしまうのは、利点でもあり、欠点でもあります。
たとえば電卓アプリをつくっていて、シミュレータで各ボタンを押したときの変数の状態を見たいときに、
ボタン1つ押すたびに動作が止まってしまうと、いちいち解除してやらないといけないので、めんどくさいです。

sleepを入れたい

デバッグ方法とはちょっと逸れるかもしれませんが、sleep入れたいときないですか?
そういうときはこれで止まります。

処理がx秒止まる
Thread.sleep(forTimeInterval: 3.00) //3秒Wait

ただこのThreadのsleepを使うと、プログラムの処理が全停止します。
たとえばサーバから重いデータをダウンロードするときに、
一旦sleepで待たせて、ダウンロードを完了させて、後続処理につなぎたい、
という風なときは、sleep中はダウンロード処理も止まるので、その用途には使えません。
そんなときは下記で止めるといけました。

x秒止まる(実行中の処理は動作)
RunLoop.current.run(until: Date.init(timeIntervalSinceNow: 3.0))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UITableView の区切り線をカラフルにしたい!

こんな感じにしたい!

colorful_separator2.png

すべての区切り線が同じ色で良い場合

全部同じで良い場合には、tableView.separatorColor で対応できます。
例えば、下記の例では、区切り線は赤色になります。

swift
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.separatorColor = .red
    }

ではそれぞれのセルで異なる色にするにはどうすれば良いか… :thinking:

カスタムセルでやる!

実際のところ、カスタムセルでも区切り線の色を個別に変えることはできなそうです :sob:
なので、自分で区切り線 (っぽい UIview) を作ることで対応します。

下記のように、セルの下部に区切り線に見えるような UIView を配置し、区切り線に見せかけます。

swift
class CustomTableViewCell: UITableViewCell {
    // 区切り線に見せかける view
    private let separator: UIView = {
        let view = UIView()
        return view
    }()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        contentView.addSubview(separator)
        // contentView の最下部に高さの値を小さくして配置することで、区切り線に見せかける
        separator.translatesAutoresizingMaskIntoConstraints = false
        separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
        separator.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
        separator.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        separator.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setColor(color: UIColor) {
        separator.backgroundColor = color
    }
}

あとは、TableViewController 内で…。

tableView の区切り線を非表示にする (tableView.separatorStyle = .none)。

swift
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
        tableView.separatorStyle = .none
    }

カスタムセルを使うようにする。

swift
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell
        cell.textLabel?.text = String(indexPath.row + 1)
        cell.setColor(color: colors[indexPath.row]) // 好きな色を設定する
        return cell
    }

これで、あたかもカラフルな区切り線ができたかのように見せることができます!

終わりに

今回の例のように区切り線をカスタマイズしたいという要望は少ないかもしれません。
ですが、どうしてもそういうデザインにしたいけれども、提供されているプロパティでは対処できない時には、
自分でそう見えるように工夫するという選択肢が頭の中にあると、解決に一歩近づけるのかなと思っています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSでパスワードをPBKDF2でハッシュ化する

パスワードを安全に保存する

iOSでパスワードを保存したい時は、キーチェーンを利用する場合がほとんどだと思います。このキーチェーンはパスワードを「暗号化」して保存する仕組みになっています。

ただ、昨今パスワード漏洩が大きな問題となっている為、やはりクリティカルなパスワードは「ハッシュ化」して保存したいという場合があります。
(つい先日も大規模な漏洩があったので、急遽「パスワードはハッシュ化してくれ」って言われたのがこの記事のきっかけだとか・・・・)

iOSの場合、PBKDF2であれば特に外部のライブラリを使うことなくハッシュ化を実装できます。
ただ、ちょっと実装がいるので記事としてまとめました。

PBKDF2の実装

まずCommonCryptoというライブラリをインポートします。
(現在はこの一文だけでインポートは完了します)

import CommonCrypto

次にPBKDF2の設定値を決めておきます。

let iterations = UInt32(100000)   // イテレーション回数
let prf = kCCPRFHmacAlgSHA256     // 利用するハッシュ関数
let saltLength = 64               // ソルトの長さ
let hashedLength = 256            // 出力されるハッシュの長さ

設定値によってセキュリティの強度が変わるので、ここの設定は案件毎にセキュリティに詳しい人と相談して決めましょう。

なお、ハッシュ関数については、

設定値  
kCCPRFHmacAlgSHA1 HMAC-SHA1
kCCPRFHmacAlgSHA224 HMAC-SHA224
kCCPRFHmacAlgSHA256 HMAC-SHA256
kCCPRFHmacAlgSHA384 HMAC-SHA384
kCCPRFHmacAlgSHA512 HMAC-SHA512

から選択できます。

設定値が決まれば、いよいよハッシュ化の処理です。
ハッシュ化はCCKeyDerivationPBKDFというメソッドを使いますが、元はCのAPIなのでSwiftからはちょっと使いにくいです。
それで、次のようなSwift用にラップしたメソッドを作ります。

func pbkdf2(password: String, salt: Data, iterations: UInt32) -> Data {
    var hashed = Data(count: hashedLength)
    let saltBuffer = [UInt8](salt)

    let result = hashed.withUnsafeMutableBytes { data in
        CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
                             password, password.count,
                             saltBuffer, saltBuffer.count,
                             CCPseudoRandomAlgorithm(prf),
                             iterations,
                             data, hashedLength)
    }

    guard result == kCCSuccess else { fatalError("pbkdf2 error") }
    return hashed
}

また、パスワードをハッシュ化する場合のソルトは、ランダムで生成されたものが良いのでソルトを生成するメソッドも用意します。

func generateSalt(length: Int) -> Data {
    return Data(bytes: (0..<length).map { _ in UInt8.random(in: 0...UInt8.max) })
}

アプリで利用する

アプリ上でハッシュ化してパスワードを保存する場合、

  1. パスワードをハッシュ化する
  2. 入力されたパスワードと保存済のハッシュ化されたパスワードを比較

という2つの処理が必要です。

パスワードをハッシュ化する

ユーザに決めてもらったパスワードを保存する時のコードです。

func encode(password: String) -> (hash: String, salt: String) {
    // ランダムなソルトを生成する
    let salt = generateSalt(length: saltLength)
    // パスワードをハッシュ化する
    let hash = pbkdf2(password: password, salt: salt, iterations: iterations)
    // 保存しやすいようにBase64化しておく
    return (hash: hash.base64EncodedString(), salt: salt.base64EncodedString())
}

アプリから呼び出す時は以下のようになります。

let password = "ユーザが入力したパスワード"
let result = encode(password: password)
print("ハッシュ化されたパスワード: ", result.hash) 
print("ソルト: ", result.salt)

あとは、ハッシュ化されたパスワードとソルトの両方を保存します。
保存先はキーチェーンにしておくとより確実かと思います。

なお、イテレーション回数が変わるとハッシュ化の結果が違ってきますので、もしイテレーション回数を可変する場合はイテレーション回数も保存が必要です。

パスワードを比較する

次に、保存されているパスワードとユーザが入力したパスワードを比較する時のコードです。

func verify(password: String, hash: String, salt: String) -> Bool {
    // Base64化されたソルトをDataに戻す
    guard let saltData = Data(base64Encoded: salt) else { return false }
    // 保存されていたソルトを使って入力したパスワードをハッシュ化する
    let inputHash = pbkdf2(password: password, salt: saltData, iterations: iterations)
    // ハッシュ化されたパスワードと保存済のハッシュ化されたパスワードを比較
    return inputHash.base64EncodedString() == hash
}

アプリから呼び出す時は以下のようになります。

// let savedHash = 保存しておいたハッシュ化されたパスワード(Base64)
// let savedSalt = 保存しておいたソルト(Base64)

let ok = verify(password: "ユーザが入力したパスワード", hash: savedHash, salt: savedSalt)
print("結果: ", ok)   // true
let ng = verify(password: "違うパスワード", hash: savedHash, salt: savedSalt)
print("結果: ", ng)   // false

参考: ハッシュ化は必要か?

一般的にパスワードを安全に保存しておくには「暗号化」ではなく「ハッシュ化」しておくことが必要1だと言われています。

というのも、パスワードが漏れてしまった時に、ハッシュ化していれば簡単に元のパスワードに戻すことができませんが、暗号化したパスワードの場合は、復号する為の鍵も一緒に漏れると簡単に元のパスワードに戻されてしまうからです。

では、ハッシュ化せずにキーチェーンに保存しておくのは安全では無いのか?と言われると、一概にそうは言えません。詳しくは参考資料2やiOSのセキュリティガイドなどを参照していただきたいのですが、キーチェーンのデータの復号用の鍵はアプリ側で保管されていなかったり、暗号化専用のハードが用意されていたりと、暗号化したデータと一緒に鍵も漏れてしまうということが起こりにくいように色々と対策が取られています。

という訳で、一般的な個人のパスワード程度であれば、このキーチェーンへの保存で十分に安全と言えますし、Appleのリファレンスでもパスワードはキーチェーンへ保存するよう記載されています。

ただ、やっぱりパスワード系はハッシュ化していないと心配で眠れないとか、なんか気持ち悪いって場合は、そんなに実装コストのかかるものでも無いのでさくっとハッシュ化してしまいましょう!


  1. ただし、ハッシュ化して保存すると元のパスワードに戻せなくなる為、平文のパスワードが必要な場合はハッシュ化ではなく暗号化での保存が必要です。例えば、Safariのパスワード保存のようにアプリから別サービスへパスワードを使ってログインをするような場合は、暗号化で保存しないといけません。 

  2. https://github.com/OWASP/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutterウィークリー #45

Flutterウィークリーとは?

FlutterファンによるFlutterファンのためのニュースレター
https://flutterweekly.net/

この記事は#45の日本語訳です
https://mailchi.mp/flutterweekly/flutter-weekly-45

※Google翻訳を使って自動翻訳を行っています。翻訳に問題がある箇所を発見しましたら編集リクエストを送っていただければ幸いです。

読み物&チュートリアル

Flutterスプライトシートアニメーション

https://medium.com/flutter-community/sprite-sheet-animations-in-flutter-1b693630bfb3


Luan NicoがFlutterアプリでスプライトシートを使用する方法を紹介します。

紹介: Flutterウィジェット-メーカー、 FlutterのApp-Builderはで書かれたFlutter

https://medium.com/flutter-community/introducing-flutter-widget-maker-a-flutter-app-builder-written-in-flutter-231e8d959348


Norbertは、ビジュアル環境でウィジェットを作成するためのこのツールを作成しました。

クイックヒント:テキストURLをクリック可能なハイパーリンクに変換する

https://medium.com/flutter-community/quicktip-converting-text-urls-into-clickable-hyperlinks-2bb2d3b3b4b2


Darshan KawarがFlutterアプリのURLをリンクするための簡単なコツを紹介します。

dartfmtを使用してコードスタイルを確認してフォーマットする

https://medium.com/@guitcastro/checking-and-format-your-code-style-using-dartfmt-665cc4189223


dartファイルでリンターを実行し、CI経由でGuilherme Torresで実行する方法についての簡単なメモ

Flutter - 開発期間を短縮するためのIDEショートカット

https://medium.com/flutter-community/flutter-ide-shortcuts-for-faster-development-2ef45c51085b


AS用のショートカットのリストをチェックして、 Flutter by Pooja Bhaumikを使った作業を改善してください。

FlutterとWebコードの共有州管理デモ

https://medium.com/flutter-community/flutter-and-web-code-sharing-a-state-management-demo-2e615d3f2b1a


Mellati Meftahが、 Flutter状態を処理しながら、 FlutterとWebの間でコードを共有する方法を紹介します。

FlutterのWebViewのパワー

https://medium.com/flutter-io/the-power-of-webviews-in-flutter-a56234b57df2


FlutterチームのEmily FortunaによるWebView Widgetの詳細な分析。

キャッチャーでFlutterエラーを処理する

https://medium.com/flutter-community/handling-flutter-errors-with-catcher-efce74397862


Jakub Homlala氏は、アプリケーションのエラー処理を支援するための、 FlutterプラグインのCatcherを紹介しています。

アーキテクチャなし

https://buildflutter.com/no-architecture/

例としてFlutterを使用したアーキテクチャを使用しない、Adam Pedleyによるモバイル開発への興味深いアプローチ。

Flutter速い!

https://medium.com/flutter-community/flutter-faster-db1e0fef57ba


Greg Perryは、MVCアーキテクチャをFlutterアプリに適用した経験を共有しています。

Firebase、クラウドストレージ、 Flutter - Flutter Pub - Medium

https://medium.com/flutterpub/firebase-cloud-storage-and-flutter-fa2e91663b95


あなたのFirebase Cloud StorageにFlutterアプリからファイルを保存する方法に関するAseem Wangooによるチュートリアル。

BuiltValueSerializerを使用したカスタムbuilt_valueシリアライザの作成

https://medium.com/@solid.goncalo/creating-custom-built-value-serializers-with-builtvalueserializer-46a52c75d4c5


カスタムシリアライザを作成するGonçaloPalmaによるbuilt_valueの高度な使用法に関する記事。

フレアの逆運動学 - 2次元 - 中

https://medium.com/2dimensions/inverse-kinematics-in-flare-777bbb24bc49


Guido Rossoを使用すると、FlareでのIKを理解して、 Flutterアプリに適したアニメーションを作成できます。

要素、キー、 Flutterのパフォーマンス

https://medium.com/flutter-community/elements-keys-and-flutters-performance-3ef15c90f607


ウィジェットのキーを処理するとパフォーマンスがどのように向上するかについてのTomekPolańskiによる記事。

Flutter + MLKit =❤

https://medium.com/flutter-community/flutter-mlkit-8039ec66b6a


Stefan BlosによるFlutter MLKitの使用に関するチュートリアル

ビデオ&メディア

Flutterローカル認証指紋とFaceID | Dartパッケージ - YouTube

https://www.youtube.com/watch?v=4-P_Su9O5NM


ローカル認証パッケージを使用してFlutterアプリケーションに指紋認証とFaceIDローカル認証を簡単に導入する方法についてのビデオ。

Flutter YouTube検索チュートリアルコース

https://www.youtube.com/playlist?list=PLB6lc7nQ1n4jtXh6TgCEIO4kCfIT0-NZl


Flutter YouTubeの検索機能を模倣する方法についてのReso Coderによるプレイリスト。

Flutterインスペクタを使用してスクロール位置を保存する(The Boring Flutter Development Show、Ep。15) - YouTube

https://www.youtube.com/watch?v=ht76lDzPgUQ&feature=youtu.be&linkId=63306304


The Boring Flutter Development Showのこのエピソードでは、EmilyとLaraが、 Flutter Inspectorと、それがどのように使用されるか、そしてページのスクロール位置を維持する方法について話します。

Flutter UI - クリーンデザイン - ヘアスタイリストアプリ - YouTube

https://www.youtube.com/watch?v=td_wyIn9b3k&feature=youtu.be


今回はヘアスタイリストアプリである、アプリuiの作成に関するRaja Yoganによるチュートリアル。

整列(今週のFlutterウィジェット) - YouTube

https://www.youtube.com/watch?v=g2E7yl3MwMk&t=0s&index=26&list=PLOU2XLYxmsIL0pH0zWe_ZOHgGhZ7UasUE


整列ウィジェットを使用すると、ウィジェットをその親ウィジェットの定義済み領域に配置できます。

折りたたみサイドバーとナビゲーション引き出し| Flutter UI - YouTube

https://www.youtube.com/watch?v=2SjvhAUR9aw&feature=youtu.be


Techie Blossomによる、引き出しとして、または足場本体のどこにでも使用できる折りたたみ式の引き出し/サイドバーの作成方法に関するビデオ。

Flutter使ってGmailを作成する

https://www.youtube.com/playlist?list=PLWIO1jq0WronOimzw5BGlB3F9TU7Celpd


Impatient DeveloperによるFlutter Gmailクローンの作成に関するプレイリスト。

Flutterチュートリアル - Flutter GestureDetectorとInkWell - YouTube

https://www.youtube.com/watch?v=pAA62_x0zKE&feature=youtu.be


Whatsupcodersは、GestureDetectorとInkwellをアプリで使用する方法を私たちに示しています。

Flutterトーク

https://blog.codemagic.io/flutter-talks-podcast-fast-beautiful-productive-open/


Flutter専用の新しいPodcast。この最初の号では、Martin Aguinisへのインタビューやそれ以上のことを聞くことができます。

ライブラリ&コード

mdi-dart

https://github.com/csharad/mdi-dart

Flutter用の自動生成されたMaterial Design Iconパッケージ。

データビュー

https://github.com/synw/dataview

アプリケーションのドキュメントディレクトリ用のファイルエクスプローラ。

flutter_clipper_experiments

https://github.com/spagni/flutter_clipper_experiments

カスタムクリッピング実験をテストするためのFlutterアプリ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftLintの全ルール一覧(Swift 4.2版)

はじめに

すでに SwiftLintのルールをまとめてくださっている方 がいらっしゃいますが、約2年ほど更新されていなかったので、自分でもまとめてみました。

SwiftLintの概要や導入については以下をご参照ください。
Swiftの静的解析ツール「SwiftLint」のセットアップ方法 - Qiita

注意

公式ページのルールを簡単に翻訳してまとめたものです。
英語に慣れている方は公式ページを直接見るのがいいです。
https://github.com/realm/SwiftLint/blob/master/Rules.md

また、間違っている箇所や不適切な箇所がありましたら教えていただけると嬉しいです。

環境

  • Swift:4.2.1
  • Xcode:10.1 (10B61)
  • SwiftLint:0.30.1

Default

デフォルトで有効になっているルールの一覧です。

Block Based KVO

Swift 3.2以降の場合、新しいブロックベースのKVO APIとキーパスを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#block-based-kvo

// bad
class Foo: NSObject {
    override func observeValue(forKeyPath keyPath: String?, of object: Any?,
        change: [NSKeyValueChangeKey: Any]?,
        context: UnsafeMutableRawPointer?) { }
}

// good
let observer = foo.observe(\.value, options: [.new]) { (foo, change) in
    print(change.newValue)
}

Class Delegate Protocol

デリゲートプロトコルはクラスのみであるべきで、弱参照されることができます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#class-delegate-protocol

// bad
protocol FooDelegate { }

// good
protocol FooDelegate: class { }

Closing Brace Spacing

右括弧で閉じ括弧を閉じる場合、間にスペースを含めるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closing-brace-spacing

// bad
[].map({ } )

// good
[].map({ })

Closure Parameter Position

クロージャのパラメータは開き括弧と同じ行にあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-parameter-position

// bad
[1, 2].map {
    number in
    number + 1
}

// good
[1, 2].map { number in
    number + 1
}

Colon

: は、型の指定時には識別子の後ろ、ディクショナリではキーの後ろにあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#colon

// bad
let abc:Void
let abc :Void
let abc : Void
let abc: [String:Int]
let abc: [String :Int]
let abc: [String : Int]

// good
let abc: Void
let abc: [String: Int]

Comma Spacing

カンマの前にスペースがあるべきではなく、カンマの後ろには1つの半角スペースがあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#comma-spacing

// bad
func abc(a: String,b: String) { }
func abc(a: String ,b: String) { }
func abc(a: String , b: String) { }

// good
func abc(a: String, b: String) { }

Compiler Protocol Init

ExpressibleByArrayLiteral のようなコンパイルプロトコルで定義されているイニシャライザは直接呼び出すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#compiler-protocol-init

// bad
let set = Set(arrayLiteral: 1, 2)
let set = Set.init(arrayLiteral: 1, 2)

// good
let set: Set<Int> = [1, 2]
let set = Set(array)

Control Statement

if , for , guard , switch , while , catch 文は条件や引数を不必要に括弧で括るべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#control-statement

// bad
if (condition) { }
for (item in collection) { }
guard (condition) else { }
switch (foo) { }
while (condition) { }
do {
} catch (let error) {
}

// good
if condition { }
for item in collection { }
guard condition else { }
switch foo { }
while condition { }
do {
} catch let error {
}

Custom Rules

正規表現を指定してカスタムルールを作成できます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#custom-rules

Cyclomatic Complexity

関数内は複雑にすべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#cyclomatic-complexity

// bad
func f1() {
    if true {
        if true {
            if false { }
        }
    }
    if false { }
    let i = 0

    switch i {
    case 1: break
    case 2: break
    case 3: break
    case 4: break
    default: break
    }
    for _ in 1...5 {
        guard true else {
            return
        }
    }
}

// good
func f1() {
    if true {
        for _ in 1..5 { } }
    if false { }
}

Deployment Target

可用性のチェックまたは属性は、デプロイメントターゲットが満たす古いバージョンを使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#deployment-target

// bad
@available(iOS 6.0, *)
class A { }

if #available(iOS 6.0, *) { }

// good
@available(iOS 12.0, *)
class A { }

if #available(iOS 12.0, *) { }

Discarded Notification Center Observer

ブロックを使って通知を登録するとき、返される不透明なオブザーバは後で削除できるように格納すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discarded-notification-center-observer

// bad
nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { }

// good
let foo = nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { }

Discouraged Direct Initialization

有害な可能性がある型を直接初期化すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-direct-initialization

// good
let foo = UIDevice.current
let foo = Bundle.main
let foo = Bundle(path: "bar")
let foo = Bundle(identifier: "bar")

// bad
let foo = UIDevice()
let foo = Bundle()

Duplicate Imports

インポートは1回のみ行うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#duplicate-imports

// bad
import Foundation
import Dispatch
import Foundation

// good
import Foundation
import Dispatch

Dynamic Inline

dynamic@inline(__always) を同時に使ってはいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#dynamic-inline

// bad
class C {
    @inline(__always) dynamic func f() { }
}

// good
class C {
    dynamic func f() { }
}
class C {
    @inline(__always) func f() { }
}

Empty Enum Arguments

列挙型が連想型と一致しない場合、引数を省略すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-enum-arguments

// bad
switch foo {
    case .bar(_): break
}
switch foo {
    case .bar(): break
}

// good
switch foo {
    case .bar: break
}

Empty Parameters

Void -> より () -> を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-parameters

// bad
let abc: (Void) -> Void = { }

// good
let abc: () -> Void = { }

Empty Parentheses with Trailing Closure

トレイリングクロージャを使う場合、メソッドの呼び出し後に空の括弧を記述すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-parentheses-with-trailing-closure

// bad
[1, 2].map() { $0 + 1 }

// good
[1, 2].map { $0 + 1 }

File Line Length

ファイル内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#file-line-length

// bad
print("swiftlint")
print("swiftlint")
print("swiftlint")

For Where

for文内にif文が1つのみ存在する場合、 where 句を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#for-where

// bad
for user in users {
    if user.id == 1 {
        user.myFunction()
    }
}

// good
for user in users where user.id == 1 {
    user.myFunction()
}

Force Cast

強制キャスト( as! )は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-cast

// bad
NSNumber() as! Int

// good
NSNumber() as? Int

Force Try

強制トライ( try! )は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-try

func a() throws { }

// bad
try! a()

// good
do {
    try a()
} catch {
    // エラー処理
}

Function Body Length

関数内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-body-length

Function Parameter Count

関数の引数の数は少なくすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-parameter-count

// bad
func f(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) { }

// good
func f(a: Int, b: Int, c: Int, d: Int, e: Int) { }

Generic Type Name

ジェネリック型は英数のみを含み、大文字で始まり、1〜20文字にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#generic-type-name

// bad
func foo<T, U_Foo>(param: U_Foo) -> T { }
func foo<T, u>(param: u) -> T { }

// good
func foo<T, U>(param: U) -> T { }

Identifier Name

識別子名は英数のみを含み、小文字で始まるか、大文字のみを含むべきです。
上記以外では、変数名は静的かつ不変と定義されている場合は大文字から始まることがあります。
変数名は長過ぎたり短過ぎたりしてはいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#identifier-name

// bad
let MyLet = 0
let _myLet = 0
let id = 0

// good
let myLet = 0

Implicit Getter

読取専用のコンピューテッドプロパティとサブスクリプトには get キーワードを使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#implicit-getter

// bad
class Foo {
    var foo: Int {
        get {
            return 20
        }
    }
}

// good
class Foo {
    var foo: Int {
        return 20
    }
}

Inert Defer

defer が親スコープの終わりにある場合、その場所で実行されます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#inert-defer

// bad
func example() {
    print("other code")
    defer { /* deferred code */ }
}

// good
func example() {
    defer { /* deferred code */ }
    print("other code")
}

Is Disjoint

Set.intersection(_:).isEmpty より Set.isDisjoint(with:) を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#is-disjoint

// bad
_ = Set(syntaxKinds).intersection(commentAndStringKindsSet).isEmpty

// good
_ = Set(syntaxKinds).isDisjoint(with: commentAndStringKindsSet)

Large Tuple

タプルはあまりにも多くのメンバーを持つべきではありません。
代わりにカスタムタイプを作成すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#large-tuple

// bad
let foo: (Int, Int, Int)

// good
let foo: (Int, Int)

Leading Whitespace

ファイルは先頭にスペースを含んではいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#leading-whitespace

// bad
 // foo

// good
// foo

Legacy CGGeometry Functions

構造体のエクステンションのプロパティとメソッドは、従来の関数より優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-cggeometry-functions

// bad
CGRectGetWidth(rect)
CGRectGetHeight(rect)
CGRectGetMinX(rect)
CGRectGetMidX(rect)
CGRectGetMaxX(rect)
CGRectGetMinY(rect)
CGRectGetMidY(rect)
CGRectGetMaxY(rect)
CGRectIsNull(rect)
CGRectIsEmpty(rect)
CGRectIsInfinite(rect)
CGRectStandardize(rect)
CGRectIntegral(rect)
CGRectInset(rect, 10, 5)
CGRectOffset(rect, -2, 8.3)
CGRectUnion(rect1, rect2)
CGRectIntersection(rect1, rect2)
CGRectContainsRect(rect1, rect2)
CGRectContainsPoint(rect, point)
CGRectIntersectsRect(rect1, rect2)

// good
rect.width
rect.height
rect.minX
rect.midX
rect.maxX
rect.minY
rect.midY
rect.maxY
rect.isNull
rect.isEmpty
rect.isInfinite
rect.standardized
rect.integral
rect.insetBy(dx: 5.0, dy: -7.0)
rect.offsetBy(dx: 5.0, dy: -7.0)
rect1.union(rect2)
rect1.intersect(rect2)
rect1.contains(rect2)
rect.contains(point)
rect1.intersects(rect2)

Legacy Constant

構造スコープ定数は従来のグローバル定数より優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-constant

// bad
CGRectInfinite
CGPointZero
CGRectZero
CGSizeZero
NSZeroPoint
NSZeroRect
NSZeroSize
CGRectNull
CGFloat(M_PI)
Float(M_PI)

// good
CGRect.infinite
CGPoint.zero
CGRect.zero
CGSize.zero
NSPoint.zero
NSRect.zero
NSSize.zero
CGRect.null
CGFloat.pi
Float.pi

Legacy Constructor

Swiftのコンストラクタは従来のコンビニエンス関数より優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-constructor

// bad
CGPointMake(10, 10)
CGPointMake(xVal, yVal)
CGPointMake(calculateX(), 10)
CGSizeMake(10, 10)
CGSizeMake(aWidth, aHeight)
CGRectMake(0, 0, 10, 10)
CGRectMake(xVal, yVal, width, height)
CGVectorMake(10, 10)
CGVectorMake(deltaX, deltaY)
NSMakePoint(10, 10)
NSMakePoint(xVal, yVal)
NSMakeSize(10, 10)
NSMakeSize(aWidth, aHeight)
NSMakeRect(0, 0, 10, 10)
NSMakeRect(xVal, yVal, width, height)
NSMakeRange(10, 1)
NSMakeRange(loc, len)
UIEdgeInsetsMake(0, 0, 10, 10)
UIEdgeInsetsMake(top, left, bottom, right)
NSEdgeInsetsMake(0, 0, 10, 10)
NSEdgeInsetsMake(top, left, bottom, right)
CGVectorMake(10, 10)
NSMakeRange(10, 1)
UIOffsetMake(0, 10)
UIOffsetMake(horizontal, vertical)

// good
CGPoint(x: 10, y: 10)
CGPoint(x: xValue, y: yValue)
CGSize(width: 10, height: 10)
CGSize(width: aWidth, height: aHeight)
CGRect(x: 0, y: 0, width: 10, height: 10)
CGRect(x: xVal, y: yVal, width: aWidth, height: aHeight)
CGVector(dx: 10, dy: 10)
CGVector(dx: deltaX, dy: deltaY)
NSPoint(x: 10, y: 10)
NSPoint(x: xValue, y: yValue)
NSSize(width: 10, height: 10)
NSSize(width: aWidth, height: aHeight)
NSRect(x: 0, y: 0, width: 10, height: 10)
NSRect(x: xVal, y: yVal, width: aWidth, height: aHeight)
NSRange(location: 10, length: 1)
NSRange(location: loc, length: len)
UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 10)
UIEdgeInsets(top: aTop, left: aLeft, bottom: aBottom, right: aRight)
NSEdgeInsets(top: 0, left: 0, bottom: 10, right: 10)
NSEdgeInsets(top: aTop, left: aLeft, bottom: aBottom, right: aRight)
UIOffset(horizontal: 0, vertical: 10)
UIOffset(horizontal: horizontal, vertical: vertical)

Legacy Hashing

hashValue をオーバーライドするのではなく、 hash(info:) 関数を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-hashing

// bad
struct Foo: Hashable {
    let bar: Int = 10

    public var hashValue: Int {
        return bar
    }
}

// good
struct Foo: Hashable {
    let bar: Int = 10

    func hash(into hasher: inout Hasher) {
        hasher.combine(bar)
    }
}

Legacy NSGeometry Functions

従来の関数より構造体のエクステンションのプロパティとメソッドを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-nsgeometry-functions

// bad
NSWidth(rect)
NSHeight(rect)
NSMinX(rect)
NSMidX(rect)
NSMaxX(rect)
NSMinY(rect)
NSMidY(rect)
NSMaxY(rect)
NSEqualRects(rect1, rect2)
NSEqualSizes(size1, size2)
NSEqualPoints(point1, point2)
NSEdgeInsetsEqual(insets2, insets2)
NSIsEmptyRect(rect)
NSIntegralRect(rect)
NSInsetRect(rect, 10, 5)
NSOffsetRect(rect, -2, 8.3)
NSUnionRect(rect1, rect2)
NSIntersectionRect(rect1, rect2)
NSContainsRect(rect1, rect2)
NSPointInRect(rect, point)
NSIntersectsRect(rect1, rect2)

// good
rect.width
rect.height
rect.minX
rect.midX
rect.maxX
rect.minY
rect.midY
rect.maxY
rect.isEmpty
rect.integral
rect.insetBy(dx: 5.0, dy: -7.0)
rect.offsetBy(dx: 5.0, dy: -7.0)
rect1.union(rect2)
rect1.intersect(rect2)
rect1.contains(rect2)
rect.contains(point)
rect1.intersects(rect2)

Line Length

1行にはあまりにも多くの文字を含めるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#line-length

Mark

MARK コメントは有効な形式であるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#mark

// bad
//MARK: bad
// MARK:bad
// MARK: -bad
// MARK:- bad
// MARK bad

// good
// MARK: good
// MARK: - good
// MARK: -

Multiple Closures with Trailing Closure

複数のクロージャを引数とする場合、トレイリングクロージャを使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiple-closures-with-trailing-closure

// bad
foo.something(param1: { $0 }) { $0 + 1 }

// good
foo.something(param1: { $0 }, param2: { $0 + 1 })

Nesting

型は最大1レベル、ステートメントは最大5レベルの深さでネストすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#nesting

// bad
class A { class B { class C { } } }

// good
class A { class B { } }

No Fallthrough Only

case に少なくとも1つのステートメントが含まれている場合のみ、fallthrouth を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#no-fallthrough-only

// bad
switch myvar {
case 1:
    fallthrough
case 2:
    var a = 2
}

// good
switch myvar {
case 1:
    var a = 1
    fallthrough
case 2:
    var a = 2
}
switch myvar {
case 1, 2:
    var a = 2
}

Notification Center Detachment

オブジェクトは deinit でのみ自分自身のオブザーバを削除すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#notification-center-detachment

// bad
class Foo {
    func bar() {
        NotificationCenter.default.removeObserver(self)
    }
}

// good
class Foo {
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

Opening Brace Spacing

{ は定義と同じ行で前に1つの半角スペースを置くべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#opening-brace-spacing

// bad
func abc(){
}
func abc()
{
}

// good
func abc() {
}

Operator Function Whitespace

演算子の定義時、1つの半角スペースで囲まれるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#operator-function-whitespace

// bad
func <|(lhs: Int, rhs: Int) -> Int { }
func <|  (lhs: Int, rhs: Int) -> Int { }

// good
func <| (lhs: Int, rhs: Int) -> Int { }

Private over fileprivate

fileprivate より private を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-over-fileprivate

// TODO: 例を見ても法則がわからない。。

Private Unit Test

private の単体テストは暗黙のうちにスキップされます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-unit-test

// bad
private class FooTests: XCTestCase {
   func test1() { }
}

class FooTests: XCTestCase {
    // bad
    private func test1() { }

    // good
    func test1() { }
 }

Protocol Property Accessors Order

プロトコルでプロパティを定義するときは、アクセサの順番を「ゲッター→セッター」とすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prohibited-calls-to-super

protocol Foo {
    // bad
    var bar: String { set get }

    // good
    var bar: String { set }
    var bar: String { get }
    var bar: String { get set }
}

Redundant Discardable Let

関数の戻り値を使わずに実行する場合、 let _ = foo() より _ = foo() を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-discardable-let

// bad
let _ = foo()
if let _ = foo() { }
guard let _ = foo() else { return }

// good
_ = foo()

Redundant @objc Attribute

冗長な @objc 属性は避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-objc-attribute

// bad
@objc @IBAction private func foo(_ sender: Any) { }

// good
@IBAction private func foo(_ sender: Any) { }

Redundant Optional Initialization

オプショナル型の変数を nil で初期化するのは冗長です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-optional-initialization

// bad
var myVar: Int? = nil

// good
var myVar: Int?
let myVar: Int? = nil
var myVar: Int? = 0

Redundant Set Access Control Rule

プロパティのセッターのアクセスレベルは、変数のアクセスレベルと同様であれば明示的に指定すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-set-access-control-rule

// bad
private(set) private var foo: Int

// good
private(set) public var foo: Int

Redundant String Enum Value

文字列の列挙型の値は、ケースと同名なら省略できます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-string-enum-value

// bad
enum Numbers: String {
  case one = "one"
  case two = "two"
}

// good
enum Numbers: String {
  case one
  case two
}
enum Numbers: String {
  case one = "ONE"
  case two = "TWO"
}

Redundant Void Return

関数の定義で Void を返すのは冗長です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-void-return

// bad
func foo() -> Void { }
func foo() -> () { }

// good
func foo() { }
let foo: Int -> Void

Returning Whitespace

戻り値の矢印と型は1つの半角スペースまたは別の行で区切るべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#returning-whitespace

// bad
func abc()-> Int { }
func abc() ->Int { }
func abc()->Int { }

// good
func abc() -> Int { }
func abc()
    -> Int { }
func abc() ->
    Int { }

Shorthand Operator

省略形の演算子を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#shorthand-operator

// bad
foo = foo + 1
foo = foo - 1
foo = foo * 1
foo = foo / 1

// good
foo += 1
foo -= 1
foo *= 1
foo /= 1

Statement Position

elsecatch は、前の定義の1つの半角スペースの後ろで同じ行にあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#statement-position

// bad
}else {

}
catch {

// good
} else {

} catch {

Superfluous Disable Command

無効化されたルールが無効化された領域で違反を起こさなかった場合、SwiftLintの disable コマンドは不要です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#superfluous-disable-command

Switch and Case Statement Alignment

case 文はそれを囲む switch 文と同じインデントにすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#switch-and-case-statement-alignment

// bad
switch someBool {
    case true:
        print("red")
    case false:
        print("blue")
}

// good
switch someBool {
case true:
    print("red")
case false:
    print("blue")
}

Syntactic Sugar

糖衣構文を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#syntactic-sugar

// bad
let x: Array<String>
let x: Dictionary<Int, String>
let x: Optional<Int>
let x: ImplicitlyUnwrappedOptional<Int>

// good
let x: [String]
let x: [Int: String]
let x: Int?
let x: Int!

Todo

TODOFIXME は解決すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#todo

// bad
// TODO:
// FIXME:

Trailing Comma

配列やディクショナリの末尾のカンマは避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-comma

// bad
let array = [1, 2, 3,]
let dictionary = ["foo": 1, "bar": 2,]

// good
let array = [1, 2, 3]
let dictionary = ["foo": 1, "bar": 2]

Trailing Newline

ファイルは末尾に1つの改行を持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-newline

Trailing Semicolon

行の末尾にセミコロンを付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-semicolon

// bad
let a = 0;

// good
let a = 0

Trailing Whitespace

行の末尾に半角スペースを付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-whitespace

// bad
let a = 1

// good
let a = 1

Type Body Length

型内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#type-body-length

Type Name

型名は英数のみを含み、大文字で始まり、3〜40文字にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#type-name

// bad
class My_Type { }
class myType { }
class aa { }

// good
class MyType { }

Unneeded Break in Switch

不要な break は避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unneeded-break-in-switch

switch a {
// bad
case .foo:
    something()
    break

// good
case .bar:
    something()
case .baz:
    break
case .qux:
    for i in [0, 1, 2] { break }
}

Unused Closure Parameter

クロージャで使われていないパラメータは _ に置き換えるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-closure-parameter

// bad
[1, 2].map { number in
    3
}

// good
[1, 2].map { _ in
    3
}
[1, 2].map { number in
    number + 1
}
[1, 2].map { $0 + 1 }

Unused Control Flow Label

未使用の制御フローラベルは削除すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-control-flow-label

// bad
loop: while true { break }

// good
loop: while true { break loop }
loop: while true { continue loop }

Unused Enumerated

インデックスまたはアイテムが使われていない場合、 .enumerated() を削除できます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-enumerated

// TODO: わからない。。

Unused Optional Binding

let _ = より != nil を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-optional-binding

// bad
if let _ = a { }

// good
if a != nil { }

Unused Setter Value

セッターの値は使われるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-setter-value

var aValue: String {
    get {
        return Persister.shared.aValue
    }
    set {
        // bad
        Persister.shared.aValue = aValue
        // good
        Persister.shared.aValue = newValue
    }
}

Valid IBInspectable

@IBInspectable はサポートされている型の変数のみに使い、その型を明示的にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#valid-ibinspectable

class Foo {
    // bad
    @IBInspectable private let count: Int
    @IBInspectable private var count = 0
    @IBInspectable private var count: Int?
    @IBInspectable private var count: Int!

    // good
    @IBInspectable private var count: Int
    @IBInspectable private var count: Int = 0
}

Vertical Parameter Alignment

関数の定義時、パラメータが複数行にまたがっている場合は垂直方向に揃えるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-parameter-alignment

// bad
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
                    dictionary: [String: SourceKitRepresentable]) { }
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
                       dictionary: [String: SourceKitRepresentable]) { }

// good
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
                      dictionary: [String: SourceKitRepresentable]) { }

Vertical Whitespace

空白行は1行に制限します。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace

Void Return

-> () より -> Void を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#void-return

// bad
let abc: () -> Void = { }

// good
let abc: () -> () = { }

Weak Computed Property

コンピューテッドプロパティに weak を追加しても効果はありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#weak-computed-property

class Foo {
    private weak var _delegate: SomeProtocol?

    // bad
    weak var delegate: SomeProtocol? {
    // good
    var delegate: SomeProtocol? {
    get { return _delegate }
    set { _delegate = newValue }
    }
}

Weak Delegate

デリゲートは循環参照を避けるために弱参照とすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#weak-delegate

class Foo {
    // bad
    var delegate: SomeProtocol?

    // good
    weak var delegate: SomeProtocol?
}

XCTFail Message

XCTFail の呼び出しにはアサーションの説明を含めるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#xctfail-message

func testFoo() {
    // bad
    XCTFail()

    // good
    XCTFail("bar")
}

Opt-in

デフォルトで無効になっているルールの一覧です。

AnyObject Protocol

クラス専用のプロトコルでは、 class より AnyObject を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#anyobject-protocol

// bad
protocol SomeClassOnlyProtocol: class { }

// good
protocol SomeClassOnlyProtocol: AnyObject { }

Array Init

シーケンスを配列に変換する場合、 seq.map { $0 } より Array(seq) を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#array-init

// bad
seq.map { $0 }

// good
Array(seq)

Attributes

属性は関数や型では別の行にあるべきですが、変数やインポートでは同じ行にあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#attributes

// bad
@available(iOS 9.0, *) func animate(view: UIStackView)

@available(iOS 9.0, *) class UIStackView

@objc
var x: String

@testable
import SourceKittenFramework

// good
@available(iOS 9.0, *)
func animate(view: UIStackView)

@available(iOS 9.0, *)
class UIStackView

@objc var x: String

@testable import SourceKittenFramework

Closure Body Length

クロージャ内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-body-length

// bad
foo.bar { toto in
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
let a = 0
}

// good
foo.bar { $0 }
foo.bar { toto in
}

Closure End Indentation

クロージャの終了は開始と同様のインデントを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-end-indentation

// bad
SignalProducer(values: [1, 2, 3])
    .startWithNext { number in
        print(number)
}

// good
SignalProducer(values: [1, 2, 3])
    .startWithNext { number in
        print(number)
    }
[1, 2].map { $0 + 1 }

Closure Spacing

クロージャ内は各括弧の内側に1つの半角スペースがあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-spacing

// bad
[].filter {$0.contains(location)}

// good
[].filter { $0.contains(location) }

Collection Element Alignment

コレクション内の全要素は垂直方向に揃うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#collection-element-alignment

// bad
let abc = [
    "alpha": "a",
      "beta": "b",
    "gamma": "g",
    "delta": "d",
   "epsilon": "e"
]

// good
let abc = [
    "alpha": "a",
    "beta": "b",
    "gamma": "g",
    "delta": "d",
    "epsilon": "e"
]

let abc = [1, 2, 3, 4]

let abc = [
    1, 2, 3, 4
]

Conditional Returns on Newline

条件文では次の行でリターンすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#conditional-returns-on-newline

// bad
guard true else { return }

// good
guard true else {
    return true
}

Contains over first not nil

first(where:) != nil より contains を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#contains-over-first-not-nil

// bad
myList.first { $0 % 2 == 0 } != nil
myList.first(where: { $0 % 2 == 0 }) != nil

// good
myList.first { $0 % 2 == 0 }
myList.first(where: { $0 % 2 == 0 })

Convenience Type

静的メンバーのみをホストするために使われる型は、インスタンス化を回避するために、case のない列挙型として実装すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#convenience-type

// bad
struct Math {
    public static let pi = 3.14
}
class Math {
    public static let pi = 3.14
}

// good
enum Math {
    public static let pi = 3.14
}

// 継承を伴う場合はOK
class MathViewController: UIViewController {
    public static let pi = 3.14
}

// Obj-Cに見えるクラスはOK
@objc class Math: NSObject {
    public static let pi = 3.14
}

// 静的でない型もある場合はOK
struct Math {
    public static let pi = 3.14
    public let randomNumber = 2
}

Discouraged Object Literal

オブジェクトリテラルよりイニシャライザを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-object-literal

// bad
let image = #imageLiteral(resourceName: "image.jpg")
let color = #colorLiteral(red: value, green: value, blue: value, alpha: 1)

// good
let image = UIImage(named: "image")
let color = UIColor(red: value, green: value, blue: value, alpha: 1)

Discouraged Optional Boolean

オプショナル型よりそうでない Bool 型を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-optional-boolean

// bad
var foo: Bool?

// good
var foo: Bool

Discouraged Optional Collection

オプショナルのコレクションより空のコレクションを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-optional-collection

// bad
var foo: [Int]?
var foo: [String: Int]?
let foo: [Int]? = nil
let foo: [String: Int]? = nil

// good
var foo: [Int]
var foo: [String: Int]
let foo: [Int] = []
let foo: [String: Int] = [:]

Empty Count

count0 と比較するより isEmpty を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-count

// bad
[Int]().count == 0
[Int]().count > 0
[Int]().count != 0

// good
[Int]().isEmpty

Empty String

空の文字列と比較するより isEmpty を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-string

// bad
myString == ""
myString != ""

// good
myString.isEmpty
!myString.isEmpty

Empty XCTest Method

空のXCTestメソッドを実装すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-xctest-method

// bad
class TotoTests: XCTestCase {

    override func setUp() {
    }

    override func tearDown() {
    }

    func testFoo() {
    }

}

// good
class TotoTests: XCTestCase {

    var foobar: Foobar?

    override func setUp() {
        super.setUp()
        foobar = Foobar()
    }

    override func tearDown() {
        foobar = nil
        super.tearDown()
    }

    func testFoo() {
        XCTAssertTrue(foobar?.foo)
    }

    func helperFunction() {
    }
}

Explicit ACL

全ての定義はアクセス制御レベルのキーワードを明示的に指定すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-acl

// bad
enum A { }

// good
internal enum A { }

Explicit Enum Raw Value

列挙型はローバリューを明示的に割り当てるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-enum-raw-value

// bad
enum Numbers: Int {
    case one
    case two
}

// good
enum Numbers: Int {
    case one = 1
    case two = 2
}

Explicit Init

.init() を明示的に呼び出すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-init

// bad
[1].flatMap { String.init($0) }
[String.self].map { Type in Type.init(1) }

// good
[1].flatMap(String.init)
[String.self].map { $0.init(1) }
[String.self].map { type in type.init(1) }

Explicit Self

インスタンス変数と関数は self. で明示的にアクセスされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-self

struct A {
    let p1: Int
    func f1() { }
    func f2() {
        // bad
        f1()
        _ = p1

        // good
        self.f1()
        _ = self.p1
    }
}

Explicit Top Level ACL

最上位の定義はアクセス制御レベルを明示的に指定すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-top-level-acl

// bad
enum A {
    enum B { }
}

// good
internal enum A {
    enum B { }
}

Explicit Type Interface

プロパティは型インターフェースを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-type-interface

class Foo {
    // bad
    var myVar = 0

    // good
    var myVar: Int? = 0
}

Extension Access Modifier

エクステンションにはアクセス修飾子を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-type-interface

// TODO: 例を見ても法則がわからない。。

Fallthrough

fallthrough は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#fallthrough

// bad
switch foo {
case .bar:
    fallthrough
case .bar2:
    something()
}

// good
switch foo {
case .bar, .bar2:
    something()
}

Fatal Error Message

fatalError はメッセージを付けて呼び出すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#fatal-error-message

// bad
func foo() {
    fatalError()
}

// good
func foo() {
    fatalError("Foo")
}

File Header

https://github.com/realm/SwiftLint/blob/master/Rules.md#file-header

// TODO: 例を見ても法則がわからない。。

File Name

ファイル名はファイル内で定義されている型またはエクステンションと一致すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#file-name

First Where

コレクションでは .filter {} .first より .first(where:) を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#first-where

// bad
myList.filter { $0 % 2 == 0 } .first

// good
myList.first(where: { $0 % 2 == 0 })
myList.first { $0 % 2 == 0 }

Force Unwrapping

強制アンラップ( ! )は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-unwrapping

// bad
let url = NSURL(string: query)!

// good
if let url = NSURL(string: query) { }

Function Default Parameter at End

関数でデフォルト値を持つ引数は後ろにまとめるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-default-parameter-at-end

// bad
func foo(y: Int = 0, x: String, z: CGFloat = 0) { }

// good
func foo(x: String, y: Int = 0, z: CGFloat = 0) { }

Identical Operands

同一のオペランドを比較するのはおそらく間違いです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#identical-operands

// bad
foo == foo

// good
foo == bar

Implicit Return

クロージャでは暗黙のリターンを優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#implicit-return

// bad
foo.map {
    return $0 + 1
}

// good
foo.map { $0 + 1 }

Implicitly Unwrapped Optional

暗黙的にアンラップされるオプショナル型はできる限り使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#implicitly-unwrapped-optional

// bad
private var label: UILabel!
let int: Int! = 42

// good
@IBOutlet private var label: UILabel!
let int: Int? = 42

Joined Default Parameter

デフォルトの区切り文字は明示的に使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#joined-default-parameter

// bad
let foo = bar.joined(separator: "")

// good
let foo = bar.joined()
let foo = bar.joined(separator: ",")

Last Where

コレクションでは .filter {} .last より .last(where:) を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#last-where

// bad
myList.filter { $0 % 2 == 0 } .last

// good
myList.last(where: { $0 % 2 == 0 })

Legacy Random

従来の関数より type.ramdom(in:) を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-random

// bad
arc4random(10)
arc4random_uniform(83)
drand48(52)

// good
Int.random(in: 0..<10)
Double.random(in: 8.6...111.34)
Float.random(in: 0..<1)

Variable Declaration Whitespace

letvar は他のステートメントと空白行で区切るべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#variable-declaration-whitespace

// bad
let a = 0
var x = 1
x = 2

// good
let a = 0
var x = 1

x = 2

Literal Expression End Indentation

配列とディクショナリのリテラルの末尾は、開始行と同様のインデントを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#literal-expression-end-indentation

// bad
let x = [
    1,
    2
    ]

// good
[1, 2, 3]
let x = [
    1,
    2
]
let x = [1,
    2
]
let x = [
    1,
    2]

Lower ACL than parent

定義が親よりも低いまたは同じアクセス制御レベルを持つようにすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#lower-acl-than-parent

// bad
struct Foo {
    public func bar() { }
}

// good
public struct Foo {
    public func bar() { }
}

Missing Docs

定義はドキュメント化すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#missing-docs

// good
/// docs
public class A {
    /// docs
    public func b() { }
}
/// docs
public class B: A {
    // オーバーライドメソッドはOK
    override public func b() { }
}

Modifier Order

修飾子の順番は一貫しているべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#modifier-order

public class Foo {
    // bad
    convenience required public init() { }
    static public let bar = 42

    // good
    public convenience required init() { }
    public static let bar = 42
}

Multiline Arguments

引数は同じ行に入れるか1行に1つずつ入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-arguments

// bad
func foo(
    param1: "Param1", param2: "Param2",
    param3: "Param3"
)

// good
func foo(param1: "Param1", param2: "Param2", param3: "Param3")
func foo(
    param1: "Param1",
    param2: "Param2",
    param3: "Param3"
)

Multiline Arguments Brackets

複数行の引数は、それらを括る大括弧を新しい行に持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-arguments-brackets

// bad
foo(param1: "Param1",
    param2: "Param2"
)
foo(
    param1: "Param1",
    param2: "Param2")

// good
foo(param1: "Param1", param2: "Param2")
foo(
    param1: "Param1",
    param2: "Param2"
)

Multiline Function Chains

関数を連鎖的に呼び出す場合、同じ行に入れるか1行に1つずつ入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-function-chains

// bad
let evenSquaresSum = [20, 17, 35, 4]
    .filter { $0 % 2 == 0 } .map { $0 * $0 }
    .reduce(0, +)

// good
let evenSquaresSum = [20, 17, 35, 4].filter { $0 % 2 == 0 } .map { $0 * $0 } .reduce(0, +)
let evenSquaresSum = [20, 17, 35, 4]
    .filter { $0 % 2 == 0 }
    .map { $0 * $0 }
    .reduce(0, +)

Multiline Literal Brackets

複数行のリテラルは、新しい行にそれを括る大括弧を入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-literal-brackets

// bad
let trio = ["harry",
            "ronald",
            "hermione"
]
let trio = [
    "harry",
    "ronald",
    "hermione"]

// good
let trio = ["harry", "ronald", "hermione"]
let trio = [
    "harry",
    "ronald",
    "hermione"
]

Multiline Parameters

関数とメソッドの引数は、同様の行にあるか1行に1つずつあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-parameters

// bad
func foo(param1: Int, param2: Bool,
         param3: [String]) { }

// good
func foo(param1: Int, param2: Bool, param3: [String]) { }
func foo(param1: Int,
         param2: Bool,
         param3: [String]) { }

Multiline Parameters Brackets

複数行の引数は、新しい行にそれを括る大括弧を入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-parameters-brackets

// bad
func foo(param1: "Param1",
         param2: "Param2",
         param3: "Param3"
)
func foo(
    param1: "Param1",
    param2: "Param2",
    param3: "Param3")

// good
func foo(param1: "Param1", param2: "Param2", param3: "Param3")
func foo(
    param1: "Param1",
    param2: "Param2",
    param3: "Param3"
)

Nimble Operator

フリーのmatcher関数よりNimble演算子のオーバーロードを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#nimble-operator

//bad
expect(seagull.squawk).toNot(equal("Hi"))

// good
expect(seagull.squawk) == "Hi!"

No Extension Access Modifier

エクステンションにアクセス修飾子を付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#no-extension-access-modifier

// bad
private extension String { }
fileprivate extension String { }
internal extension String { }
public extension String { }
open extension String { }

// good
extension String { }

No Grouping Extension

エクステンションは同じソースファイル内のコードをグループ化するために使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#no-grouping-extension

// bad
enum Fruit { }
extension Fruit { }

// good
// プロトコルのデフォルト実装はOK
protocol Food { }
extension Food { }

NSLocalizedString Key

genstringsが機能するためには、静的文字列を NSLocalizedString のキーとして使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#nslocalizedstring-key

// bad
NSLocalizedString(method(), comment: nil)

// good
NSLocalizedString("key", comment: nil)

Number Separator

_ は十進数で千の区切り文字として使うべきです。

// bad
let foo = 1000
let foo = 1000_000.000_000_1
let foo = 1_000_000.000000_1
let foo = 1_0_00_000.000_000_1

// good
let foo = 1_000
let foo = 1_000_000.000_000_1

Object Literal

画像や色はイニシャライザよりリテラルを使って生成すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#object-literal

// bad
let image = UIImage(named: "image")
let color = UIColor(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1.0)

// good
let image = #imageLiteral(resourceName: "image.jpg")
let color = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1.0)

Operator Usage Whitespace

演算子を使うときは1つの半角スペースで囲まれるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#operator-usage-whitespace

// bad
let foo = 1+2
let foo=1+2

// good
let foo = 1 + 2

Overridden methods call super

一部のオーバーライドメソッドは常に親クラスのメソッドを呼び出すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#overridden-methods-call-super

// bad
class VC: UIViewController {
    override func viewWillAppear(_ animated: Bool) {
    }
}

// good
class VC: UIViewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    override func loadView() {
    }
}

Override in Extension

エクステンションでは定義をオーバーライドすべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#override-in-extension

extension Person {
    // good
    var age: Int { return 42 }
    func celebrateBirthday() { }

    // bad
    override var age: Int { return 42 }
    override func celebrateBirthday() { }
}

Pattern Matching Keywords

キーワードをタプルの外に出し、複数のパターンマッチングバインディングを組み合わせるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#pattern-matching-keywords

switch foo {
  // bad
  case (let x, let y):

  // good
  case let (x, y):
}

Prefixed Top-Level Constant

最上位の定数はプリフィックスに k を付けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prefixed-top-level-constant

// bad
let bar = 20.0

// good
let kBar = 20.0
struct Foo {
    let bar = 20.0
}

Private Actions

@IBActionprivate にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-actions

// bad
class Foo {
    @IBAction func barButtonTapped(_ sender: UIButton) { }
}

// good
class Foo {
    @IBAction private func barButtonTapped(_ sender: UIButton) { }
}

Private Outlets

@IBOutletprivate にすべきです。
∵上位レイヤーへ UIKit が漏洩するのを防ぐため
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-outlets

// bad
class Foo {
    @IBOutlet var label: UILabel?
}

// good
class Foo {
    @IBOutlet private var label: UILabel?
}

Prohibited Interface Builder

IBを使ってビューを生成するのは避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prohibited-interface-builder

class ViewController: UIViewController {
    // bad
    @IBOutlet var label: UILabel!
    @IBAction func buttonTapped(_ sender: UIButton) { }

    // good
    var label: UILabel!
    @objc func buttonTapped(_ sender: UIButton) { }
}

Prohibited calls to super

一部のメソッドは親クラスのメソッドを呼び出してはいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prohibited-calls-to-super

// bad
class VC: UIViewController {
    override func loadView() {
        super.loadView()
    }
}

Quick Discouraged Call

Quickを使ったことがないのでわかりません。。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-call

Quick Discouraged Focused Test

こちらも上記と同様にわかりません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-focused-test

Quick Discouraged Pending Test

こちらも上記と同様にわかりません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-pending-test

Redundant Nil Coalescing

?? 演算子は左辺が nil の場合のみ評価されるため、右辺に nil を記述するのは冗長です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-nil-coalescing

// bad
var myVar: Int? = nil
myVar ?? nil

// good
var myVar: Int?
myVar ?? 0

Redundant Type Annotation

変数は冗長な型アノテーションを持つべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-type-annotation

// bad
var url: URL = URL()

// good
var url = URL()
var url: CustomStringConvertible = URL()

Required Deinit

クラスは明示的な deinit メソッドを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#required-deinit

// bad
class Apple { }

// good
class Apple {
    deinit { }
}

Required Enum Case

特定のプロトコルに準拠した列挙型は特定のケースを実装する必要があります。
https://github.com/realm/SwiftLint/blob/master/Rules.md#required-enum-case

// bad
enum MyNetworkResponse: String, NetworkResponsable {
  case success
  case error
}

// good
enum MyNetworkResponse: String, NetworkResponsable {
  case success
  case error
  case notConnected
}
enum MyNetworkResponse: String, NetworkResponsable {
  case success
  case error
  case notConnected(error: Error)
}

Single Test Class

テストファイルは単一の QuickSpec または XCTestCase クラスを含むべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#single-test-class

// bad
class FooTests: QuickSpec { }
class BarTests: QuickSpec { }

class FooTests: XCTestCase { }
class BarTests: XCTestCase { }

class FooTests: XCTestCase { }
class BarTests: QuickSpec { }

// good
class FooTests: XCTestCase { }

class FooTests: QuickSpec { }

Min or Max over Sorted First or Last

sorted().firstsorted().last より min()max() を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#min-or-max-over-sorted-first-or-last

// bad
myList.sorted().first
myList.sorted().last
let min = myList.sorted(by: >).first

// good
myList.min()
myList.max()
let min = myList.min(by: >)

Sorted Imports

インポート文はソートされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#sorted-imports

// bad
import AAA
import CCC
import BBB
import DDD

// good
import AAA
import BBB
import CCC
import DDD

Static Operator

演算子は自由関数でなく静的関数として定義すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#static-operator

// bad
func == (lhs: A, rhs: A) -> Bool {
    return false
}
func == <T>(lhs: A<T>, rhs: A<T>) -> Bool {
    return false
}

// good
class A: Equatable {
    static func == (lhs: A, rhs: A) -> Bool {
        return false
    }
}
class A<T>: Equatable {
    static func == <T>(lhs: A<T>, rhs: A<T>) -> Bool {
        return false
    }
}

Strict fileprivate

fileprivate は避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#strict-fileprivate

// bad
fileprivate extension String { }

extension String {
    fileprivate func Something() { }
}

// good
private extension String { }

extension String {
    func Something() { }
}

Strong IBOutlet

@IBOutlet は弱参照で定義すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#strong-iboutlet

// bad
class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel?
    @IBOutlet unowned var label: UILabel!
}

// good
class ViewController: UIViewController {
    @IBOutlet var label: UILabel?
    weak var label: UILabel!
}

Switch Case on Newline

switch 文の中の case 文は常に改行すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#switch-case-on-newline

// bad
switch foo {
case 1: return true
}

// good
switch foo {
case 1:
    return true
}

Toggle Bool

someBool = !someBool より someBool.toggle() を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#toggle-bool

// bad
isHidden = !isHidden

// good
isHidden.toggle()

Trailing Closure

できる限りトレイリングクロージャを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-closure

// bad
foo.map({ $0 + 1 })
foo.reduce(0, combine: { $0 + 1 })

// good
foo.map { $0 + 1 }
foo.reduce(0) { $0 + 1 }

Unavailable Function

未実装の関数は使用不可としてマークされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unavailable-function

// bad
class ViewController: UIViewController {
    public required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// good
class ViewController: UIViewController {
    @available(*, unavailable)
    public required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Unneeded Parentheses in Closure Argument

クロージャ引数の定義時に括弧は不要です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unneeded-parentheses-in-closure-argument

// bad
let foo = { (bar) in }
let foo = { (bar, _)  in }

// good
let foo = { (bar: Int) in }
let foo = { bar in }
let foo = { bar, _ in }

Untyped Error in Catch

catch 文は型キャストなしでエラー変数を定義すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#untyped-error-in-catch

// bad
do {
    try foo()
} catch let error {
}

// good
do {
    try foo()
} catch let error as MyError {
} catch { }

Unused Import

インポートされた全てのモジュールがファイルをコンパイルするために必要とされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-import

// bad
import Foundation
class A { }

// good
import Foundation
@objc
class A { }

Unused Private Declaration

private の定義はそのファイル内で参照されるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-private-declaration

// bad
private let a = 0

// good
private let a = 0
_ = a

Vertical Parameter Alignment On Call

関数の呼び出し時、パラメータが複数行にまたがっている場合は垂直方向に揃えるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-parameter-alignment-on-call

// bad
validateFunction(_ file: File, kind: SwiftDeclarationKind,
               dictionary: [String: SourceKitRepresentable]) { }
validateFunction(_ file: File, kind: SwiftDeclarationKind,
                  dictionary: [String: SourceKitRepresentable]) { }

// good
validateFunction(_ file: File, kind: SwiftDeclarationKind,
                 dictionary: [String: SourceKitRepresentable]) { }

Vertical Whitespace Between Cases

SwitchのCase間は空白行を1行含んでください。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace-between-cases

// bad
switch x {
case .valid:
    print("x is valid")
case .invalid:
    print("x is invalid")
}

// good
switch x {
case .valid:
    print("x is valid")

case .invalid:
    print("x is invalid")
}

Vertical Whitespace before Closing Braces

中括弧を閉じる前に空白行を入れないでください。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace-before-closing-braces

// bad
{
    {
    }

}

// good
{
    {
    }
}

Vertical Whitespace after Opening Braces

中括弧を開いた後に空白行を入れないでください。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace-after-opening-braces

// bad
{

    {
    }
}

// good
{
    {
    }
}

XCTest Specific Matcher

XCTAssertEqualXCTAssertNotEqual より特定のXCTestマッチャーを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#xctest-specific-matcher

// bad
XCTAssertEqual(foo, true)
XCTAssertEqual(foo, false)
XCTAssertEqual(foo, nil)
XCTAssertNotEqual(foo, true)
XCTAssertNotEqual(foo, false)
XCTAssertNotEqual(foo, nil)

// good
XCTAssertFalse(foo)
XCTAssertTrue(foo)
XCTAssertNil(foo)
XCTAssertNotNil(foo)
XCTAssertEqual(foo, 2)

Yoda condition rule

変数は比較演算子の左側、定数は右側に配置すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#yoda-condition-rule

// bad
if 42 == foo { }

// good
if foo == 42 { }

おわりに

SwiftLintのルールを一通り眺めるだけでもSwiftyに書けるようになった気がします。
ただ、これらを他のメンバーにも守ってもらうには、やはりSwiftLintの導入が便利です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UITextViewの保存と編集

Important Point

(1) You cannot implement images with HTML by this method. Though NSTextView handles both strings and images, UITextView seems not to handle images with HTML strings.

(2) If you accept to change Line spacing, Font size and Font color all at once, apply them to UITextView before or after editing HTML.
In my case, Font color per all strings is initialized when webView HTML is saved to NSArray so that a user can change color dinamically per every word or characters during editing.

(3) Sample code
① Method to save webView HTML to NSArray.
② Method to read strings in NSArray to UITextView.
③ Method to write or save UITextView to NSArray.

今朝、iOSアプリの修正版を申請しました。8:40に申請して、9:42に承認されました。超最短に驚き!! マイナーな修正だったのですが、前回、macOSアプリ申請でApplication Groupでつまずき、plistの1行を削除するのにインシデントを使わないと解決できなかったので、今、ほっとしています。

注意点

(1) NSTextViewと違い、HTMLの画像は簡単に取り込めません。現時点では、それが事実かどうか不明ですが、少なくとも実装できていません。

(2) 行間スペース、フォント、フォントサイズ、フォント色を一様に同じにするのであれば、UITextViewを編集する箇所でこれらの変更を反映させればいいです。しかし、たとえば、フォント色をテキストによって変更できるような仕様にする場合、最初にUITextViewを設定するときに初期値を適用し、その後、個々の文字列の色を変えれるように実装する必要があります。

(3) したがって①初回保存、②編集開始時、保存先からUITextViewへの読み込み、③編集完了時、UITextViewの保存の3つのメソッドが必要となりました。

謝意: 【Swift4】リッチテキストファイル(rtf)のテキストをUITextViewに表示するが参考になりました。SatoTakeshiXさん、ありがとうございます。

初回保存

    let Z                       = MesaModalArea.sharedInstance      //共通エリア
    var vFontSize:CGFloat       = 13                                //フォントサイズ
    var vFontColor:UIColor      = UIColor.black                     //フォント色
    var vLinespace:CGFloat      = 2                                 //行間

    func CallSaveRTFD(_ pHTML:String,pArray:[NSMutableDictionary],  //<*webViewのHTML保存(Saveボタン)*>
                      pRow:Int, pEncode:UInt) {                     //
        if pRow < 0 || pRow > pArray.count { return }               //♻️return♻️
        else { }                                                    //
        let wView:UITextView    = UITextView()                      //
        let wAtt                = NSMutableAttributedString()       //
        wAtt.append( try! NSAttributedString(                       //?iOS Read相当
            data:               pHTML.data(using: String.Encoding(rawValue:pEncode))!,//HTML Decode
            options:            [:],                                //
            documentAttributes: nil))                               //
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
        let wLine   = NSMutableParagraphStyle()                     //
        wLine.lineSpacing = Z.vLinespace                            //行間スペース
        wAtt.addAttributes([                                        //
            .paragraphStyle:    wLine,                              // 行間
            .foregroundColor:   Z.vFontColor,                       //フォント色
            .font:              UIFont.systemFont(ofSize: Z.vFontSize)],//フォントサイズ
             range:NSRange(location:0, length:wAtt.length) )        //
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
        wView.textStorage.setAttributedString(wAtt)                 //NSTextViewにデータをsetする。
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
        let wData:Data = try! wAtt.data(                            //
            from:NSRange(location:0, length:wView.attributedText!.length),//
            documentAttributes:[NSAttributedString.DocumentAttributeKey.documentType:NSAttributedString.DocumentType.html])//
        Z.rcArray[Z.maybeLastRow][Z.fNote] = String(data:wData, encoding:String.Encoding(rawValue:pEncode))!//エンコード & saveする
    }//??????????????????????????????

編集開始、UITextViewへの読み込み

    func CallReadRTFD(_ pView:UITextView, pArray:[NSMutableDictionary],//<*HTMLをTextViewに読み込む*>
        pRow:Int) -> Bool{                                          //
        if pRow < 0 || pRow > pArray.count { return false }         //♻️return♻️ 行位置が範囲外。
        else { }                                                    //
        let wAtt                = NSMutableAttributedString()       //
        let wHTML:String        = pArray[pRow][Z.fNote] as! String  //html
        if wHTML.count > 0 {                                        //
            let wEncode:UInt    = pArray[pRow][Z.fEncode] as! UInt  //
            wAtt.append( try! NSAttributedString(                   //?iOS Read相当
                data:           wHTML.data(using:String.Encoding(rawValue: wEncode))!,//HTML Decode
                options:        [.documentType:NSAttributedString.DocumentType.html],//
                documentAttributes: nil))                           //
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
            let wLine           = NSMutableParagraphStyle()         //
            wLine.lineSpacing   = Z.vLinespace                      //行間スペース
            wAtt.addAttributes([                                    //
                .paragraphStyle:    wLine,                          // 行間
//              .foregroundColor:   Z.vFontColor,                   //フォント色
                .font:              UIFont.systemFont(ofSize: Z.vFontSize),//フォントサイズ
                ], range:NSRange(location:0, length:wAtt.length))   //
            pView.textStorage.setAttributedString(wAtt)             //NSTextViewにデータをsetする。
        } else {                                                    //
            wAtt.append(NSAttributedString(string:""))              //NSTextViewの初期化
            pView.textStorage.setAttributedString(wAtt)             //NSTextViewにデータをsetする。
        }                                                           //
        return true                                                 //♻️return♻️
    }//-------------------------------------------------------------//

編集完了時、UITextViewの保存

    func CallWriteRTFD(_ pView:UITextView, pArray:[NSMutableDictionary],//<*TextViewをHTMLで保存する*>
        pRow:Int)->[NSMutableDictionary] {                          //
        if pRow < 0 || pRow > pArray.count { return pArray }        //♻️return♻️
        else { }                                                    //
        let wData:Data          = try! pView.attributedText!.data(  //
            from:               NSRange(location:0, length:pView.attributedText!.length),//
            documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType:NSAttributedString.DocumentType.html])//html
        let wEncode:UInt        = pArray[pRow][Z.fEncode] as! UInt  //
        pArray[pRow][Z.fNote]   = String(data: wData,               //
                   encoding:String.Encoding(rawValue: wEncode))!//エンコード & saveする
        return pArray                                               //♻️return♻️
    }//??????????????????????????????
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UTTextViewの保存と編集

Important Point

(1) You cannot implement images with HTML by this method. Though NSTextView handles both strings and images, UITextView seems not to handle images with HTML strings.

(2) If you accept to change Line spacing, Font size and Font color all at once, apply them to UITextView before or after editing HTML.
In my case, Font color per all strings is initialized when webView HTML is saved to NSArray so that a user can change color dinamically per every word or characters during editing.

(3) Sample code
① Method to save webView HTML to NSArray.
② Method to read strings in NSArray to UITextView.
③ Method to write or save UITextView to NSArray.

今朝、iOSアプリの修正版を申請しました。8:40に申請して、9:42に承認されました。超最短に驚き!! マイナーな修正だったのですが、前回、macOSアプリ申請でApplication Groupでつまずき、plistの1行を削除するのにインシデントを使わないと解決できなかったので、今、ほっとしています。

注意点

(1) NSTextViewと違い、HTMLの画像は簡単に取り込めません。現時点では、それが事実かどうか不明ですが、少なくとも実装できていません。

(2) 行間スペース、フォント、フォントサイズ、フォント色を一様に同じにするのであれば、UITextViewを編集する箇所でこれらの変更を反映させればいいです。しかし、たとえば、フォント色をテキストによって変更できるような仕様にする場合、最初にUITextViewを設定するときに初期値を適用し、その後、個々の文字列の色を変えれるように実装する必要があります。

(3) したがって①初回保存、②編集開始時、保存先からUITextViewへの読み込み、③編集完了時、UITextViewの保存の3つのメソッドが必要となりました。

謝意: 【Swift4】リッチテキストファイル(rtf)のテキストをUITextViewに表示するが参考になりました。SatoTakeshiXさん、ありがとうございます。

初回保存

    let Z                       = MesaModalArea.sharedInstance      //共通エリア
    var vFontSize:CGFloat       = 13                                //フォントサイズ
    var vFontColor:UIColor      = UIColor.black                     //フォント色
    var vLinespace:CGFloat      = 2                                 //行間

    func CallSaveRTFD(_ pHTML:String,pArray:[NSMutableDictionary],  //<*webViewのHTML保存(Saveボタン)*>
                      pRow:Int, pEncode:UInt) {                     //
        if pRow < 0 || pRow > pArray.count { return }               //♻️return♻️
        else { }                                                    //
        let wView:UITextView    = UITextView()                      //
        let wAtt                = NSMutableAttributedString()       //
        wAtt.append( try! NSAttributedString(                       //?iOS Read相当
            data:               pHTML.data(using: String.Encoding(rawValue:pEncode))!,//HTML Decode
            options:            [:],                                //
            documentAttributes: nil))                               //
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
        let wLine   = NSMutableParagraphStyle()                     //
        wLine.lineSpacing = Z.vLinespace                            //行間スペース
        wAtt.addAttributes([                                        //
            .paragraphStyle:    wLine,                              // 行間
            .foregroundColor:   Z.vFontColor,                       //フォント色
            .font:              UIFont.systemFont(ofSize: Z.vFontSize)],//フォントサイズ
             range:NSRange(location:0, length:wAtt.length) )        //
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
        wView.textStorage.setAttributedString(wAtt)                 //NSTextViewにデータをsetする。
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
        let wData:Data = try! wAtt.data(                            //
            from:NSRange(location:0, length:wView.attributedText!.length),//
            documentAttributes:[NSAttributedString.DocumentAttributeKey.documentType:NSAttributedString.DocumentType.html])//
        Z.rcArray[Z.maybeLastRow][Z.fNote] = String(data:wData, encoding:String.Encoding(rawValue:pEncode))!//エンコード & saveする
    }//??????????????????????????????

編集開始、UITextViewへの読み込み

    func CallReadRTFD(_ pView:UITextView, pArray:[NSMutableDictionary],//<*HTMLをTextViewに読み込む*>
        pRow:Int) -> Bool{                                          //
        if pRow < 0 || pRow > pArray.count { return false }         //♻️return♻️ 行位置が範囲外。
        else { }                                                    //
        let wAtt                = NSMutableAttributedString()       //
        let wHTML:String        = pArray[pRow][Z.fNote] as! String  //html
        if wHTML.count > 0 {                                        //
            let wEncode:UInt    = pArray[pRow][Z.fEncode] as! UInt  //
            wAtt.append( try! NSAttributedString(                   //?iOS Read相当
                data:           wHTML.data(using:String.Encoding(rawValue: wEncode))!,//HTML Decode
                options:        [.documentType:NSAttributedString.DocumentType.html],//
                documentAttributes: nil))                           //
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
            let wLine           = NSMutableParagraphStyle()         //
            wLine.lineSpacing   = Z.vLinespace                      //行間スペース
            wAtt.addAttributes([                                    //
                .paragraphStyle:    wLine,                          // 行間
//              .foregroundColor:   Z.vFontColor,                   //フォント色
                .font:              UIFont.systemFont(ofSize: Z.vFontSize),//フォントサイズ
                ], range:NSRange(location:0, length:wAtt.length))   //
            pView.textStorage.setAttributedString(wAtt)             //NSTextViewにデータをsetする。
        } else {                                                    //
            wAtt.append(NSAttributedString(string:""))              //NSTextViewの初期化
            pView.textStorage.setAttributedString(wAtt)             //NSTextViewにデータをsetする。
        }                                                           //
        return true                                                 //♻️return♻️
    }//-------------------------------------------------------------//

編集完了時、UITextViewの保存

    func CallWriteRTFD(_ pView:UITextView, pArray:[NSMutableDictionary],//<*TextViewをHTMLで保存する*>
        pRow:Int)->[NSMutableDictionary] {                          //
        if pRow < 0 || pRow > pArray.count { return pArray }        //♻️return♻️
        else { }                                                    //
        let wData:Data          = try! pView.attributedText!.data(  //
            from:               NSRange(location:0, length:pView.attributedText!.length),//
            documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType:NSAttributedString.DocumentType.html])//html
        let wEncode:UInt        = pArray[pRow][Z.fEncode] as! UInt  //
        pArray[pRow][Z.fNote]   = String(data: wData,               //
                   encoding:String.Encoding(rawValue: wEncode))!//エンコード & saveする
        return pArray                                               //♻️return♻️
    }//??????????????????????????????
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ARアプリのCamera Permission取得テンプレート

はじめに

最近ARKitを使ったAR Cake Dividerというアプリをリリースしました。しかし、初回のリリース時にApple Human Interface GuidelinesのResuesting Permission(以下AHIG)に準拠していないという事でリジェクトを受けました。ARアプリを作る上でCamera Permissionは必須になりますが、XcodeのデフォルトARプロジェクトではAHIGに準拠した形のパーミッション要求を出してくれません。
アプリ作成の度に修正をするのは面倒なのでテンプレートプロジェクトを作成してGitHubで公開しました。

AHIGのResuesting Permissionの要点

  1. アプリは位置情報や連絡先などの情報へのアクセスを、ユーザーがコントロールできる様にするべき
  2. 情報へのアクセス要求はユーザにとって、明らかにアプリがその情報が必要だとわかるときに行うべき
  3. アクセス要求時にシステムから表示されるダイアログには、短く明快になぜアプリはその情報が必要なのか書くべき

この中でどれも重要なのですが、2番目は「アプリがその情報にアクセスできなければ動作できないのであれば、ちゃんと要求を出さなければいけない」ということも意味しています。

具体的には、例えばあるカメラアプリが起動時にカメラを起動させるとして、起動前にパーミッション要求を行ったとします。
一度ユーザが「許可しない」を選択したとして、その後アプリを再起動させました。
そのときアプリは一度パーミッション取得できなかったため、再び要求を出さずそのまま状態だったとします。

これはアプリがその情報が必要なときに要求を行っていないとみなされます。アプリがその情報にアクセスできなければ動作できないのであれば、再度要求を出すことが求められます。

テンプレートプロジェクトのポイント

以上の点を踏まえてテンプレートプロジェクトでは次の機能を実装しています。

  1. アプリは初回起動時に自分がCamera Permissionを持っているか確認する

    let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)

  2. 初回起動時の判定に基づいて、アプリがForegroundになったタイミングで

    • Camera Permissionを持っていなければ要求を出す⇨ requestCameraPermission()
    • Camera Permissionをユーザに断られていたら、アプリの動作に必要な旨のダイアログを表示し、設定アプリへ誘導する⇨ alertCameraAccessNeeded()

そのために、UIApplication.willEnterForegroundNotificationを監視する。
NotificationCenter.default.addObserver(self, selector: #selector(checkPermission), name: UIApplication.willEnterForegroundNotification, object: nil)

  1. ユーザが理解し易い様にダイアログのメッセージは国際化する
    ⇨ InfoPlist.strings、Localizable.strings

スクリーンショット

初回起動時のダイアログ
B.png

「許可しない」を選択した後に再起動した時のダイアログ
A.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSアプリのUIテスト(XCUITest)をBitrise上で実行する

細かいところでハマったのでメモ。

Xcodeプロジェクト側の環境構築・テストコード実装

Xcodeプロジェクト側の追加設定

テスト対象のTargetにUITestのTargetを紐付ける

  • テスト対象のTargetの「Edit Scheme...」を選択する
    Screen_Shot_2019-02-11_at_1_38_07.jpg

  • TestにUITestのTargetを追加する
    Screen_Shot_2019-02-11_at_2_03_13.jpg

  • TestにUITestのTargetが追加されればOK
    Screen_Shot_2019-02-11_at_2_23_49.jpg

  • 上記の修正をリモートリポジトリへプッシュする

Bitrise側の設定

Bitrise側の追加設定

Workflowを修正してテスト実行できるようにする

  • Workflow Editorより、Run CocoaPods installやCarthageの後あたりに「Xcode Test for iOS」を追加する
    Screen_Shot_2019-02-11_at_2_40_51.jpg

  • (オプション)「Export UITest Artifacts」をtrueに変更する ※これを設定しておくとテスト結果をArtifactsに保存できる
    Screen_Shot_2019-02-11_at_2_46_59.jpg

Bitrise上でテスト(ビルド)を実行

  • テストが成功すると、コンソールログに以下のように表示される
    slack-imgs.jpg

  • テストが失敗した場合は、失敗した箇所のスクリーンショットやログがArtifactsに保存されるので、それを使って調査する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LLDBでアドレスからオブジェクトの内容を確認する

例えば、XcodeのDebugging View Hierarchiesを使ってデバッグ中に取得したアドレスから、オブジェクトのより詳細な内容を確認したいときなどがあるかと思います。
そこで、SwiftのunsafeBitCast(_:to:)を使えば簡単にアドレスからオブジェクトの内容を確認することができます。

LLDBでは以下のようになるかと思います。

(lldb) expr -l Swift -- import UIKit
(lldb) expr -l Swift -- let $label = unsafeBitCast(0x7fac81778d00, to: UILabel.self)
(lldb) expr -l Swift -- print($label.font)
Optional(<UICTFont: 0x7fac81779410> font-family: ".SFUIDisplay-Bold"; font-weight: bold; font-style: normal; font-size: 22.00pt)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ChildViewControllerのSafeAreaが正しくない場合がある

特定の条件下においてChildViewControllerのSafeAreaが正しい値に設定されない場合があり、レイアウトが崩れることがあったので記録しておきます。

発生した際の前提条件

環境

  • Xcode10.1
  • iPhoneXシュミレータ(iOS12.1)

Viewヒエラルキー

  • UIViewController
    • UICollectionView
      • UICollectionViewCell
        • UIViewController qiita-safearea.png

事象

上記Viewヒエラルキーにおいて、UICollectionViewCell内のChildViewControllerにSafeAreaが正しくない値が設定されることがありました。
初回に画面内に表示されるセルのChildViewControllerにおいては正しい値が設定されており、画面外のセルのChildViewControllerには正しくない以下の値が設定されていました。
▿ UIEdgeInsets
- top : 0.0
- left : 0.0
- bottom : 0.0
- right : 0.0

頻度は毎回必ず。
親のUICollectionViewCellのSafeAreaまでは正しい値。

ワークアラウンド

親のViewから正しいSafeAreaを受け渡し、愚直に各Viewのレイアウトに反映させる方法をとりました。
SafeArea関連のAPIを調べてみましたがsetterが存在するのは-[UIViewController additionalSafeAreaInsets]だけのようでSafeAreaを上書きする用途だと少し扱いづらいですね。

何か他に良い方法があれば教えて欲しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む