RxSwiftでUIActivityIndicatorViewのローディングを実装する

概要

RxSwiftでUIActivityIndicatorViewをローディングさせたかったがやり方が分からなかったのでメモとして残してみます。 クラス構成は適当ですが、MVVMを意識しています。

ViewModelの実装

ViewModel.swift

import RxSwift
import RxCocoa

class ViewModel {

    final let isAnimating = Variable(false)
    
    func showIndicator() {
        isAnimating.value = true
    }
    
    func hiddenIndicator() {
        isAnimating.value = false
    }
}

ViewControllerの実装

ViewController.swift

import RxSwift
import RxCocoa

class ViewController {
    @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!  // IBOutletの接続は行う事

    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        
          // isAnimatingのフラグをIndicatorのisAnimatingに連携させる
        viewModel.isAnimating.asDriver()
            .drive(activityIndicatorView.rx.isAnimating)
            .disposed(by: disposeBag)
        // isAnimatingのフラグをIndicatorのisHiddenに連携させる
        viewModel.isAnimating.asDriver()
            .map { !$0 }
            .drive(activityIndicatorView.rx.isHidden)
            .disposed(by: disposeBag)
    }

    @IBAction func tapShowIndicator() {
        showIndicator() 
    }

    @IBAction func tapHiddenIndicator() {
        hiddenIndicator() 
    }
}

説明

ViewModelでフラグをVariableと宣言します。Variable(false)とする事でRxのストリームでfalseのフラグが流れるようになります。 ViewController側でそれをキャッチ(.asDriver())してbind(.drive(activityIndicatorView.rx.isHidden))させることによって フラグから@IBOutletへの接続(連携)実装が可能になります。

あとはViewController側でUIButtonをtapShowIndicatortapShowIndicatorをそれぞれ紐づけて シミュレータでボタンをタップすればindicatorがクルクル回るようになります。

もちろん、UIButtonもRxSwift実装すれば@IBActionが不要になりますがそれはまた別の機会で説明します。

グーグル検索でここまで感動したことはあっただろうか?

エンジニアのグーグル検索についてのテクニック

をまとめたいと思う。 特にiOSエンジニアの場合にはXcodeのコンソール上で意味不明なエラー文言が起きた時には どうやって検索すべきかを悩んでしまう。 プログラマとして日々バグのデバッグの解決法としてグーグルで検索した時にQiitaの記事がヒットしてくれたら非常にラッキーでありますが、 大体はStackOverFlowのページが検索されると思います。

幸福度の順番で言えば

Qiita > StackOverFlow > 日本人のブログ記事 > 英語の記事

の順番かな。

ですが、プログラマとしてブログに情報発信するようになってからはトレンドを追う癖ができてしまって 何かを調べている時に日本語の記事がヒットすると逆にイラついてしまいます。

トレンドな情報の多くはやっぱり英語の記事が多いので 初めからグーグルさんが英語だけに絞った検索をしてくれたらいいのになと思っていたところ そのテクニックが分かりましたので共有します。

英語サイトのみをGoogle検索結果に表示させる方法

グーグルのトップ画面にアクセスします。

Google

f:id:qed805:20190209001100p:plain
google

  1. 検索窓の下の「設定」ボタンをクリックします。

  2. 言語を選択します

f:id:qed805:20190209001245p:plain
settei

  1. 言語選択画面が表示されますので「English」を選び「保存」をクリックします。

f:id:qed805:20190209001403p:plain
language

クリックで表示された検索結果のページの多くが英語の記事になっています。

f:id:qed805:20190209001512p:plain
english

これで英語だけの検索結果になりトレンドな情報を追いやすくなりました。 仕事では意外に便利な機能なのでぜひ活かしてもらえれば幸いです。

Swift4.2で導入された機能 Dynamic Member LoopupでSwiftのコードをよりCOOLにしよう

概要

Swift4.2で導入されたDynamic Member Lookupの機能について紹介します。

といっても使い方は@dynamicMemberLookupアノテーションを付与すれば使えるようになりますが、この機能でSwiftのコードがどれだけ可読性が上がるのかを理解できるようになります。

参考記事

この辺りが参考になります。
今後、Custom subscriptは機械学習の発展によって活用の場が増えてくると思いますのでこれを機に流れだけでも把握するのはアリだと思います。

また、こちらは下記の記事を翻訳したものです。

Custom Subscripts in Swift

では始めて行きます!

導入

独自の型を添字で拡張する方法を学び、ネイティブの配列や辞書と同じように単純な構文でそれらに添字を付けることができるようにします。

Custom subscriptsは、コードの利便性と読みやすさを向上させる強力な言語機能です。

演算子のオーバーライドと同様に、Custom subscriptsを使用すると、ネイティブのSwift構文を使用できます。 より冗長なcheckerBoard.objectAt(x:2、y:3)ではなく、checkerBoard [2] [3]のような簡潔なものを使用できます。

このチュートリアルでは、playgroundで基本的なチェッカーゲームの基礎を築くことによってCustom subscriptsを探ります。あなたはそれがボードの周りにピースを動かすために購読を使うことがどれほど簡単であるかわかります。 あなたが終わったら、あなたはあなたの指のすべての暇な時間中にあなたの指を占有し続けるために新しいゲームを作るためにあなたの方法でうまくいくでしょう。

そしてあなたはsubscriptsについてももっともっと知っているでしょう!

このチュートリアルは、あなたがすでにSwift開発の基本を知っていることを前提としています。 もしSwiftを初めて使用するのであれば、私達の初心者向けSwiftチュートリアルをチェックするか、あるいはまずSwift Apprenticeを読んでください。

Getting Started(はじめに)

まず始めに、新しいplaygroundを作りましょう。 File▸New▸Playground…の順に進み、iOS▸Blankのテンプレートを選択し、次へをクリックします。 ファイルにSubscripts.playgroundという名前を付けて、[Create]をクリックします。

デフォルトのテキストを次のように置き換えます。

Subscripts.playground.swift
import Foundation

struct Checkerboard {
  enum Square: String {
    case empty = "▪️"
    case red = "🔴"
    case white = "⚪️"
  }

  typealias Coordinate = (x: Int, y: Int)

  private var squares: [[Square]] = [
    [ .empty, .red,   .empty, .red,   .empty, .red,   .empty, .red   ],
    [ .red,   .empty, .red,   .empty, .red,   .empty, .red,   .empty ],
    [ .empty, .red,   .empty, .red,   .empty, .red,   .empty, .red   ],
    [ .empty, .empty, .empty, .empty, .empty, .empty, .empty, .empty ],
    [ .empty, .empty, .empty, .empty, .empty, .empty, .empty, .empty ],
    [ .white, .empty, .white, .empty, .white, .empty, .white, .empty ],
    [ .empty, .white, .empty, .white, .empty, .white, .empty, .white ],
    [ .white, .empty, .white, .empty, .white, .empty, .white, .empty ]
  ]
}

Checkerboardには3つの定義があります。

  • Square はボード上の個々の正方形の状態を表します。 .emptyは空の正方形を表し、.red.whiteはその正方形上の赤または白の部分の存在を表します。
  • Coordinateは2つの整数のタプルの別名です。 ボード上の正方形にアクセスするには、このタイプを使用します。
  • squaresはボードの状態を格納する2次元配列です。

次にこちらを追加します。

Subscripts.playground.swift
extension Checkerboard: CustomStringConvertible {
  var description: String {
    return squares.map { row in row.map { $0.rawValue }.joined(separator: "") }
        .joined(separator: "\n") + "\n"
  }
}

これはCustomStringConvertibleに適合を追加するための拡張です。 カスタムなdescriptionを使用すると、checkerboardをコンソールに印刷できます。

View▸Debug Area▸Show Debug Areaの表示の順に選択してコンソールを開き、playgroundの下部に以下の行を入力します。

Subscripts.playground.swift
var checkerboard = Checkerboard()
print(checkerboard)

このコードはCheckerboardインスタンスを初期化します。 その後、CustomStringConvertible実装を使用してdescriptionプロパティをコンソールに出力します。

[Execute Playground]ボタンを押すと、コンソールの出力は次のようになります。

▪️🔴▪️🔴▪️🔴▪️🔴
🔴▪️🔴▪️🔴▪️🔴▪️
▪️🔴▪️🔴▪️🔴▪️🔴
▪️▪️▪️▪️▪️▪️▪️▪️
▪️▪️▪️▪️▪️▪️▪️▪️
⚪️▪️⚪️▪️⚪️▪️⚪️▪️
▪️⚪️▪️⚪️▪️⚪️▪️⚪️
⚪️▪️⚪️▪️⚪️▪️⚪️▪️

Getting and Setting Pieces (ピースの取得と設定)

コンソールを見ると、どの部分が特定の正方形を占めているかを知るのは非常に簡単ですが、あなたのプログラムはまだそのような力を持っていません。 squares配列はprivate(非公開)なので、どのプレイヤーが指定された座標にいるのかわかりません。

ここで重要なポイントがあります:正方形の配列はボードの実装です。 ただし、Checkerboardのユーザーは、このタイプの実装については何も知りません。

型はその内部実装の詳細からユーザを保護するべきです。 そのため、正方形配列がprivateになります。

squares配列を割り当てる場所の後に、Checkerboardに次のメソッドを追加します。

Subscripts.playground.swift
func piece(at coordinate: Coordinate) -> Square {
  return squares[coordinate.y][coordinate.x]
}

mutating func setPiece(at coordinate: Coordinate, to newValue: Square) {
  squares[coordinate.y][coordinate.x] = newValue
}

配列に直接アクセスするのではなく、Coordinateタプルを使用して - squaresにアクセスする方法に注目してください。 実際の格納メカニズムは配列の配列です。それはまさにあなたがユーザから保護するべきである実装の詳細の一つです!

Defining Custom Subscripts (Custom Subscriptsを定義する)

お気づきかもしれませんが、これらのメソッドはプロパティのgetterメソッドとsetterメソッドの組み合わせのように非常によく似ています。 多分それらをcomputed propertyとして実装するべきか?

残念ながらそれはうまくいきません。これらのメソッドはCoordinateパラメータを必要とし、computed propertiesはパラメータを持つことができません。それは方法にこだわっているということですか?

この特別なケースが、まさにsubscriptsの目的です。

subscriptsの定義方法を見てください。

Subscripts.playground.swift
subscript(parameterList) -> ReturnType {
  get {
    // return someValue of ReturnType
  }

  set (newValue) {
    // set someValue of ReturnType to newValue
  }
}

Subscriptの定義は、関数定義とcomputed property定義の両方の構文を混在させます。

  • 最初の部分は、パラメータリストと戻り値の型を含む、関数定義とよく似ています。funcキーワードと関数名の代わりに、特別なsubscriptキーワードを使用します。
  • メインは、ゲッターとセッターを持つcomputed propertyによく似ています。

関数とプロパティの構文の組み合わせは、subscriptsの力を際立たせています。 これはインデックス付きコレクションの要素にアクセスするためのショートカットを提供します。 これについてはすぐに詳しく説明しますが、まず、次の例を検討してください。

piece(at :)setPiece(at:to :)を次のsubscriptに置き換えます。

Subscripts.playground.swift
subscript(coordinate: Coordinate) -> Square {
  get {
    return squares[coordinate.y][coordinate.x]
  }
  set {
    squares[coordinate.y][coordinate.x] = newValue
  }
}

このsubscriptのgetterおよびsetterは、それらが置き換えるメソッドを実装するのとまったく同じ方法で実装します。

  • Coordinateを指定すると、ゲッターは列と行に正方形を返します。
  • Coordinatevalueが与えられると、セッターは列と行の四角形にアクセスしてその値を置き換えます。

playgroundの最後に次のコードを追加して、新しいsubscriptにテストドライブを付けます。

Subscripts.playground.swift
let coordinate = (x: 3, y: 2)
print(checkerboard[coordinate])
checkerboard[coordinate] = .white
print(checkerboard)

playgroundは(3、2)の部分が赤であることをあなたに教えてくれるでしょう。白に変更すると、コンソールの出力は次のようになります。

▪️🔴▪️🔴▪️🔴▪️🔴
🔴▪️🔴▪️🔴▪️🔴▪️
▪️🔴▪️⚪️▪️🔴▪️🔴
▪️▪️▪️▪️▪️▪️▪️▪️
▪️▪️▪️▪️▪️▪️▪️▪️
⚪️▪️⚪️▪️⚪️▪️⚪️▪️
▪️⚪️▪️⚪️▪️⚪️▪️⚪️
⚪️▪️⚪️▪️⚪️▪️⚪️▪️

Comparing Subscripts, Properties and Functions (Subscripts、プロパティ、関数の比較)

Subscriptsは多くの点でcomputed propertiesと似ています。

  • それらはgetterとsetterから成ります。
  • setterはオプションです。つまり、Subscriptsはread-write(読み書き可能)またはread-only(読み取り専用)にすることができます。
  • 読み取り専用のSubscriptsには、明示的なgetブロックやsetブロックは必要ありません。 全体がgetterです。
  • setterには、subscriptの戻り値の型と等しい型を持つデフォルトパラメータnewValueがあります。 通常、このパラメータを宣言するのは、名前をnewValue以外の名前に変更するときだけです。
  • ユーザーはsubscriptsが速いことを望み、できればO(1)なので、短くてきれいにしてください。

checkers.png

computed propertiesとの大きな違いは、subscripts自体にはプロパティ名がないことです。 演算子オーバーライドと同様に、subscriptsを使用すると、コレクションの要素にアクセスするために通常使用される言語レベルの角かっこ[]を上書きできます。

Subscriptsもパラメータリストと戻り値の型を持つという点で関数と似ていますが、次の点で異なります。

  • Subscriptパラメータには、デフォルトでは引数ラベルがありません。 それらを使用したい場合は、それらを明示的に追加する必要があります。
  • Subscriptsには、inoutまたはdefaultパラメータを使用できません。 ただし、可変長(...)パラメータは使用できます。
  • Subscriptsはエラーをスローできません。 つまり、subscriptゲッターはその戻り値を通じてエラーを報告する必要があり、subscriptセッターはエラーをスローまたは返すことはできません。

Adding a Second Subscript (2番目の添え字を追加する)

subscriptsが関数に似ているもう1つのポイントがあります。それらはオーバーライドされる可能性があるということです。つまり、パラメータリストや戻り値の型が異なる限り、1つの型に複数のsubscriptsを付けることができます。

Checkerboardの既存のsubscript定義の後に次のコードを追加します。

Subscripts.playground.swift
subscript(x: Int, y: Int) -> Square {
  get {
    return self[(x: x, y: y)]
  }
  set {
    self[(x: x, y: y)] = newValue
  }
}

このコードは、Coordinateタプルではなく2つの整数を受け入れる2番目のsubscriptをCheckerboardに追加します。

playgroundの最後に次の行を追加して、この新しいsubscriptを試してください。

Subscripts.playground.swift
print(checkerboard[1, 2])
checkerboard[1, 2] = .white
print(checkerboard)

(1、2)の部分が赤から白に変わります。

Using Dynamic Member Lookup

Swift 4.2の新機能は、dynamic member lookupの言語機能です。これにより、型にランタイムプロパティを定義できます。つまり、ドット(.)表記を使用して値やオブジェクトにインデックスを付けることができますが、特定のプロパティを事前に定義する必要はありません。

これはデータベースやリモートサーバーのオブジェクトのように、オブジェクトに実行時に定義された内部データ構造がある場合に最も役立ちます。 別の言い方をすればこれはNSObjectサブクラスを必要とせずにSwiftにキー値コーディングをもたらします。

この機能には2つの部分が必要です。@dynamicMemberLookupアノテーションと特別な形式のsubscriptです。

A Third Subscript

まずsubscriptのオーバーライドをもう1つ追加して、dynamic lookupの基盤を築きます。 これは文字列を使ってcoordinateを定義します。

上記のsubscript定義の下に次のコードを追加します。

Subscripts.playground.swift
private func convert(string: String) -> Coordinate {
  let expression = try! NSRegularExpression(pattern: "[xy](\\d+)")
  let matches = expression
    .matches(in: string,
             options: [],
             range: NSRange(string.startIndex..., in: string))
  let xy = matches.map { String(string[Range($0.range(at: 1), in: string)!]) }
  let x = Int(xy[0])!
  let y = Int(xy[1])!
  return (x: x, y: y)
}

subscript(input: String) -> Square {
  get {
    let coordinate = convert(string: input)
    return self[coordinate]
  }
  set {
    let coordinate = convert(string: input)
    self[coordinate] = newValue
  }
}

このコードはいくつかのことを追加します。

  1. 最初に、convert(string :)はx#y#(ここで、 '#'は数字です)の形式の文字列を取り、x値とy値を持つCoordinateを返します。正規表現のパターンが一致しなかった場合は通常エラーをスローしますが、subscriptsはエラーをthrowすることができないため、とにかくクラッシュ以外にできることは多くないため、この場合はtryが強制されます。
  2. それから新しく導入されたsubscriptは文字列を取り、それをCoordinateに変換し、そして先に定義された最初のCoordinateを再利用します。

次の行をplaygroundに追加して、これを試してみてください

Subscripts.playground.swift
print(checkerboard["x2y5"])
checkerboard["x2y5"] = .red
print(checkerboard)

今回は、6行目の白の1つが赤に変わります。

▪️🔴▪️🔴▪️🔴▪️🔴
🔴▪️🔴▪️🔴▪️🔴▪️
▪️⚪️▪️⚪️▪️🔴▪️🔴
▪️▪️▪️▪️▪️▪️▪️▪️
▪️▪️▪️▪️▪️▪️▪️▪️
⚪️▪️🔴▪️⚪️▪️⚪️▪️
▪️⚪️▪️⚪️▪️⚪️▪️⚪️
⚪️▪️⚪️▪️⚪️▪️⚪️▪️

Implementing Dynamic Member Lookup

これまでのところ、これはdynamic member lookuではなく、特別な形式の単なる文字列インデックスです。 次に、あなたはシンタックスシュガーを振りかけます。

まず、playgroundの最上部、structキーワードの直前に次の行を追加します。

Subscripts.playground.swift
@dynamicMemberLookup

それから他のsubscript定義の下に以下を追加してください。

Subscripts.playground.swift
subscript(dynamicMember input: String) -> Square {
  get {
    let coordinate = convert(string: input)
    return self[coordinate]
  }
  set {
    let coordinate = convert(string: input)
    self[coordinate] = newValue
  }
}

これは最後のsubscriptと同じですが、特別な引数label:dynamicMemberがあります。このsubscriptシグネチャとタイプのアノテーションを使用すると、ドットシンタックスを使用してCheckerboardにアクセスできます。

うわー、すぐに文字列インデックスは角かっこ([])の中にある必要はありません。 インスタンス上で直接文字列にアクセスできます。

これらの最後の行をplaygroundの一番下に追加することで実際にそれを見てください。

Subscripts.playground.swift
print(checkerboard.x6y7)
checkerboard.x6y7 = .red
print(checkerboard)

playgroundをもう一度走らせると、最後の白い部分が赤に変わります。

▪️🔴▪️🔴▪️🔴▪️🔴
🔴▪️🔴▪️🔴▪️🔴▪️
▪️⚪️▪️⚪️▪️🔴▪️🔴
▪️▪️▪️▪️▪️▪️▪️▪️
▪️▪️▪️▪️▪️▪️▪️▪️
⚪️▪️🔴▪️⚪️▪️⚪️▪️
▪️⚪️▪️⚪️▪️⚪️▪️⚪️
⚪️▪️⚪️▪️⚪️▪️🔴▪️

A Word of Warning

Dynamic lookupは、コードを非常にきれいにする強力な機能、特にサーバーやスクリプトコードです。 ドット表記でアクセスするので、コンパイル時にオブジェクトの構造を定義する必要はもうありません。

それでも、いくつかの危険な欠点があります。

たとえば、@dynamicMemberLookupアノテーションは基本的に、プロパティ名の有効性をチェックしないようにコンパイラに指示します。型チェックと明示的に定義されたプロパティの補完はまだ得られますが、今度はピリオドの後に何でも置くことができ、コンパイラはエラーを出しません。 あなたがタイプミスをした場合にのみ、実行時に見つけるでしょう。

この行をplaygroundに追加しても、実行するまでエラーにはなりません。

Subscripts.playground.swift
checkerboard.queen

Where to Go From Here?

このチュートリアルの上部または下部にあるDownload Materialsボタンを使用して、最終的なplaygroundをダウンロードできます。

ツールキットにsubscriptsを追加したので、自分のコードでそれらを使用する機会を探しましょう。適切に使用されると、それらはあなたのコードをより読みやすく直観的にします。

そうは言っても、いつもsubscriptsに戻ることを望まないと思います。APIを書いている場合、ユーザーはインデックス付きコレクションの要素にアクセスするためにsubscriptsを使うことに慣れています。 他のものにそれらを使用することは不自然で強制されるでしょう。

subscriptsの詳細については、AppleThe Swift Programming Languageドキュメントのこの章を参照してください。

来たるSwift5の新機能をまとめました

概要

1/25(金)の仕事終わりにツイッターでこのようなつぶやきがタイムラインで上がってきていた。

Swift 5 Release Notes for Xcode 10.2 beta

とうとう、Swift5がリリースされるそうでした。

ええと、Swift4.2がリリースされてからまだ半年しか経っていないのですが、、、
時代の流れが早いそうです。
(現場によってはまだSwift3の所もあるかと思います。)

Swift4.2でパワーアップしたことについてをまとめました

そんな来たるSwift5のために
Swift5で結局何が変わるの?というのを要点をまとめて整理しようと思います。

因みに元記事はこちらとなります。

What’s new in Swift 5.0

では始めて行きたいと思います。


始めに

Swift 5.0はSwiftの次のメジャーリリースであり、やっとABIの安定性をもたらす予定です。 それだけではありません。Raw strings、将来のenumケース、整数倍数のチェックなど、いくつかの重要な新機能がすでに実装されています。

来年初め(2019/1月)にSwift 5.0をリリースする前に試してみたい場合は、最新のSwift trunk開発スナップショットをダウンロードし、現在のXcodeバージョン内でアクティブ化してから、以下の私の例に従ってください。

Raw strings

SE-0200では、Raw stringsを作成する機能が追加されました。バックスラッシュと引用符(quote marks)は、文字や文字列の終端文字をエスケープするのではなく、それらのリテラル記号として解釈されます。 これにより、多くのユースケースがより簡単になりますが、特に正規表現が役立ちます。

Raw stringsを使用するには、次のように文字列の前に1つ以上の記号を配置します。

Sample.swift
let rain = #"The "rain" in "Spain" falls mainly on the Spaniards."#

文字列の最初と最後の記号は文字列の区切り文字の一部になるため、Swiftは、"rain"と"Spain"を囲むスタンドアロンの引用符は、文字列を終了するのではなくリテラル引用符として扱う必要があると理解します。

Raw stringsでもバックスラッシュを使うことができます。

Sample.swift
let keypaths = #"Swift keypaths such as \Person.name hold uninvoked references to properties."#

これはバックスラッシュを文字列内のエスケープ文字ではなくリテラル文字として扱います。 これは、文字列補間の動作が異なることを意味します。

Sample.swift
let answer = 42
let dontpanic = #"The answer to life, the universe, and everything is \#(answer)."#

私はいかにして文字列補間を使うために\#(answer)を使ったかに注意してください - 通常の\(answer)は文字列内の文字として解釈されるので、Raw stringsで文字列補間を行いたいときはを追加しなければなりません。

SwiftのRaw stringsのおもしろい機能の1つは、始めと終わりにハッシュ記号を使用することです。ここで良い例を出すのは非常に珍しく難しいのですが、この文字列を考慮してください。私の犬は "woof" #gooddogと言いました。ハッシュの前にスペースがないので、Swiftは "#を見てすぐにそれを文字列の終端文字として解釈します。このような状況では、デリミタを#"から##"に変更する必要があります。

Sample.swift
let str = ##"My dog said "woof"#gooddog"##

最後のハッシュの数が最初のハッシュの数とどのように一致しなければならないかに注意してください。
Raw stringsは、Swiftの複数行の文字列システムと完全に互換性があります。開始するには#"""を使用し、次に終了するには"""#を使用するだけです。

Sample.swift
let multiline = #"""
The answer to life,
the universe,
and everything is \#(answer).
"""#

多くのバックスラッシュを使わずにできるという事は、正規表現で特に役立ちます。たとえば、\Person.nameのようなキーパスを見つけるための単純な正規表現を書くと、これは次のようになります。

Sample.swift
let regex1 = "\\\\[A-Z]+[A-Za-z]+\\.[a-z]+"

Raw stringsのおかげで、バックスラッシュの半分の数で同じことを書くことができます。

Sample.swift
let regex2 = #"\\[A-Z]+[A-Za-z]+\.[a-z]+"#

正規表現でも使用されているので、まだいくつか必要です。

Dynamically callable types

SE-0216ではSwiftに新しい@dynamicCallable属性が追加されました。これにより、型を直接呼び出し可能としてマークすることができます。 これは、コンパイラの魔法のようなものではなく、シンタックスシュガーでありこのコードを効果的に変換します。

Sample.swift
let result = random(numberOfZeroes: 3)

これに:

Sample.swift
let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])

以前、私はSwift 4.2の@dynamicMemberLookupという機能について書きました。 @dynamicCallable@dynamicMemberLookupの自然な拡張であり、同じ目的を果たします。SwiftコードPythonJavaScriptなどの動的言語と連携して動作するのを容易にするためです。

この機能を独自の型に追加するには、@dynamicCallable属性とこれらのメソッドの一方または両方を追加する必要があります。

Sample.swift
func dynamicallyCall(withArguments args: [Int]) -> Double

func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double

最初のものは、パラメータラベルなしで型を呼び出すときに使用され(例:a(b、c))、2番目はラベルを提供するときに使用されます(例:a(b:cat、c:dog))。

@dynamicCallableは、そのメソッドがどのデータ型を受け入れて返すかについて非常に柔軟なので、Swiftのすべての型安全性の恩恵を受けることができますが、高度な使用法にはまだ少し余裕があります。そのため、最初のメソッド(パラメータラベルなし)には、array、array slice、setなど、ExpressibleByArrayLiteralに準拠したものを使用でき、2番目のメソッド(パラメータラベル付き)には、dictionaryやkey value pairなどのExpressibleByDictionaryLiteralに準拠したものを使用できます。

注:これまでKeyValuePairsを使用したことがない場合は、@dynamicCallableを使用すると非常に便利なので、それらが何であるかを学ぶのに適した時期です。ここで詳細をご覧ください:KeyValuePairsは何か?

さまざまな入力を受け付けるだけでなく、さまざまな出力に対して複数のオーバーライドを指定することもできます。1つは文字列を返し、もう1つは整数などを返します。Swiftがどちらが使用されているかを解決することができれば、必要なものをすべて組み合わせて組み合わせることができます。

例を見てみましょう。まず、渡された入力に応じて、0から特定の最大値までの数値を生成するRandomNumberGenerator構造体があります。

Sample.swift
struct RandomNumberGenerator {
    func generate(numberOfZeroes: Int) -> Double {
        let maximum = pow(10, Double(numberOfZeroes))
        return Double.random(in: 0...maximum)
    }
}

これを@dynamicCallableに切り替えるには、代わりに次のように書きます。

Sample.swift
@dynamicCallable
struct RandomNumberGenerator {
    func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double {
        let numberOfZeroes = Double(args.first?.value ?? 0)
        let maximum = pow(10, numberOfZeroes)
        return Double.random(in: 0...maximum)
    }
}

このメソッドは任意の数のパラメータ、またはおそらくゼロで呼び出すことができます。そのため、最初の値を注意深く読み、適切なデフォルト値があることを確認するためにnil合体を使用します。

これでRandomNumberGeneratorインスタンスを作成し、それを関数のように呼び出すことができます。

Sample.swift
let random = RandomNumberGenerator()
let result = random(numberOfZeroes: 0)

代わりにdynamicCall(withArguments :)を使用した場合、または同時に両方を単一タイプにすることができるため、次のように記述します。

Sample.swift
@dynamicCallable
struct RandomNumberGenerator {
    func dynamicallyCall(withArguments args: [Int]) -> Double {
        let numberOfZeroes = Double(args[0])
        let maximum = pow(10, numberOfZeroes)
        return Double.random(in: 0...maximum)
    }
}

let random = RandomNumberGenerator()
let result = random(0)

@dynamicCallableを使用する際に注意が必要ないくつかの重要な規則があります。

  • 構造体、列挙型、クラス、そしてプロトコルに適用することができます。
  • withKeywordArguments:を実装したり、withArguments:を実装しない場合でも、型はパラメータラベルなしで呼び出すことができます - キーには空の文字列を取得するだけです。
  • withKeywordArguments:またはwithArguments:の実装がthrowingとしてマークされている場合は、型の呼び出しもthrowします。
  • @dynamicCallableをextensionに追加することはできません。型のプライマリな定義のみです。
  • まだ他のメソッドやプロパティに型を追加して、通常どおりに使用することができます。

おそらくもっと重要なのは、メソッドの解決がサポートされていないことです。つまり、型の特定のメソッドを呼び出すのではなく (例 random.generate(numberOfZeroes: 5))、型を直接呼び出す必要があります (例 random(numberOfZeroes: 5))。このようなメソッドシグネチャを使用して後者を追加することについてはすでにいくつかの議論があります。

Sample.swift
func dynamicallyCallMethod(named: String, withKeywordArguments: KeyValuePairs<String, Int>)

それが将来のSwiftのバージョンで可能になった場合、テストモックのためのいくつかの非常に興味深い可能性を開くかもしれません。

それまでの間、@dynamicCallableは広く普及する可能性は低いですが、PythonJavaScript、およびその他の言語との対話を望む少数の人々にとって非常に重要です。

Handling future enum cases

SE-0192には、固定されたenumと将来変更される可能性のあるenumを区別する機能が追加されています。

Swiftのセキュリティ機能の1つは、すべてのswitchステートメントが網羅的である必要があるということです - それらはすべてのケースをカバーしなければならないということです。これは安全性の観点からはうまく機能しますが、将来新しいケースが追加されると互換性の問題が発生します。システムフレームワークがあなたの要求に応えていないものを送信するかもしれません。 あなたのswitchがもはや網羅的ではないので、breakにコンパイルしてください。

@unknown属性を使用すると、2つの微妙に異なるシナリオを区別できます。「このdefaultケースは、個別に処理したくないため、他のすべてのケースに対して実行する必要があります。」および「すべてのケースを個別に処理したいのですが、将来的にエラーが発生するのではなく使用します。」

これがenumの例です。

Sample.swift
enum PasswordError: Error {
    case short
    case obvious
    case simple
}

switchブロックを使用してこれらの各ケースを処理するコードを書くことができます。

Sample.swift
func showOld(error: PasswordError) {
    switch error {
    case .short:
        print("Your password was too short.")
    case .obvious:
        print("Your password was too obvious.")
    default:
        print("Your password was too simple.")
    }
}

これは、.shortと.obviousのケースのパスワードに対して2つの明示的なケースを使用しますが、3番目のケースをdefaultブロックにまとめます。

さて将来、oldと呼ばれる新しいケースを以前に使用されていたパスワードに追加すると、そのメッセージが実際には意味をなさない場合でも、自動的にdefaultケースが呼び出されます。パスワードは単純すぎてはいけません。

このコードは技術的に正しい(最良の種類として正しい)ため、Swiftが警告することはできません。したがって、この間違いは簡単に見逃される可能性があります。 幸いなことに、新しい@unknown属性は完全にそれを修正します - それはdefaultケースでのみ使用することができ、新しいケースが将来発生したときに実行されるように設計されています。

例えば:

Sample.swift
func showNew(error: PasswordError) {
    switch error {
    case .short:
        print("Your password was too short.")
    case .obvious:
        print("Your password was too obvious.")
    @unknown default:
        print("Your password wasn't suitable.")
    }
}

そのコードはswitchブロックがもう網羅的ではないので警告を出すでしょう。Swiftは、各ケースを明示的に処理するように求めています。これは単なる警告にすぎないので、この属性が非常に便利になります。将来frameworkで新しいケースが追加された場合は、警告が表示されますが、ソースコードが破損される事はありません。

Flattening nested optionals resulting from try?

SE-0230try?の動作を変更して、ネストされたOptionalがフラット化されて通常のOptionalになるようにします。 これはOptionalのチェーニングと条件付きタイプキャストと同じように機能します。どちらも以前のSwiftバージョンではOptionalをフラットにします。

これは、変更を実証する実際的な例です。

Sample.swift
struct User {
    var id: Int

    init?(id: Int) {
        if id < 1 {
            return nil
        }

        self.id = id
    }

    func getMessages() throws -> String {
        // complicated code here
        return "No messages"
    }
}

let user = User(id: 1)
let messages = try? user?.getMessages()

User構造体には無効なイニシャライザがあります。これはfolksが確実に有効なIDを持つuserを作成できるようにするためです。getMessages()メソッドには、理論上userへのすべてのメッセージのリストを取得するためのある種の複雑なコードが含まれているので、throwとしてマークしています。コードがコンパイルされるために、固定の文字列を返すようにしました。

最後の行がキーラインです。userはOptionalなのでオプショナルチェーンを使用し、getMessages()はそれをスローすることができるのでtry?を使用してスローメソッドをオプショナルに変換します。Swift 4.2以前ではmessagesString?? - オプショナルのオプショナルString - にしていましたが、Swift 5.0以降では、try?はすでに値がオプショナルの場合、値をオプショナルで囲むことはできません。
messagesは単にString?になります。

この新しい動作は、オプショナルチェーンと条件付きタイプキャストの既存の動作と一致しています。つまり、必要に応じて1行のコードでオプショナルチェーンを数十回使用することもできますが、12個の入れ子になったオプショナルを使用することはできません。同様に、as?でオプショナルチェーンを使用した場合でも、最終的には1つのレベルの選択性しか得られないでしょう。それは通常あなたが望むものだからです。

Checking for integer multiples

SE-0225では、integerにisMultiple(of :)メソッドが追加され、除算剰余演算を使用するよりもはるかに明確な方法で、ある数が別の数の倍数であるかどうかを確認できます。

具体例

Sample.swift
let rowNumber = 4

if rowNumber.isMultiple(of: 2) {
    print("Even")
} else {
    print("Odd")
}

はい、同じチェックをif rowNumber % 2 == 0を使用して書くこともできますが、それほど明確ではないことを認めなければなりません。メソッドとしてisMultiple(of :)を使用すると、Xcodeのコード補完オプションに表示できるため、検索に役立ちます。

Counting matching items in a sequence

SE-0220では、filter()と同等の処理を行い、シングルパスでカウントする新しいcount(where :)メソッドが導入されました。これにより、すぐに破棄される新しい配列の作成が節約され、一般的な問題に対する明確で簡潔な解決策が提供されます。

次の例では、テストの結果の配列を作成し、85以上の数が数えられます。

Sample.swift
let scores = [100, 80, 85]
let passCount = scores.count { $0 >= 85 }

そしてこれは、配列内で“ Terry”で始まる名前の数を数えます。

Sample.swift
let pythons = ["Eric Idle", "Graham Chapman", "John Cleese", "Michael Palin", "Terry Gilliam", "Terry Jones"]
let terryCount = pythons.count { $0.hasPrefix("Terry") }

このメソッドはSequenceに準拠しているすべての型で利用可能であるため、SetやDictionaryにも使用できます。

Transforming and unwrapping dictionary values with compactMapValues()

SE-0218はDictionaryに新しいcompactMapValues()メソッドを追加され、DictionaryのmapValues()メソッドとArray(「値の変換、結果のアンラップ、そしてnil以外のものの破棄」)からのcompactMap()機能が統合されました。my keyはそのままですが、my valueを変換します。

一例として、これはレースに参加した人々のDictionaryと、彼らが数秒で終了するのにかかった時間です。 "DNF"とマークされた1人の人が終了しませんでした。

Sample.swift
let times = [
    "Hudson": "38",
    "Clarke": "42",
    "Robinson": "35",
    "Hartis": "DNF"
]

nameとtimeはintegerとして、1人のDNFを削除した新しいDictionaryを作成するためにcompactMapValues()を使用することができます。

Sample.swift
let finishers1 = times.compactMapValues { Int($0) }

あるいは、Intのイニシャライザを直接compactMapValues()に直接渡すこともできます。

Sample.swift
let finishers2 = times.compactMapValues(Int.init)

また、、次のようにcompactMapValues()を使用する事で、変換の任意の並べ替えを行うことなく、optionalのラップを解除しnilの値を破棄する事ができます。

Sample.swift
let people = [
    "Paul": 38,
    "Sophie": 8,
    "Charlotte": 5,
    "William": nil
]

let knownAges = people.compactMapValues { $0 }

More still to come!

これらはすでにSwift 5.0ブランチに実装されている機能ですが、Swift 5.0のリリースに至るまでの数カ月のうちに間違いなくもっと多くの機能が追加されるでしょう。

そして、ABIの安定性が5.0の最大の機能の1つであることを忘れないでください。多くの大企業(もちろんApple自身を含みます)がこれが起こるのを待っているので、Swiftへの素晴らしい移行が始まります。

エキサイティングな時代になってきましたね!

追記1

Swift5に関する情報はMatsudateさんのこちらの記事の方が詳しかった(笑!)ので参考資料としてリンク貼ることでご了承ください。

What’s new in Swift 5

追記2

Xcode10.2のβ版がAppleのダウンロードページでダウンロードできるそうです。

Xcode10.2 β

優しい方で良かったです。
(引用ダメそうでしたら削除します。)

iOSエンジニアがフリーランス案件の面談で良く聞かれる質問集をまとめてみた

概要

転職エージェントは、本当にスゴ腕なのか!?──偽エンジニアを見抜けるか試してみたツイッターのタイムラインで上がってきたので興味が湧きました。

転職エージェントが豪腕エンジニアを見抜けるかはさておき、この記事では実際に転職の面談の流れが紹介されていた。
私は今でこそ4年くらいフリーランスとして活動しているが約1年ベースで現場を渡り歩いたので現場の数だけ面談を通ってきました。

そこで今回はフリーランスの案件に携わる時の人事の方から質問を受ける問いをまとめたいと思う。
もちろん私のメインはiOSエンジニアなのでiOSの案件の面談です。

面談時によく聞かれる質問

これまでに携わってきたプロジェクトと職務経歴について説明してください

こちらは自分が作成した職務経歴書を元にして自分が今まで経験してきたプロジェクトの内容について説明します。全部が全部を説明するのではなくアピールしたいプロジェクトを3つほどピックアップして職務経歴書には書かなかった事(書けない事)を伝えます。

メインで伝える事としては

  • プロジェクトの規模感(iOSエンジニアx名、Androidエンジニアx名、サーバーサイドエンジニアx名のプロジェクトでiOSエンジニアを担当)
  • 開発環境について(チケット管理、CI、その他ツールなど)
  • github-flowやgit-flowなどどんな開発スタイルを経験してきたか
  • そのプロジェクトを通して経験した知見、つらみ、教訓
  • そのメンバーで自分が発揮できたバリューの内容
  • タスクの取り組み方

これまでに使ってきたフレームワークは具体的にはどういったものを使ってきましたか?

iOSエンジニアでしたらどのようなライブラリ管理ツールを使ってきたのか(CocoaPodかCarthage)

RxSwiftでの開発経験はありますか?

こちらは開発でRxSwiftを導入しているプロジェクトの場合に聞かれます。
基本、私は「はい、経験があります」と伝えますがこの質問は色々と恐怖を感じます。
というのはプロジェクトによっては「トレンドだから使ってみたい」といった理由で採用されたりする事があってメンバーのスキルセットによっては後々の技術的負債となるケースがあったからです。
ちなみにAndroidのRxJava・RxAndroidに関してはとても便利なのでAndroidでRxを採用しているところにハズレはありませんでした。

これまでにgithubやQiitaで情報発信してきましたか?

技術レベルが一定以上の会社に関してはほぼ聞かれました。
こちらは「今は情報発信はしていませんがゆくゆくは情報発信をしていきたいです」という前向きな姿勢を見せたらいいのかと思います。
私の場合は趣味アプリは他人に見せたくないのでプライベートリポジトリで管理している関係でgithubの方は答えにくかったりしますが、「プライベートリポジトリで開発しています」という風に伝えてました。
パブリックリポジトリは基本ゴミしかありません(汗)。

最近のiOSのトレンドや興味を持っている技術について教えてくれませんか?

RxSwiftとFlutterとブロックチェーンですと答えます。(Flutterは触ってみたいけど人生の時間が足りません。)
ブロックチェーンをGoと言い換えたりしていますが、「なぜにGo?」という反応が多かったのでブロックチェーンと答える習慣がつきました。

テストコードを書いた経験はありますか?(リファクタリングをした経験はありますか?)

  1. UITest・UnitTestを書いた経験がなかった時期

テストコードは書いたことはありません。すみません。

  1. 今(どっちも経験ずみ)

UnitTestの経験はありませんでしたが、UITestの経験はあります。UITestのきっかけはリリースのたびにデグレ・バグが発生するプロジェクトがあった時に精神安定剤としてUITestを使って画面のスナップショットを取るようにしました、と回答しています。

ここは特にテストコードの経験がなくても大丈夫ですが、あれば加点のポジティブQuestionという印象があります。
ただiOSアプリの場合はそもそもテストコードがない事が多いのでテストコードを書かなくてもいい文化がありますね。
寧ろ、テストを書く理由の方が大事な印象があります。

UITestが本当に必要だと思ったプロジェクトは1クラス2000行以上は当たり前、1メソッドにつき200行の処理が書かれているソースコードを見たりした時は流石にテストが必要だなと実感します。

弊社は過去の負債が沢山あってリリースの度にクラッシュバグが多発していてそれを軽減させたいのですが、それは可能でしょうか?

こちらは本当は「不可能です!」と素直に答えたいですがそれはプログラマの意地で「できなくはありませんね」と答えてしまいます(笑)。回答への繋げ方としては逆にどうしたらデグレやバグが多発しないようにリファクタリングしていくのかのプロセスを提示できればOK。

リファクタリングのやり方としては様々なアプローチを考えられると思いますが、

  1. MVCでもMVVMでも何でもまずはModelから攻めます。
  2. Modelのユニットテストを書きます。
  3. Modelのユニットテストのテンプレが出来上がったら共通クラス・メソッドUtilityクラスを攻めます。
  4. スパゲティコードの原因の多くはUtilityクラスはメソッドを叩いている「だけ」のメソッドが多いので返り値を返すメソッドに無理やり直します。
  5. メソッドは無理やりin(input)とout(output)で分けるように心がけます。
  6. Modelクラスがある程度スマートになってくればViewModelになるように心がけます。
  7. DBなどを使っていたらデッドロックにならない方にスレッドに気をつける
  8. ViewModelが終わったらView->Controllerの順番に攻める
  9. Swiftを使っていたらprotocolを使ってprotocol extensionで切って1ファイルあたりの行数を減らす
  10. protocol extensionでクラス間が疎結合になると思うからUITestがしやすくなる

2-3の過程でAPI側のparameterとかアウトプットの仕様が分かってくるため精神安定剤としてドキュメントを作成します。
UITestまで持っていけたらfastlaneとかを使って自動キャプチャーまで持っていければ合格点!

とかいった説明になります。

サーバーサイドもやっていただく可能性がありますが大丈夫ですか?

サーバーサイドの言語によって変わります。
バックエンドにPHPRailsを採用していたら見る事ができますがそれ以外のフレームワーク・言語でしたら学習コストがかかると思います。
Goの場合はチャレンジしてもいいのでしたら私頑張って学習しますのでぜひやらせてください。

と伝えます。

APIの通信を実装した経験はありますか

今の時代API通信を実装できないと仕事になりませんので大丈夫です。
AFNetworkingだろうがAlamofireだろうがAPIKitだろうがURLSessionによる直書きだろうが対応できます。でもGETPOSTはどちらを使う傾向の開発スタイルでしょうか、と逆に質問しています。

-> サーバーサイドの会話が始まります。

これまでにとても苦労したことやハマったこと、大きな失敗をしたことについて教えてください

これは結構プロジェクトの機密情報に関わってきますので記載に困ります(面談時であればクソだった案件でどこがどういう風にクソだったのかを伝えるだけ)。
一つあげるとしたら、CoreDataを使用したらプロジェクトで前任のプログラマが実装した部分で新しい機能追加してリリース後にデグレが発生してそこがデットロックしてアプリが固まってしまうバグが非常に大変でした。

現役のiOSエンジニアに見せるとだいたい発狂します。

一応デバッグ方法はありますので頑張れます。ですが、Realmとか別のDBで作り直した方がいいです。

CoreDataのスレッド間のチェック

あとはAppleのアプリ内課金(レシート問題)でのトラブルネタは結構トピックにできていいです。

Androidの開発経験はありますか?やってみたいですか?

開発経験はあります。ぜひやってみたいです。iOSAndroid両方触りたいですしKotlin書きたいです。

iOS開発でも誰にも負けないと言える分野やスキルはありますか?

UnitTestやAPI周りの実装です。あとはドキュメント作り(Confluence職人)、コーディング規約作りもやります。
Swiftならvarよりletですよね。Immutable大事ですよね。
あとは仕様をまとめるスキルです。

ここだけはどうしても苦手、という分野について教えてください

デザイン周りかもしれません。1px辺りの調整が苦手だったりします。
まあ、でも対応できますので特にありませんね、あー、でもCoreDataのデッドロックとか辛いですよね。

弊社ではコードレビューを導入していますが共同開発の経験はありますか?

今までレビューのなかった現場はありませんでしたので全部先輩エンジニアにレビューされましたしレビューしていました。一人でもフルスクラッチで開発できますが共同開発の方が経験が多いです。iOSエンジニアが合計1-4人規模のアプリ開発に携わっていました。LGTM貰うまでマージできない文化でしたね。

GitHubのIssueやPull requestで頻出する英語の略語達

itunes Connectを使ったことはありますか?(アプリのリリース作業をしたことはありますか?)

アプリのリリース経験はもちろんあります。自分で趣味アプリも開発していますのでリリースしていました。
(と伝えてこのタイミングで自分のアプリを見せます。)

弊社のプロジェクトはAutoLayout / SizeClass / Anchor を使っていますがそれを使った経験はありますか?

全部、経験があります。SizeClassは1つの現場だけ経験しましたが、StoryboradxibファイルがあるプロジェクトではAutoLayout、コードベースでの設計の場合はAnchorを使って実装していました。

などが参考資料ですけど面談中にリンクを貼ることができず・・・。

これまでにどのような設計のプロジェクトに携わってきましたか?

これまでに携わってきたプロジェクトと職務経歴について説明してくださいと同義。

 これまでに課金システムを実装した経験はありますか?

あります。ただ、これはXXX系プロジェクトの時にアプリ内課金が実装されていたアプリのメンテで使用していました。
課金周りはユーザーのお金に関わりますので慎重に対応していた記憶があります。

これまでにソーシャルシェアログインの実装経験はありますか?

あります。だいたいはfacebookTwitterログインはSDKを使って実装してきました。
それにアプリ内課金が絡んだ時は結構実装大変ですよね。

これまでの経験で辛かった経験があったかとそれはどんな経験でしょうか?

ここだけはどうしても苦手、という分野について教えてくださいと同義です。

逆にあなたの強みを教えてください。

iOS開発でも誰にも負けないと言える分野やスキルはありますか?と同義です。

面談で人事やエンジニアの方から「質問はありますか」と聞かれた時に使える確認した方がいい質問集

UIのデザインはStoryboardなどのファイルを使用した開発でしょうか。それともコードだけの開発でしょうか。

チケット管理は何でやっていますか?

開発体制(エンジニアとデザイナーの人数)について教えてください

デザインは重視されるプロジェクトでしょうか。

デザインのファイルはPhotoshopのファイルでしょうか。エンジニアも画像の書き取りをする必要がありますか?

パソコンはiMacでしょうか。ノートパソコンでしょうか。

リリーススケジュールは決まっている感じでしょうか。(1/week or 1/2week or 1/month or at random ?)

開発体制はアジャイル開発でしょうか。スクラムを導入していますか?

ライブラリ管理はCocoapodでしょうか。Carthageでしょうか。

使っているライブラリの個数について教えてください

クラッシュ解析は何を使っていますか?

プロジェクトの設計スタイルについて教えてください

サーバーサイドの言語は何でしょうか。Ruby? PHP? C# ?

アプリの画面は1画面1storyboardスタイルでしょうか。それとも分割せずに一つのstoryboardで開発されてますか?

通信系のライブラリは何を使用されてますか?