- 投稿日:2019-02-11T19:56:15+09:00
UITabBarItemのiconにオリジナルな画像を設定する方法(ノンコード)
はじめに
- ノンコードです。
- AssetsCatalogを利用します。
できること
- アイコンを単色塗りつぶしではない画像オリジナルな状態で表示できます。
やりかた
- Sampleとしてプロジェクト作成時に利用できるTabbed App(テンプレ)を利用しています。
Assets.xccasetsファイルを開きます。(以降InspectorはAttribute Inspectorに設定)
Firstを選択し、Attributes Inspector内の「Render As」をDefaultから「Original Image」に変更します。★1
3. Main.StoryBoardを開いてFirst View Controllerを確認すると以下のようになっていれば設定OKです。
First View Controller Second View Controller(未変更) ★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で設定可能です。
- 投稿日:2019-02-11T17:40:10+09:00
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にしてください。
2. XcodeからCapabilitiesでAssociatedDomainsの設定をする
この部分で、AssociatedDomainsをONにしておいてください。Dynamic Linksの設定が終わったら追加で記入します。
Dynamic Linksの設定
利用開始
Firebaseのコンソールを開き、対象のプロジェクトを選択して、左側のメニューから「Dynamic Links」を開いてください。この画面はころころ変わりそうな気もするのであれですが、下の画像のような内容が出てると思うので、「始める」を押してください。
サブドメインの設定
サブドメインの設定を求められるのでいい感じのサブドメインを決めましょう。checka!の場合は、
checka.page.link
です。そのままですね。この部分は早いものがちだと思うので、取られていたら仕方ないという感じですね。Dynamic Linkの作成
新規作成
こんな感じの画面になっていると思うので、「新しいダイナミックリンク」を押してください。
パスの設定
Dynamic Linksは短縮URLの作成ができるので、今回の用途的には少し違和感があるかもしれません。URL接頭辞として、すきな文字列を入力しましょう。checka!のグループ参加の場合は
/join_group
しました。ちなみに、2019/02/11現在ではパスを切ることはできませんので、/group/join
にはできませんでした、残念。ディープリンクの設定
今回は使わないので、ディープリンクURLはAppStoreのURLを入れて、リンク名もアプリ名を入れておきましょう。
iOS用の設定
下の「ディープリンクをiOSアプリで開く」にチェックを付けて、セレクトボックスから対象のアプリを開きましょう。ここで選択されたアプリのbundleIDが、端末にインストールされているアプリのbunldeIDと異なっていると、Universal Linksによるアプリ起動ができないのでご注意ください。また、AppStoreIDやTeamIDの入力が必要になります。AppStoreIDは、checka!だと
https://itunes.apple.com/jp/app/id1451433619
の数字の部分、TeamIDはAppleDevelopterのAppIDのPrefixの部分です。選択すると、下記のような項目がでてきます。ここでは、アプリがインストールされていない場合の挙動を設定できます。自分は上の「アプリのAppStoreページ」を選択しましたが、必要であればカスタムURLの設定を行うことができるようです。
Android用の設定
Androidでも利用する場合は設定を行う必要があります。今回は割愛します。
詳細オプション
checka! では、一番下のチェックボックスにはチェックを入れています。ここのチェックを入れていない場合、インストールされていない端末で開くと、Firebase側の画面に一度アクセスして、そこにボタンがあり、そのボタンを押すとAppStoreへアクセスすることになってしまい、自分としては微妙でした。よって、チェックをいれています。
完了
ここまでで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
- 投稿日:2019-02-11T16:32:05+09:00
three.jsをスマホアプリで動かして暖をとる(react-native-webglの導入手順)
はじめに
この記事では、react-native-webglというライブラリの導入手順について書きます。
react-native-webglは、React NativeでWebGLを使うためのライブラリです。
これを使えばネイティブアプリでthree.jsが使えるようになるので、試しに作ってみました!とりあえずスマホカイロにしてみましたが、誰かかっこいいアプリ作ってくれないかな・・・
iOS: https://itunes.apple.com/us/app/three-js-native/id1447928256
Android: https://play.google.com/store/apps/details?id=com.three
- アプリ内の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
Android
issueを読みつつ諸々を書き換えます。
android/build.gradlebuildscript { 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.9node_modules/react-native-webgl/android/build.gradletask 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.jsconst THREE = require("three"); global.THREE = THREE; if (!window.addEventListener) window.addEventListener = () => { }; require("three/examples/js/renderers/Projector"); export default THREE;App.jsimport 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を使ったかっこいいアプリがさらに増えていくと嬉しいですね!
- 投稿日:2019-02-11T15:15:38+09:00
iOSアプリのデバッグ方法まとめ
とりあえずprintしてみる
基本ですね。
print(anyVariant) //変数の中身が見たいとき print(type(of: anyVariant)) //変数の型が知りたい中身が見たいときでなくても、そのロジック通ってるか見たいときにも使えます。
原始的なデバッグ法です。
あまりお行儀はよくない扱いみたいで、次に紹介するブレイクポイントを仕込むほうがまっとうなやり方でしょう。ブレイクポイントを仕込む
Xcodeの機能で、ブレイクポイントというものがあります。
使い方は簡単です。
↑この例では、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))
- 投稿日:2019-02-11T14:46:52+09:00
UITableView の区切り線をカラフルにしたい!
こんな感じにしたい!
すべての区切り線が同じ色で良い場合
全部同じで良い場合には、
tableView.separatorColor
で対応できます。
例えば、下記の例では、区切り線は赤色になります。swiftoverride func viewDidLoad() { super.viewDidLoad() tableView.separatorColor = .red }ではそれぞれのセルで異なる色にするにはどうすれば良いか…
カスタムセルでやる!
実際のところ、カスタムセルでも区切り線の色を個別に変えることはできなそうです
なので、自分で区切り線 (っぽいUIview
) を作ることで対応します。下記のように、セルの下部に区切り線に見えるような
UIView
を配置し、区切り線に見せかけます。swiftclass 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
)。swiftoverride func viewDidLoad() { super.viewDidLoad() tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell") tableView.separatorStyle = .none }カスタムセルを使うようにする。
swiftoverride 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 }これで、あたかもカラフルな区切り線ができたかのように見せることができます!
終わりに
今回の例のように区切り線をカスタマイズしたいという要望は少ないかもしれません。
ですが、どうしてもそういうデザインにしたいけれども、提供されているプロパティでは対処できない時には、
自分でそう見えるように工夫するという選択肢が頭の中にあると、解決に一歩近づけるのかなと思っています。
- 投稿日:2019-02-11T13:01:39+09:00
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) }) }アプリで利用する
アプリ上でハッシュ化してパスワードを保存する場合、
- パスワードをハッシュ化する
- 入力されたパスワードと保存済のハッシュ化されたパスワードを比較
という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のリファレンスでもパスワードはキーチェーンへ保存するよう記載されています。
ただ、やっぱりパスワード系はハッシュ化していないと心配で眠れないとか、なんか気持ち悪いって場合は、そんなに実装コストのかかるものでも無いのでさくっとハッシュ化してしまいましょう!
ただし、ハッシュ化して保存すると元のパスワードに戻せなくなる為、平文のパスワードが必要な場合はハッシュ化ではなく暗号化での保存が必要です。例えば、Safariのパスワード保存のようにアプリから別サービスへパスワードを使ってログインをするような場合は、暗号化で保存しないといけません。 ↩
https://github.com/OWASP/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md ↩
- 投稿日:2019-02-11T12:12:43+09:00
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
Norbertは、ビジュアル環境でウィジェットを作成するためのこのツールを作成しました。クイックヒント:テキストURLをクリック可能なハイパーリンクに変換する
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コードの共有州管理デモ
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シリアライザの作成
カスタムシリアライザを作成する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アプリ。
- 投稿日:2019-02-11T11:40:58+09:00
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-rulesCyclomatic 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 DispatchDynamic 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? IntForce Try
強制トライ(
try!
)は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-tryfunc a() throws { } // bad try! a() // good do { try a() } catch { // エラー処理 }Function Body Length
関数内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-body-lengthFunction 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 = 0Implicit 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 // fooLegacy 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.piLegacy 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-lengthMark
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-superprotocol 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? = 0Redundant 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: IntRedundant 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 -> VoidReturning 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 /= 1Statement Position
else
とcatch
は、前の定義の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-commandSwitch 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
TODO
とFIXME
は解決すべきです。
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-newlineTrailing Semicolon
行の末尾にセミコロンを付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-semicolon// bad let a = 0; // good let a = 0Trailing Whitespace
行の末尾に半角スペースを付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-whitespace// bad let a = 1 // good let a = 1Type Body Length
型内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#type-body-lengthType 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-switchswitch 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-valuevar 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-ibinspectableclass 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-whitespaceVoid 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-propertyclass 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-delegateclass Foo { // bad var delegate: SomeProtocol? // good weak var delegate: SomeProtocol? }XCTFail Message
XCTFail
の呼び出しにはアサーションの説明を含めるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#xctfail-messagefunc 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 SourceKittenFrameworkClosure 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: BoolDiscouraged 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
count
を0
と比較するよりisEmpty
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-count// bad [Int]().count == 0 [Int]().count > 0 [Int]().count != 0 // good [Int]().isEmptyEmpty String
空の文字列と比較するより
isEmpty
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-string// bad myString == "" myString != "" // good myString.isEmpty !myString.isEmptyEmpty 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-selfstruct 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-interfaceclass 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-nameFirst 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 == barImplicit 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? = 42Joined 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
let
とvar
は他のステートメントと空白行で区切るべきです。
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 = 2Literal 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-orderpublic 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_1Object 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 + 2Overridden 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-extensionextension 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-keywordsswitch 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
@IBAction
はprivate
にすべきです。
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
@IBOutlet
はprivate
にすべきです。
∵上位レイヤーへ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-builderclass 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-callQuick Discouraged Focused Test
こちらも上記と同様にわかりません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-focused-testQuick Discouraged Pending Test
こちらも上記と同様にわかりません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-pending-testRedundant 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 ?? 0Redundant 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().first
やsorted().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 DDDStatic 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 _ = aVertical 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
XCTAssertEqual
やXCTAssertNotEqual
より特定の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の導入が便利です。
- 投稿日:2019-02-11T09:08:50+09:00
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♻️ }//??????????????????????????????
- 投稿日:2019-02-11T09:08:50+09:00
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♻️ }//??????????????????????????????
- 投稿日:2019-02-11T07:36:07+09:00
ARアプリのCamera Permission取得テンプレート
はじめに
最近ARKitを使ったAR Cake Dividerというアプリをリリースしました。しかし、初回のリリース時にApple Human Interface GuidelinesのResuesting Permission(以下AHIG)に準拠していないという事でリジェクトを受けました。ARアプリを作る上でCamera Permissionは必須になりますが、XcodeのデフォルトARプロジェクトではAHIGに準拠した形のパーミッション要求を出してくれません。
アプリ作成の度に修正をするのは面倒なのでテンプレートプロジェクトを作成してGitHubで公開しました。AHIGのResuesting Permissionの要点
- アプリは位置情報や連絡先などの情報へのアクセスを、ユーザーがコントロールできる様にするべき
- 情報へのアクセス要求はユーザにとって、明らかにアプリがその情報が必要だとわかるときに行うべき
- アクセス要求時にシステムから表示されるダイアログには、短く明快になぜアプリはその情報が必要なのか書くべき
この中でどれも重要なのですが、2番目は「アプリがその情報にアクセスできなければ動作できないのであれば、ちゃんと要求を出さなければいけない」ということも意味しています。
具体的には、例えばあるカメラアプリが起動時にカメラを起動させるとして、起動前にパーミッション要求を行ったとします。
一度ユーザが「許可しない」を選択したとして、その後アプリを再起動させました。
そのときアプリは一度パーミッション取得できなかったため、再び要求を出さずそのまま状態だったとします。これはアプリがその情報が必要なときに要求を行っていないとみなされます。アプリがその情報にアクセスできなければ動作できないのであれば、再度要求を出すことが求められます。
テンプレートプロジェクトのポイント
以上の点を踏まえてテンプレートプロジェクトでは次の機能を実装しています。
アプリは初回起動時に自分がCamera Permissionを持っているか確認する
⇨let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
初回起動時の判定に基づいて、アプリがForegroundになったタイミングで
- Camera Permissionを持っていなければ要求を出す⇨
requestCameraPermission()
- Camera Permissionをユーザに断られていたら、アプリの動作に必要な旨のダイアログを表示し、設定アプリへ誘導する⇨
alertCameraAccessNeeded()
そのために、UIApplication.willEnterForegroundNotificationを監視する。
⇨NotificationCenter.default.addObserver(self, selector: #selector(checkPermission), name: UIApplication.willEnterForegroundNotification, object: nil)
- ユーザが理解し易い様にダイアログのメッセージは国際化する
⇨ InfoPlist.strings、Localizable.stringsスクリーンショット
- 投稿日:2019-02-11T03:15:09+09:00
iOSアプリのUIテスト(XCUITest)をBitrise上で実行する
細かいところでハマったのでメモ。
Xcodeプロジェクト側の環境構築・テストコード実装
この辺りの記事を参考にしながらやる
【Swift】初めてのUITest導入ローカルでテストが成功するところまで確認すること
Xcodeプロジェクト側の追加設定
テスト対象のTargetにUITestのTargetを紐付ける
上記の修正をリモートリポジトリへプッシュする
Bitrise側の設定
この辺りの記事を参考にしながらやる
[iOS] Bitriseを導入する手順について証明書やProvisioning Profile周りはfastlane matchを使って管理することを推奨
Bitrise側の追加設定
Workflowを修正してテスト実行できるようにする
Workflow Editorより、Run CocoaPods installやCarthageの後あたりに「Xcode Test for iOS」を追加する
(オプション)「Export UITest Artifacts」をtrueに変更する ※これを設定しておくとテスト結果をArtifactsに保存できる
Bitrise上でテスト(ビルド)を実行
- 投稿日:2019-02-11T01:32:09+09:00
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)
- 投稿日:2019-02-11T00:58:07+09:00
ChildViewControllerのSafeAreaが正しくない場合がある
特定の条件下においてChildViewControllerのSafeAreaが正しい値に設定されない場合があり、レイアウトが崩れることがあったので記録しておきます。
発生した際の前提条件
環境
- Xcode10.1
- iPhoneXシュミレータ(iOS12.1)
Viewヒエラルキー
事象
上記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を上書きする用途だと少し扱いづらいですね。何か他に良い方法があれば教えて欲しいです。