20200406のSwiftに関する記事は7件です。

【Swift】データ型簡易まとめ

概要

Swiftの学習中の備忘録です。
ここではデータ型について出来るだけ簡単にまとめておきます。

筆者環境

  • MacBook Pro (13-inch, 2019)
  • macOS Catalina(Version 10.15.4)
  • Xcode(Version 11.4)
  • Swift5.2

データ型とはなんぞや

変数を定義する時に
「この変数は文字列ですよ!」とか「数値ですよ!」みたいな感じで
カテゴリ分けをするためのものとして一旦理解をしておくことにします。

Int型(整数型)

正と負の整数を扱う場合のデータ型です。

var i: Int = 54 
//Int型(データ型)のi(変数名)に54(初期値)を代入

var i = 54 
//データ型を省略して書くことも出来る(型推論)

Double型(浮動少数点型)

小数を扱う場合のデータ型です。
浮動小数点型には「Float型」もありますが、「Double型」の方が精度の高い数値を扱えるとのこと。
基本は「Double型」を使っていくこととします。

var d: Double = 5.987 
//Double型(データ型)のd(変数名)に5.987 (初期値)を代入

var d = 5.987 
//データ型を省略して書く場合、自動的にDouble型になる。

String型(文字列型)

文字列を扱う場合のデータ型です。

var s: String = "Hello" 
//String型(データ型)のs(変数名)に文字列"Hello"(初期値)を代入

var s = "Hello" 
//データ型を省略して書くことも出来る(型推論)

Bool型(真偽値)

論理演算で「true(真)」「false(偽)」を扱う場合のデータ型です。

var b: Bool = true 
//String型(データ型)のs(変数名)に文字列"Hello"(初期値)を代入

var b = true
//データ型を省略して書くことも出来る(型推論)

(ちなみに)一度データ型を宣言すると、その他のデータ型は受け付けなくなる

たとえ同じ数値というものを扱うデータ型であっても、
Int型の変数にDouble型の値を入れることはできません。

var i = 5 
i = 3.4

//エラー文
Cannot assign value of type 'Double' to type 'Int'
//Double型の値をInt型に割り当てることは出来ません!と怒られます。

最後に

以上、簡単ですがデータ型についてでした。
学習を進める中で、追加したい内容が出てきたら都度追記していきます。
データ型と関係の深いオプショナル型については別記事にて投稿したいと思います。

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

遷移しよー!with CircleMenu

こんにちは!!!

手元からPCが消え去って1ヶ月。やっっっとパソコンが買えたので投稿。

今回はCircleMenuというオシャレなライブラリがあることを(今更)知ったのでこのアニメーションを利用してふわっと遷移しようという趣旨。

別にCircleMenuのオシャレなアニメーションに乗っかって遷移してるだけのハリボテです。

ブランクあるんや、、リハビリみたいなもんや、、

成果物

なんかgifだと残念な感じに円が残るけど実際には普通にスゥーっ!っと消えます!!!(血眼)

Unknown

コード

import UIKit
import CircleMenu

class ViewController: UIViewController {

    let menuButton: CircleMenu = {
        let view = CircleMenu(frame: CGRect(x: UIScreen.main.bounds.width / 2 - 25, y: UIScreen.main.bounds.height / 2 - 25, width: 50, height: 50), normalIcon: "menu", selectedIcon: "error", buttonsCount: 5, duration: 0.3, distance: 150)
        view.backgroundColor = .green
        view.layer.cornerRadius = view.frame.size.width / 2.0
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        menuButton.delegate = self

        self.view.addSubview(menuButton)

    }
}

extension ViewController: CircleMenuDelegate {
    func circleMenu(_ circleMenu: CircleMenu, buttonDidSelected button: UIButton, atIndex: Int) {
        var vc: UIViewController?
        switch atIndex {
        case 0:
            vc = NextViewController()
        case 1:
            vc = NextViewController()
        case 2:
            vc = NextViewController()
        case 3:
            vc = NextViewController()
        case 4:
            vc = NextViewController()
        default:
            print("no vc")
        }
        guard let VC = vc else { return }
        VC.modalPresentationStyle = .overCurrentContext
        self.present(VC, animated: false, completion: nil)
    }
}


import UIKit

class NextViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .red

        let tap = UITapGestureRecognizer(target: self, action: #selector(dismissAction))
        self.view.addGestureRecognizer(tap)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.view.alpha = 0
        UIView.animate(withDuration: 0.5) {
            self.view.alpha = 1
        }

    }

    @objc func dismissAction() {
        self.dismiss(animated: false, completion: nil)
    }
}

解説

import らへん

import CircleMenu

今回のメインであるライブラリのインポートですね。githubではbuildの項目がfailingになってましたけど普通に使えますね。なんででしょ。余裕があったら読みたいものですな。

view らへん

let menuButton: CircleMenu = {
        let view = CircleMenu(frame: CGRect(x: UIScreen.main.bounds.width / 2 - 25, y: UIScreen.main.bounds.height / 2 - 25, width: 50, height: 50), normalIcon: "menu", selectedIcon: "error", buttonsCount: 5, duration: 0.3, distance: 150)
        view.backgroundColor = .green
        view.layer.cornerRadius = view.frame.size.width / 2.0
        return view
    }()

viewの宣言をクロージャで宣言してるんや。これは宣言してるviewが使われるときに一度だけ初期化されるヨ。
この形をとることでひとまとまりになってるし、viewDidload内が汚れないから見やすいね!
あとはあんまり良くないかもしれないけど、中で宣言するインスタンス名をviewにすることで他のviewを宣言するときコピペがとっても楽になるよ!笑

delegate らへん

delegateは移譲って言って、こいつがいるときは処理を他人任せにしてる証拠だ!!(そして大抵僕に丸投げされてるんだ。 これは私情。)

もし君が仕事を任されたとき自分なりのやり方でやるよね???(異論ナシ。)
delegateも任された側のやり方で処理することができるから便利なんだ。
delegateと一緒だね!!!!(歓喜)

あと、通知の役割も大きいけど今回は使ってないからパス _ (:3 」)_

menuButton.delegate = self

このコードからは、menuButton先輩が、self、つまり私(ViewController)に仕事をぶん投げるということを高らかに宣言しているのだ。(やめてくれよ)

逆に言えば、このように高らかに宣言してくれないとdelegateできない。つまり仕事を任せられていないので、

「お前これ頼んだよな?(威圧)」
「は?寝ぼけてんのか?(えっ、??いや、頼まれてないですよ、、?)」

なんてことが起こる。(特に良く僕のコード内で。うっかり。照)

Extension らへん

swiftに限らず継承はとても便利だ。
ここでは便利なextensionの継承の、書き方について触れておこうと思う。

extension ViewController: CircleMenuDelegate {
  //省略
}

プロトコルを継承することで決まった形のクラスや構造体を作れるし、うまくかければ具象への依存かなり減らせてそれだけでとても変更しやすくなる。

しかし、ViewControllerの定義でついつい一度に継承するものを羅列してしまいがちだ。

class ViewController: UIViewController, UITableViewDelegate, CircleMenuDelegate {
  //省略
}

これではどの関数がどのクラス・プロトコルから継承してきたものかかなり分かりづらいし、区切りのないコードが一気に肥大してしまう。

そのため、今回のように一つ一つ優しくextensionしてあげよう。これだけで見通しが良くなるし、「あっ、こいつイラネ!w」となった時もどこを消せばいいかすぐわかる。
追加もしやすい。

書き方をちょっと変えるだけでいいことづくめなのだ。

guard らへん

guardとif。ifが便利すぎでわざわざguardを使う意味がわからないという方もいるのではなかろうか。

ペーペーの僕がいうのもなんだが、僕なんかよりずっといいコードを書いている人でもguardを使わずifを使っていることがあるから頭に「?」が浮かぶ。

こんなに良い関数は他にないんじゃないか? それは無いか。
(ちなみに僕的、昨今の推しはmapです。)

guard let VC = vc else { return }

guardはなんと言っても一眼で役割が分かるのがいい。

  • guardを使う場面はreturnされて本土(呼び出し元)に返されるか、fatalErrorでも呼ばれてアプリがクラッシュするかくらいだ。

「いやそれifでもできるやーん?」

そうなんだけど、あえてguardを使うことで自分や他の人がそこを読む時、一瞬で意図がわかる。

素敵だ。(イケボ)

そしてしばしばletとセットでオプショナル回避に用いられる。

let optional: Int? = 3

guard let Nakami = optional else { return }
// optional : Optional<3>
// Nakami : 3

このように中身を簡単かつ安全に、そして中身がない時は本土に返還するスグレモノなのだ。

lifecycle と animation らへん

animationを行うことで今回はそれっぽーく仕上げでいる。
透明度を表すalphaの値を事前に0にして完全に透明にしておく。

viewWillAppearが呼ばれたらアニメーションを行って、alphaを1すればふわっと素敵っぽい遷移が実現できた。(急に完了する。)

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.view.alpha = 0
        UIView.animate(withDuration: 0.5) {
            self.view.alpha = 1
        }
    }

ここで大事なのでviewWillAppearで呼ぶことだ。

これより前のviewDidloadで呼ぶと、そもそもアニメーションが行われない。
しかもvcを再利用していればviewDidloadは一回しか呼ばれない。

この後のviewWillLayoutSubviewsは名前の通りサブビューの描画準備のためにあるので役割として明らかにおかしい。

ここら辺に関してはもっと良い書き方があるんだろうなぁという感覚なので勉強します。

ここまで読んでいただきありがとうございました。

キーボードは配列が前のは海外仕様だったから逆に日本仕様に慣れない、、

書くつもりなかったのにPCきたの嬉しすぎて夢中になって書いてしまった、、
明日仕事なのに。もう寝なきゃ。

おやすみなさい

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

Macアプリで常に最前面に表示する方法(Swift)

はじめに

タイトルに示す通りのことを実現したくて「常に最前面 MacApp」でググったら、クリアメモリさんの『【Swift】常に最前面に表示されるOSXアプリの作り方』のページがヒットしました。この記事に書いてある手順で無事に実現できたのですが、OS他のバージョンが上がっており多少の変更が必要でしたので、元記事からの変更点をまとめておきます。
ほとんどはコンパイルエラーが出て教えてくれますが、元記事への恩返しの意味です。

前提とする環境

当方の環境(執筆時点);

  • maxOS Catalina version 10.15.3
  • Xcode version 11.1 (11E148)
  • Apple Swift version 5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29)

変更点

AppDelegate.swift

「Stay in Front」メニューをクリックした時の、チェックオン/オフのトグル処理のコードにおいて、NSMenuItem.stateの値が変更になっています。

元記事のコード
func toggleMenuChecked(){
    if stayInFrontMenu.state == NSOnState {
        stayInFrontMenu.state = NSOffState
    }else if stayInFrontMenu.state == NSOffState{
        stayInFrontMenu.state = NSOnState
    }
}
新しいコード
func toggleMenuChecked() {
    if stayInFrontMenu.state == .on {   
        stayInFrontMenu.state = .off
    } else if stayInFrontMenu.state == .off {
        stayInFrontMenu.state = .on
    }
}

WindowController.swift

実際にウィンドウを最前面に設定する処理のコードにおいてNSWindow.Levelの値が変更になっています。

元記事のコード
func stayInFront(){
    let normalWindow = Int(CGWindowLevelForKey(.normalWindow))
    let floatingWindow = Int(CGWindowLevelForKey(.floatingWindow))

    if window?.level == normalWindow{
        window?.level = floatingWindow
    }else if window?.level == floatingWindow{
        window?.level = normalWindow
    }
}   
新しいコード
func stayInFront() {
    if window?.level == .normal {
        window?.level = .floating
    } else if window?.level == .floating {
        window?.level = .normal
    }
}

WindowController.swift (2箇所目)

NotificationCenterのオブザーバから呼び出されるメソッドに@objcattributeが必要です。1 2

元記事のコード
func nCenter(notification: NSNotification) {
    stayInFront()
}
新しいコード
@objc
func nCenter(notification: NSNotification) {
    stayInFront()
}

その他

変更点ではありませんが、コンパイルエラーが出ないので気付きにくい点として;

新しく「WindowController」というクラスを作成し、ストーリーボードで設定してあります。
この工程は省くので、各自設定しておいてください。

下図の様にカスタムクラスを設定する必要があります。これを忘れるとWindowControllerが機能しません期待通りに動きません。
スクリーンショット

終わりに

元記事では、他にもOutletActionの接続手順も省略しています。もしこの記事をご覧の方で、これらの手順も知りたい方は、コメントいただければ追加いたします。


  1. なぜ@objcが必要なのか、こちらに説明があります。 

  2. @objc他のSwiftのattributeについてはこちらに説明があります。 

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

AtCoder に登録したら解くべき精選過去問 10 問を Swift 5 で解いてみた

元記事 https://qiita.com/drken/items/fd4e5e3630d0f5859067

問題 https://atcoder.jp/contests/language-test-202001

AtCoder with Swift

良いところ

  • 簡潔な構文
  • 第一級関数
  • Null安全設計による条件分岐がきれい
  • 使用人口は多いはず(= 知見を得やすい)
  • 普通に速い
  • 初心者がプログラミングを学ぶには結構良い言語

悪いところ

  • 競プロで役立つ標準ライブラリは少ない
    • Dequeがない(= Queueがない)
    • sum関数がない
    • など
  • 引数の名前指定がめんどくさい
  • Null安全がたまにめんどくさい
  • ところどころメソッドなどの名前が独特(toStringじゃなくてdescription)

心構え

一応システムプログラミング言語らしい(システムプログラミング言語: Wikipedia)ので

  • 安全性
  • 速度
  • メモリ効率

この辺りは意識したいところかなと思っています。

変数宣言は基本的にはletキーワードがいいですし、関数型プログラミングも積極的に行うべきです。ですが、速度とメモリも意識することも重要で、varキーワードを使うことを過剰にためらってはいけない気もしています。

精選過去問 10 問解いてみた

全体

main関数を作ってグローバル変数を少なくしたほうが速くなります。

PracticeA - Welcome to AtCoder

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    let a = Int(readLine()!)!
    let line = readInt()
    let s = readLine()!

    print(a+line[0]+line[1], s)
}

main()

ポイント

  • readLine()!
  • Int(String)!
  • print関数
  • readLine()!.split(separator: " ").map{Int($0)!}
  1. readLine関数は、標準入力を一行受け取る関数です。入力が無い場合を想定しているため、戻り値の型はOptional<String>(糖衣構文: String?)です。Optional型の値に対して!を後ろにつけると、「nilは入っとらん!」という主張のもと、強制アンラップされ、中身が取り出されます。AtCoderは入力を保証しているため、強制アンラップで問題ありません。

  2. Int(String)は、渡されたString型の値をもとに、それっぽいInt型をインスタンス化します。整数っぽくない文字列が渡される場合(Int型に変換できない場合)を想定しているため、戻り値の型はInt?です(変換できない文字列に対してはnilが返ります)。文字列が整数であると保証されている場合は!を後ろにつけて強制アンラップして構いません。

  3. print関数は以下のような定義になっています。

func print(_ items: Any..., separator: String = " ", terminator: String = "\n")

引数itemsは可変長引数です。また、初期で" "(スペース)で区切って出力するようになっているので

print(a, b)

とすれば、aとbがスペース区切りで出力されます。
4. スペース区切りの数値入力についてです。頻繁に使います。

readLine()!.split(separator: " ").map{Int($0)!}

readLines()!で得た文字列を、splitメソッドでスペース区切りで分割し、mapメソッドで各要素をIntに変換しています。mapメソッドは引数に(E) -> Tとなるようなクロージャ関数を受け取ります。今回は(Character) -> Intです。

上記のような記述は以下のような省略を経ています。

a.map({(x: Character) -> Int in Int(x)!})
// 型の省略
a.map({ x in Int(x)!})
// かっこの省略
a.map { x in Int(x)! }
// 引数の省略(第一引数 → $0, 第二引数 → $1 ...)
a.map { Int($0)! }

よく使うので、関数化しました。

ABC086A - Product

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    let ab = readInt()
    print(ab[0]*ab[1] % 2 == 0 ? "Even" : "Odd")
}

main()

ポイント

  • 三項演算子
  1. 三項演算子があります。

ABC081B - Shift only

問題文の通り A_1 ... A_N を2で割り切れなくなるまで割り続けます。Arrayなどのシーケンスの要素全て(all) が、とある条件を 満たす(Satisfy) かどうかは、そのためのメソッドがあるのでそれでチェックします。

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    let _ = Int(readLine()!)!
    var A = readInt()

    var ans = 0
    while A.allSatisfy({ $0%2 == 0 }) {
        A = A.map{ $0/2 } 
        ans += 1
    }

    print(ans)
}

main()

ポイント

  • /(徐算演算子)
  • かっこを省略しないパターン
  1. Int型の徐算は小数点切り捨てのInt型です。つまり、商です。

  2. A.allSatisfy({ $0%2 == 0 })ですが、本来はA.allSatisfy{ $0%2 == 0 }のようにかっこを省略できます。しかし、今回はwhile構文の条件式として使うため、かっこを省略するとwhileの波かっこなのか、クロージャの波かっこなのかの見分けが付かなくなるため、かっこは省略しません。

// 波かっこの見分けが付かない!
while A.allSatisfy { 
    $0%2 == 0 
} {
 // ...
}

ABC087B - Coins

func main() {
    let read = { Int(readLine()!)! }
    let a = read()
    let b = read()
    let c = read()
    let x = read()

    var ans = 0
    for i in 0...a {
        for j in 0...b {
            for k in 0...c {
                if 500*i + 100*j + 50*k == x {
                    ans += 1
                }
            }
        }
    }
    print(ans)
}

main()

ポイント

  • クロージャの賢い使い方
  • 範囲オブジェクト(Range)(糖衣構文: s...e)
  1. クロージャでタイピング数を減らせます。ありがたい。
    let read = { Int(readLine()!)! }
    let a = read()
    let b = read()
    let c = read()
    let x = read()

 
2. 範囲の末尾を含める場合はs...e、含めない場合はs..<eです。

ABC083B - Some Sums

問題文の通りに実装します。しかし記述は少し複雑です。

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    var line = readInt()
    let n = line[0]
    let a = line[1]
    let b = line[2]

    var ans = 0
    for i in 0...n {
        let x = i.description.reduce(0){ $0 + Int(String($1))! }
        if a <= x && x <= b {
            ans += i
        }
    }
    print(ans)
}

main()

ポイント

  • descriptionプロパティ
  • reduceメソッド
let x = i.description.reduce(0){ $0 + Int(String($1))! }

ここで、各桁の数を足し算しています。

  1. description(説明)プロパティは、オブジェクトの説明を示すプロパティです。数値型は大抵そのまま文字列に変換します。プロパティとは、メソッドと似ていますが、かっこで呼び出さず、文字通りプロパティとして扱いたい場合に実装されることが多いです。

  2. reduceメソッドは、ArrayやStringなど、for文で回せるようなシーケンスの型に実装されていることが多いメソッドです。初期値を第一引数に渡し、それに対してシーケンスの各要素を、第二引数に渡したクロージャや関数で処理して畳み込みます。渡すクロージャや関数は引数を二つ(畳み込み先, 要素)受け取る必要があります。

上記の記述は以下のような省略や記述の変更を経ています。

a.reduce(0, { (total: Int, c: Character) -> Int in total + Int(String(c))! })
// 引数の省略
a.reduce(0, { $0 + Int(String($1))! })
// 最後の引数がクロージャの場合は、かっこの後ろに記述できる。
a.reduce(0) { $0 + Int(String($1))! }

処理の流れは以下のようになっています。

"123456789".reduce(0){ $0 + Int(String($1))! }
  1. total ← 0 (初期化)
  2. total ← total + 1 (total == 1)
  3. total ← total + 2 (total == 3)
  4. total ← total + 3 (total == 6)
  5. total ← total + 4 (total == 10)
  6. total ← total + 5 (total == 15)
  7. total ← total + 6 (total == 21)
  8. total ← total + 7 (total == 28)
  9. total ← total + 8 (total == 36)
  10. total ← total + 9 (total == 45)

ABC088B - Card Game for Two

a_1 ... a_n を降順でソートし、奇数番目がAliceのもの、偶数番目がBobのものとすればOK。

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    let n = Int(readLine()!)!
    let A = readInt().sorted(by:>)

    var alice = 0
    var bob = 0
    for i in 1...n {
        if i % 2 == 1 {
            alice += A[i]
        } else {
            bob += A[i]
        }
    }

    print(alice - bob)
}

main()

ポイント

  • sortedメソッド
  • >
  1. sortedメソッドはsorted()sorted(by:)メソッドがあり、引数の無いほうは昇順、byのあるほうは、任意の比較方法でソートすることが可能です。
readInt().sorted(by:>)

上記の処理は以下のような省略や変更を経ています。

a.sorted(by: {(a: Int, b: Int) -> Bool in  a > b})
// 引数の省略
a.sorted(by: {$0 > $1})
// Swiftの実装上、a > b は >(a, b) のように捉えるので「>」でOK
a.sorted(by: >)

 
2. 演算子は、関数またはメソッドと捉えて実装されています。適切なプロトコルを実装すれば、独自クラスに演算子を定義できます。

ABC085B - Kagami Mochi

  1. デカいほうから積み重ねれば良さそう。
  2. 同じ大きさの餅は重ねられない。

全部違う餅なら並べ方は自明。餅の数が積み重なりの数になる。→ 重複削除した結果の個数が答え。

func main() {
    let n = Int(readLine()!)!
    let A = (1...n).map { _ in Int(readLine()!)! }
    let ans = Set(A).count

    print(ans)
}

main()

ポイント

  • Set

Set型はコレクション型の一つで、集合を表現する型です。要素の重複を許容しません。また、順序を保持しません。インスタンス化の際にシーケンスを渡すと、要素の重複を削除したコレクションとなります。

ABC085C - Otoshidama

三重ループは間に合わないので工夫します。

$ 2,000 × 2,000 × 2,000 = 8,000,000,000 = 8 × 10^9 $

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    let line = readInt()
    let N = line[0]
    let Y = line[1]

    for i in 0...N {
        for j in 0...(N-i) {
            let k = N - i - j
            if 10000*i + 5000*j + 1000*k == Y {
                print(i, j, k)
                return
            }
        }
    }
    print("-1 -1 -1")
}

main()

Swiftに関して特記すべきポイントはありません。

ABC049C - 白昼夢

後ろから部分文字列を構成していきます。ある時点での部分文字列が["dream", "dreamer", "erase", "eraser"]どれかに一致するなら部分文字列を空文字にリセットし、部分文字列の構成を再開します。
8文字以上の文字列を作った場合や、最終的に文字列が余った場合はNO

func main() {
    let S = readLine()!.map{$0}
    let n = S.count
    let A: Set = ["dream", "dreamer", "erase", "eraser"]

    var s = ""
    for i in 1...n {
        s.insert(S[n-i], at: s.startIndex)
        if s.count > 7 {
            print("NO")
            return
        }
        if A.contains(s) {
            s = ""
        }
    }

    if s.count == 0 {
        print("YES")
    } else {
        print("NO")
    }
}

main()

ポイント

  • 競プロでは扱いづらい文字列
  • Setの初期化
  • Stringの破壊的変更
  1. (Swiftの実装に詳しくないので間違っているかも知れません。) Swiftはなぜか文字列に対して数値でのアクセスができません。アクセスはRangeもしくはString.Indexでの部分文字列への参照のみ受け付けます。内部は複雑そうです。

しかたがないので、文字の配列に変換します。

let S = readLine()!.map{$0}

 
2. Setで型宣言をすると、配列を初期化するようにSetを初期化できます。ただの豆知識です。

let A: Set = ["dream", "dreamer", "erase", "eraser"]

 
3. 文字列が破壊的変更可能なのが驚きました(感想)。Pythonの文字列は不変なので。調べてみたらRubyとRustにも可変な文字列型がありました。(はい)

別解:

hasSuffixメソッドで、文字列の尻尾が引数の文字列と一致するかを調べることができます。

func main() {
    let A = ["dream", "dreamer", "erase", "eraser"]
    var S = readLine()!

    while let a = A.first(where: { S.hasSuffix($0) }) {
        S.removeLast(a.count)
    }

    if S.count == 0 {
        print("YES")
    } else {
        print("NO")
    }
}

main()

ポイント

  • while let構文
A.first(where: { S.hasSuffix($0) })

まずfirstメソッドですが、これはシーケンスの要素を順に見ていって、最初に条件にあった要素を返すメソッドです。見つからない場合が想定されているため、戻り値の型はOptional<E>です。(見つからない場合はnilが返ります)

Optionalに中身がある場合と無い場合での分岐は頻繁に見られますのでそのための構文が用意されています。

while let a = optionalA {

上記のような構文は、「optionalA(Optinal型)に中身がある場合、aに中身を代入してループする」という意味です。

if文も、条件を求める構文なのでこのような記述が可能です。

if let a = optionalA {
    // ...
} else {
    // ...
}

ついでですが、

A.first(where: { S.hasSuffix($0) })

A.first(where: S.hasSuffix)

こうできます。

ABC086C - Traveling

現時点から次の地点まで、手数が足りる、かつ手数とマンハッタン距離の偶奇が一致するという条件をクリアし続ければYes、途中でダメだったらNo

func readInt() -> [Int] {
    return readLine()!.split(separator: " ").map{Int($0)!}
}

func main() {
    let N = Int(readLine()!)!

    var (t_, x_, y_) = (0, 0, 0)
    for _ in 1...N {
        let line = readInt()
        let t = line[0]
        let x = line[1]
        let y = line[2]

        let dT = t - t_
        let dX = abs(x - x_)
        let dY = abs(y - y_)
        if dT < dX + dY {
            print("No")
            return
        }

        if dT % 2 != (dX + dY) % 2 {
            print("No")
            return
        }
        (t_, x_, y_) = (t, x, y)
    }

    print("Yes")
}

main()

ポイント

  • パターン代入

タプルによるパターン記法でまとめて代入できます。

var (x_, y_, t_) = (0, 0, 0)
(x_, y_, t_) = (x, y, t)

Swift5が採用され、参加者がより増えることを願っています。

Twitter: @conf8o

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

Swift自体の基礎知識

iPhoneアプリを開発してみたい!

iPhoneアプリを開発してみたいという方で、調べたことがあるという方は
少しばかりそれについてご存知かもしれませんが、
iPhoneアプリを開発するのに使う言語がSwiftです。
似た言語に、Objective-Cがありますが、このページではSwiftの説明をしていきます。

そもそもSwiftとは何か

Swiftは、Apple社の製品、iPhoneなどで動作するアプリを作るための言語です。
Swiftは、XcodeというIDLE(統合開発環境)で書くことができます。image.png
↑Swiftはアマツバメを指す英語

Swiftの特徴的な仕様

Swiftの特徴的な仕様の一つに、オプショナルバリューの対応があります。
オプショナルバリューとは、値が未定義の可能性がある変数、定数のことです。
簡単にいうと、配列などの値がなかった場合に返ってくるnil(未定義)を防ぐために、変数や定数にnilを代入することはできない仕組みです。

また、オプショナルバリューの値を変数に代入することは、変数定義の後に、
「let Kazu:Int?」
のように、定義の後に?をつけることによって実現する仕様です。

ちなみに、optional型の値を計算に使うには、print(表示する関数)するときにカッコの最後に!をつける必要があります。
「//kazuをラップ
let kazu:int?

kazu = 8
let nedan = 100

if let count = kazu{

  //kazuがnilでない場合
  let kingaku = nedan * kazu
  print(kingaku)

} else {

 //kazuがnilの場合
 print('定数kazuはnilです')


この操作は、アンラップと呼ばれます。

Xcodeをダウンロード・アプリ配布の仕方

Xcodeは、App Storeから入手できます。
作成したアプリを配布、販売するには、Apple developer Programに登録することが必要です。
年会費99ドルが必要になりますからご注意ください。
学習用に入手する場合は料金はかかりません。

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

PokéAPIを利用してMVP+CleanArchitectureのiOSアプリを作ったので解説する

はじめに

最近、勉強会などで、iOSの業務未経験の人たちと話している時に、これからiOSエンジニアとして仕事を得るためにどういったことを学べばいいのか、業務で実際にどういうことを意識して設計やツールを駆使しているかということをよく聞かれるので、それについて説明することができたらなと感じていました。

実際に業務で作っているコードを公開することはできないので、業務で作っているコードにかなり近い形のサンプルのアプリを作成、公開したので、それを参考にしながら私がどういったことを考えてiOSの業務をこなしているかについてを解説していきます。

注意:これはあくまで私が個人的に思う業務における考え方であって、正解というわけではありません。一つの意見として捉えてもらえると幸いです。

最も重要なことは何か

私は、iOSに限らずアプリケーション開発の業務を遂行する上で、最も重要なことは、アプリケーションのリリースサイクルを短くして、ユーザーに最速で最大の価値を提供し続けることだと考えています。

それを達成するためには、以下の様なことを意識するのが効果的と考えて、業務で実践しています。

  • 自動化をする
  • ミスを減らす
  • 開発におけるストレスを減らす

今回はそういったことを意識して、業務で実際に使っている、ミスが起こりにくく、素早い変更や改修、機能追加を容易にするためのノウハウを実践的に詰め込んだアプリを作成しました。

Pokedex

そのノウハウを詰め込んだサンプルのアプリですが、普段業務で考えていることを解説するという、少し退屈なテーマを目的としたものである以上、せめてアプリのテーマだけでも面白いものにしたいと思い、今回はPokéAPIを利用してみました。

Pokedex

ポケモンの一覧と詳細を見ることができる簡単なポケモン図鑑のアプリです。
Pokedex

セットアップ編

では、早速コードを見て解説と言いたいところですが、このアプリを起動するまでには、少しセットアップの作業が必要なので、それをまずはしていきましょう。

Makefile

Makefileに従ってセットアップを行えば、ツールやライブラリのインストールなどの面倒な作業をかなり簡単にできるのでオススメです。

実際の業務では、新たにチームメンバーが参加した時などに役立つでしょう。

README.mdに書いてあるとおり、ターミナルでプロジェクトのルートを開き、make bootstrapmake projectのコマンドを実行します。

今回インストールしたツールはiOS開発を行う上で、効率を上げてくれるので詳細を説明するべきなのですが、全部を細かく紹介すると、かなり長くなってしまうので、それぞれについてさらっと紹介して、参考になる記事を貼り付けておきます。

Mint

Swift製のコマンドラインツールを管理することができるツールです。

今回はこれを利用して、XcodeGenなどの開発する際に便利なツールを管理します。

Mint で Swift 製のコマンドラインツールを管理する

Swift製コマンドラインツールのパッケージ管理ツール「Mint」のセットアップ&操作方法

Carthage

言わずとしれたライブラリ管理ツールです。

CocoaPodsに比べて、ライブラリのインストール時間が長いですが、その分コンパイル時間が短縮できるので、オススメです。

Carthageを使ってビルド時間を短縮しよう

XcodeGen

チームで開発しているとよくあるのですが、ファイル追加による.xcodeprojのコンフリクトに悩まされます。

ターミナルでxcodegenのコマンドを実行すれば、project.ymlファイルに記載されている内容から.xcodeprojを生成してくれるので、コンフリクトのストレスから解放されます。

Pokedexは一人で開発したので、コンフリクトの問題は元々起こりにくい環境ではあるのですが、マルチモジュールで開発しているため、その辺りの管理もやりやすいことから導入するメリットがあるなと思ってます。

XcodeGenによる新時代のiOSプロジェクト管理

SwiftGen

画像や色などのリソースを取得するためのファイルを自動生成してくれるツールです。

画像の呼び出しなどをする際の文字列の指定をtypoしてしまい取得できないといった、ミスを無くすことができます。

SwiftGenを導入して無駄なビルド負担を低減する

R.swiftとSwiftGenの導入方法とどちらを採用した方がいいのか

SwiftLint

.swiftlint.ymlファイルに記載されたコードのルールに応じて、ビルド時に警告を
出したりコンパイルエラーにさせることができるツールです。

コードにルールを設けることで書き方が統一されるので、コードの読みやすさが向上します。

簡単なルールなら、自動でコードの修正をしてくれる機能もあるので、そういった点でも入れておくとかなり楽になります。

Swiftの静的解析ツール「SwiftLint」のセットアップ方法

Rule Directory Reference

設計とマルチモジュール編

セットアップができたところで、Pokedexのアプリの全体の設計についてを先に見ていきましょう。

Pokedexでは、アーキテクチャにMVP + CleanArchitectureを採用しています。
ef5fc34c-7f99-4689-8f98-bcee1ff62735

設計(アーキテクチャ)について

業務での開発において私が最も重要視しているのはこれと言っても過言ではありません。

設計がしっかりしたソースコードは責務分けがしっかりされているので、特定の改修を加える際に楽になります。
その上、テストを書きやすくなるので、特定の改修を行った際にデグレが起きてないかのチェックを簡単にできるというメリットもあります。

業務であれば、新規開発する期間よりもアプリをリリースしてから運用して改修する期間の方が長いことがほとんどなので、改修をより早く容易にできる様にアプリの設計を採用すべきと考えています。

例えば、PokedexでAPI通信する際のアクセスする先のURLを変更したいという要望があった場合に、DataStoreのモジュールにある、アクセス先のURLを指定している部分を変更すればいいということが何となくつかめるので、すぐに改修でき、テストも書いてあれば問題がないかどうかもすぐに分かり、安心してアップデートすることができます。

もし設計が何もない状態で全てのロジックがUIViewController上に書かれたソースコードだと、まずはそのUIViewControllerのクラスを開き、そこから特定のソースコードを探して改修することになります。
それが何の責務わけもされていない、数千行のUIViewControllerだった場合、簡単な変更をするだけでも他の部分に影響がないか調査するだけでも一苦労です。

こういう状況で、素早いリリースサイクルをこなすということは、非常に難しくなるでしょう。

今回私はMVP + CleanArchitectureを採用しましたが、それでないとダメというわけではありません。

MVC、MVVM、VIPERなどの数々のアーキテクチャがありますが、これを使えば全部において最強!みたいなのは存在せず、リリースサイクルを短くするのに、最も都合がいい物を都度選択すればいいと私は考えています。

設計はあくまで目的を実現するのに手段に過ぎないので、作りたいアプリのサイズ感や性質などに応じて適切なものを選択できる様になるといいでしょう。

アーキテクチャに関しては様々な記事が出ているので、もし詳細に知りたい場合は以下の記事を参考にするのをお勧めします。

現場で選ばれているiOSアーキテクチャ

まだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて

マルチモジュール化

最大の特徴はマルチモジュールで作成されているということです。

スクリーンショット 2020-04-05 7 17 15

アプリのターゲットに加えて、DataStoreDomainPresentationの三つのEmbeddef Frameworkが追加されています。

これは、先ほども上げた、アプリの構成図の黄色い四角で囲われた部分ごとに切り分けているものです。
ef5fc34c-7f99-4689-8f98-bcee1ff62735

特にこのEmbedded Frameworkに切り分けて実装せずにアプリのターゲットのみで開発することも可能なのですが、Clean Architectureの様な細かく責務が分けられた設計の場合、使うことのメリットが大きいのでこの方法を採用しました。

ビルドパフォーマンスの向上

Xcodeの差分コンパイルのが各フレームワークごとに効く様になるので、スコープが小さくなり、ビルド時間の短縮に繋がります。
ただ、Pokedexの場合はアプリ自体が大規模ではないので、そこまで恩恵は受けられてません。

不要な参照を制御

1つのターゲットに全てのファイルが入っていると、例えば、元来Presentationでは、DataStoreの処理を直接呼び出す必要がないにも関わらず、何の制限もなく呼び出すことができてしまいます。これでは、せっかく責務を切り分けたのにも関わらず、参照のルールが崩れてしまうので、図の通りにならなくなってしまいます。

自分一人だけで開発していれば、そうしない様に気を付けるだけで済みますが、複数人で開発している場合や設計に慣れていない人と一緒に開発する場合はそういうわけにはいきません。

そこをEmbedded Frameworkで切り分けることで、importしなければ、参照ができなくなるという仕様が活きてきます。

例えばPresenterでは、UseCaseの処理を呼び出すために、import Domainをしなければ、UseCaseを呼び出すことができなくなります。
スクリーンショット 2020-04-05 20 52 27

最初の例で言うならば、以下の画像の様なコードをレビューで発見した場合、怪しいコードとして心してかからなければいけないということです。
スクリーンショット 2020-04-05 20 37 42

コードレビューをした際に、こういった設計にしたがっていないコードを見落としてしまう可能性がありますが、Frameworkのターゲットを切り分けておくと、importを確認すれば、まずは参照のルールが守られているコードであるかどうかがわかるので、レビューの負担が少し減るというわけです。

あとは、少し副産物的なメリットですが、変換候補を減らすことができるという点もメリットになるでしょう。
Clean Architecutureを使用することで、ファイルや定義が非常に多くなってしまいます。(Pokedexの場合、PokemonDetailという名前を含んだ命名が32個も存在します。)

必要ない定義がFrameworkの切り分けによって少なくなるだけで、コーディングしている際のストレスがなくなります。

その32個の定義が気になる物好きの人はこちら
PokemonDetailResponse
PokemonDetailRequest
PokemonDetailAPIGatewayProvider
PokemonDetailAPIGateway
PokemonDetailAPIGatewayImpl
PokemonDetailRepositoryProvider
PokemonDetailRepository
PokemonDetailRepositoryImpl
PokemonDetailUseCaseProvider
PokemonDetailUseCase
PokemonDetailUseCaseImpl
PokemonDetailTranslatorProvider
PokemonDetailTranslator
PokemonDetailTranslatorImpl
PokemonDetailModel
PokemonDetailBuilder
PokemonDetailView
PokemonDetailViewController
PokemonDetailFavoriteButton
PokemonDetailSingleImageCell
PokemonDetailDualImageCell
PokemonDetailHeightCell
PokemonDetailWeightCell
PokemonDetailPokemonTypeCell
PokemonDetailPokemonTypeItemCell
PokemonDetailStatusCell
PokemonDetailPresenter
PokemonDetailPresenterImpl
PokemonDetailWireframe
PokemonDetailWireframeImpl
TransitToPokemonDetailWireframe

(これが変換に一気に出てくると思うとゾッとしますねw)

ライブラリの依存を明確にできる

あとは、ライブラリの依存を明確にできるということもメリットになります。
どういうことかというと、PokedexではAPI通信をする際にはAlamofireというライブラリを使用していますが、このライブラリはDataStoreの中でしか使用しません。

そういった場合に、この様に、DataStoreに対してCarthageでインストールしたライブラリをリンクしています。
スクリーンショット 2020-04-05 22 23 30

そうすると、使用する必要がないPresentationやDomainからライブラリの呼び出しを制限することができるので、ライブラリへの依存が明確になります。

コーディング編

最後に実際のコーディングに関して、最低限これはしておいて欲しいというものを紹介します。

アクセス修飾子

アクセス修飾子を正しく使うことで、より堅牢にコードを保持することができます。
全て知っておく必要がありますが、Pokedexにおいて主に使っているのはprivateinternalpublicの三つです。

他のも知っておきたい方は以下の記事を参考にしてください。
知っているようで知らないSwift5のアクセス修飾子

private

外部から参照されない(もしくはされたくない)クラスや変数には必ずprivateをつける様にしましょう。

import UIKit

final class PokemonListCell: UITableViewCell {

    @IBOutlet private weak var spriteImageView: UIImageView!

    @IBOutlet private weak var numberLabel: UILabel!

    @IBOutlet private weak var nameLabel: UILabel!

    func setData(_ data: PokemonListModel.Pokemon) {
        self.spriteImageView.loadImage(with: data.imageUrl, placeholder: Asset.mosnterball.image)
        self.numberLabel.text = "No.\(data.number)"
        self.nameLabel.text = data.name
    }
}

@IBOutletで紐づけられたpropertyはprivate付け忘れがちですが、継承して使うとかが無い限りはprivateを付けておきましょう。

import Foundation

public enum PokemonListAPIGatewayProvider {

    public static func provide() -> PokemonListAPIGateway {
        return PokemonListAPIGatewayImpl(dataStore: PokeAPIDataStoreProvider.provide())
    }
}

public protocol PokemonListAPIGateway {
    func get(completion: @escaping ((Result<PokemonListResponse, Error>) -> Void))
}

private struct PokemonListAPIGatewayImpl: PokemonListAPIGateway {

    let dataStore: PokeAPIDataStore

    func get(completion: @escaping ((Result<PokemonListResponse, Error>) -> Void)) {
        self.dataStore.request(PokemonListRequest(), completion: completion)
    }
}

PokemonListAPIGatewayImplはPokemonListAPIGatewayProviderのprovide()の中でしか呼び出す必要が無いので、privateをつけておくと、他のファイルから参照できない様になります。

public

Pokedexの場合、Embedded Frameworkを利用しているので、フレームワーク外から参照されるクラスに関してはpublicを指定する必要があります。

import DataStore
import Foundation

public enum PokemonListUseCaseProvider {

    public static func provide() -> PokemonListUseCase {
        return PokemonListUseCaseImpl(
            repository: PokemonListRepositoryProvider.provide(),
            translator: PokemonListTranslatorProvider.provide()
        )
    }
}

public protocol PokemonListUseCase {
    func get(completion: @escaping ((Result<PokemonListModel, Error>) -> Void))
}

private struct PokemonListUseCaseImpl: PokemonListUseCase {

    let repository: PokemonListRepository
    let translator: PokemonListTranslator

    func get(completion: @escaping ((Result<PokemonListModel, Error>) -> Void)) {
        self.repository.get { result in
            switch result {
            case .success(let response):
                completion(.success(self.translator.convert(from: response)))
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}

PresentationのPokemonListPresenterImpl内で、PokemonListUseCaseが参照されているため、PokemonListUseCaseにはpublicを付ける必要があります。

PokemonListUseCaseProviderとその中のprovide()メソッドもPokemonListBuilderで呼び出されるのでpublicをつけましょう。

final修飾子

継承されないclassには必ずfinalを指定する様にしましょう。

import UIKit

final class PokemonDetailViewController: UIViewController {

}

これを付けることによって、継承の必要性が無いということを明示できるのでソースコードの理解に役立つ上に、実行時のパフォーマンス向上にもつながります。

Swiftのfinalについて

Decodable

PokeAPIはレスポンスがJsonなので、Codableという機能でレスポンスをパースすることができます。

その時に、Codableは、Encodable(オブジェクト -> Data型に変換する)とDecodable(Data型 -> オブジェクトに変換する)の両方に準拠してしまうので、今回の様なEncodableが必要が無い場合には、Decodableのみに準拠させると目的がはっきりするのでわかり易くなります。

import Foundation

public struct PokemonListResponse: Decodable {

    public let count: Int

    public let previous: String?

    public let next: String?

    public let results: [Result]
}

extension PokemonListResponse {

    public struct Result: Decodable {

        public let name: String

        public let url: String
    }
}

おわりに

最初、Pokedexを公開した時に誰も見てくれないだろうと思ってtwitterで呟いたら、予想以上の人に見てもらえたので、急いで解説記事を書きました?

至らぬところは多々ある上に、少し長く難しい内容になってしまったかもしれませんが、おそらくこの記事に書いている内容を実践して身につけていけば、業務未経験の人でもiOSエンジニアとして仕事を得るのに役立つ内容であると思います。

まだ少し書きたい事柄もあり、適度に加筆修正する予定なので、この記事についての質問も歓迎ですし、さらにはPokedexへのissue、pull requestもお待ちしております!

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

iOS app の習得日記 (2)

(https://qiita.com/gomi_ningen/items/4e0e5bd98f08c4bcf93d)

続き

4.1 todoリスト作成までいった

```
import UIKit
import Foundation

class TODOMainViewController: UIViewController {

@IBOutlet weak var dismissButton: UIBarButtonItem!

override func viewDidLoad() {
    super.viewDidLoad()
    dismissButton.target = self
    dismissButton.action = #selector(TODOMainViewController.dismissButtonTapped(_:))
}

func dismissButtonTapped(_ sender: Any) {
    dismiss(animated: true, completion: nil)
}

}
```

新しくstoryboard と viewcontrollerを作り、
dismissButtonを接続しようとするも、

"Could not insert new outlet connection".

と出る。

(https://dev.classmethod.jp/articles/remove-xcode-derived-data/)
こんなのがでてきた
キャッシュを消せと
これでいいのか?

明日やってみよう

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