| ページ一覧 | ブログ | twitter |  書式 | 書式(表) |

MyMemoWiki

差分

ナビゲーションに移動 検索に移動
65,218 バイト追加 、 2024年9月12日 (木) 10:53
| [[Xcode]] | [[Mac]] | [[IPhone Xcode]] |[[SwiftUI]] | [[Swift Sample]] | [[Cocoa]] | [[リファクタリング]] |{{amazon|B081GK7636}}
==環境==
*https://swift.org/
*[https://www.hackingwithswift.com/example-code Swift Knowledge Base]
===Document===
----
=====[https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html Swift言語ガイド]=====
----
[https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html Swift言語ガイド]
 
=====[https://www.hackingwithswift.com/ Hacking With Swift]=====
----
[https://www.hackingwithswift.com/ Hacking With Swift]
 
===ライブラリ===
----
 
====標準ライブラリ====
----
*言語の一部として基本機能を提供
*インポートせずに常に利用可能
 
====コアライブラリ====
----
*モジュールとしてインポートすることで利用可能となる
=====[https://developer.apple.com/documentation/foundation Foundation]=====
*多くのアプリケーションに必要になる機能を提供
*[https://developer.apple.com/documentation/foundation/url_loading_system URL Loading]
 
=====l[https://apple.github.io/swift-corelibs-libdispatch/ ibdispatch]=====
*マルチコアハードウェア並列処理を抽象化
 
=====XCTest=====
*ユニットテスト用
 
====[https://developer.apple.com/jp/mac-catalyst/ Mac Catalyst]====
*https://developer.apple.com/jp/mac-catalyst/
Mac Catalystを使って構築したMac向けのネイティブAppは、iPad向けAppとコードを共有でき、Mac専用の機能を追加することもできます。macOS Montereyでは、最新のAPIを使用して、ウインドウのタイトルバーにポップアップボタン、ツールチップ、サブタイトルを表示することができます。また、タッチ操作の代替機能、キーボードで操作する機能、SiriのIntentのサポートを提供できるほか、Commandキーを押しながら「P」を押していつでもプリントできるようにするなど、さまざまな操作への対応が追加されました
 
====[https://www.swift.org/package-manager/ Swift Package Manager]====
----
*https://www.swift.org/package-manager/
*https://blitzgate.co.jp/blog/2234/
*https://qiita.com/hironytic/items/09a4c16857b409c17d2c
=====[https://swiftpackageindex.com/ Swift Package Index]=====
 
===[[Ubuntu]]へインストール===
----
*ツールチェイン依存ライブラリのインストール
<pre>
$ sudo apt-get update
$ sudo apt-get install clang libicu-dev
</pre>
*ツールチェインのダウンロードと解凍
*https://www.swift.org/download/
<pre>
$ wget https://download.swift.org/swift-5.5.1-release/ubuntu2004/swift-5.5.1-RELEASE/swift-5.5.1-RELEASE-ubuntu20.04.tar.gz
$ tar xzf swift-5.5.1-RELEASE-ubuntu20.04.tar.gz
</pre>
*パスを通す(解凍されたディレクトリのusr/bin)
*~/.bashrc に追記
<pre>
export PATH=/home/ubuntu/opt/swift-5.5.1-RELEASE-ubuntu20.04/usr/bin:"${PATH}"
</pre>
[[File:swift_ubuntu.png|400px]]
 
==[[Xcode]]==
*[https://www.typea.info/blog/index.php/2020/12/06/xcode_macos_proguramming/ Xcode初期環境]
 
===ツールチェイン===
----
*ツールチェインとは、コンパイラやデバッガなど開発に必要なツールを一通りまとめたもの
*Swiftのツールチェインは[[Xcode]] に含まれる
 
===開発ツール===
----
====Swift Package Manager====
----
*パッケージとはソースコードとマニフェストをまとめたもの
*マニフェストファイルは、依存関係などをまとめたもの
*Swift Package Manager はこれらをもとにビルドする
*マニフェストに記述された外部パッケージはビルド時にダウンロードされる
 
====[https://lldb.llvm.org/ LLDB]====
----
*オープンソースデバッガ
====Interface Builder====
----
=====[https://www.typea.info/blog/index.php/2022/03/08/xamarin_mac_app_storyboard/ storyboard]=====
*[https://www.typea.info/blog/index.php/2022/03/08/xamarin_mac_app_storyboard/ storyboard]
 
=====[https://www.typea.info/blog/index.php/2022/03/11/nstableview_celbased/ NSTableView CellBased]=====
*[https://www.typea.info/blog/index.php/2022/03/11/nstableview_celbased/ NSTableView CellBased]
 
=====[https://www.typea.info/blog/index.php/2022/03/23/nstableview_viewbased_macos/ NSTableView ViewBased]=====
*[https://www.typea.info/blog/index.php/2022/03/23/nstableview_viewbased_macos/ NSTableView ViewBased]
 
=====[https://www.typea.info/blog/index.php/2022/04/03/nsalert_macos/ NSAlert ダイアログ]=====
*[https://www.typea.info/blog/index.php/2022/04/03/nsalert_macos/ NSAlert ダイアログ]
 
====AutoLayout====
----
=====[https://www.typea.info/blog/index.php/2022/03/26/autolayout_concept/ AutoLayout概要]=====
*[https://www.typea.info/blog/index.php/2022/03/26/autolayout_concept/ AutoLayout概要]
 
=====[https://www.typea.info/blog/index.php/2022/03/28/interface_builder_autolayout/ AutoLayout操作]=====
*[https://www.typea.info/blog/index.php/2022/03/28/interface_builder_autolayout/ AutoLayout操作]
=====[https://www.typea.info/blog/index.php/2022/05/30/swift_ios_chart/ AutoLayout簡易操作 TinyConstraint]=====
*[https://www.typea.info/blog/index.php/2022/05/30/swift_ios_chart/ AutoLayout簡易操作 TinyConstraint]
 
====画面遷移====
----
*https://qiita.com/superman9387/items/c006ced215352f28a7b9
=====[https://www.typea.info/blog/index.php/2022/03/13/storyboard_childwindow_macos/ 子画面表示]=====
*[https://www.typea.info/blog/index.php/2022/03/13/storyboard_childwindow_macos/ storyboard を使って子画面を表示]
=====[https://www.typea.info/blog/index.php/2022/03/15/storyboard_segue_connect_screen/ segue(セグエ)で画面遷移]=====
----
*[https://www.typea.info/blog/index.php/2022/03/15/storyboard_segue_connect_screen/ storyboard segue(セグエ)で画面遷移]
*segueを呼び出し画面遷移させる
<pre>
//呼び出し元
performSegue(withIdentifier: "some_segue_id", sender: nil)
</pre>
=====[https://www.typea.info/blog/index.php/2022/06/04/ios_swift_navigation_controller/ Navigation Controller ナビゲーションバー]=====
*[https://www.typea.info/blog/index.php/2022/06/04/ios_swift_navigation_controller/ NavigationController ナビゲーションバー]
=====[https://www.typea.info/blog/index.php/2022/03/12/interfacebuilder_storyboard_tabview_screentransition/ Tab Viewの利用と画面遷移]=====
*[https://www.typea.info/blog/index.php/2022/03/12/interfacebuilder_storyboard_tabview_screentransition/ storyboard を使って Tab Viewの利用と画面遷移]
==コマンドライン==
*swiftと入力
*終了には、:q もしくは、ctrl+Dもしくは、ctrl+D
*パスが通ったところに配置されていれば実行できる
<pre>
test
</pre>
====コマンドラインツールを実行===
*アクティブ開発ディレクトリのコマンドラインツールを見つけて実行する
*アクティブ開発ディレクトリは、xcode-select で指定する
$ xcrun swift
</pre>
 
===プロジェクトの作成と実行===
*プロジェクトの作成
<pre>
$ mkdir swift_demo
$ cd swift_demo
$ swift package init --type executable
</pre>
*ビルドして実行
<pre>
$ swift run
[0/0] Build complete!
Hello, world!
</pre>
*.build/debug に実行ファイルが出力される
<pre>
$ swift build
[0/0] Build complete!
$ .build/debug/swift_demo
Hello, world!
</pre>
 
===SDK===
*SDKを指定してswiftを起動するときに、指定できるSDK一覧を表示
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk
</pre>
 
{{amazon|B086VVQ3DJ}}
==コメント==
===クイックヘルプ===
----
* 「///」 : クイックヘルプ用コメント
*[https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/MarkupFunctionality.html#//apple_ref/doc/uid/TP40016497-CH54-SW1 マークアップ]
<pre>
/// arp -a の結果をHostListに格納する
///
/// - Parameters:
/// - hosts: 結果を格納する Host のリストを渡す
/// - Throws:
/// - Returns:
</pre>
[[File:swift_quick_help_comment.png|400px]]
 
==アクセス制御==
 
{| class="wikitable"
|-
! scope="col"| アクセス制御
! scope="col"| 意味
|-
| open
| モジュール情報をインポートすればどんなソースファイルからでもアクセス可能、サブクラスの作成や上書き定義も自由にできる
|-
| public
| モジュール情報をインポートすればどんなソースファイルからでもアクセス可能、サブクラスの作成や上書き定義はそのクラスを定義したモジュール内でのみ可能
|-
| internal
| 定義を含むソースファイルと同じモジュール内からはどのソースファイルからもアクセス可能。アクセス制御規定値
|-
| fileprivate
| 定義されたソースファイルの中だけで使用できる
|-
| private
| 定義単位の内部のみで利用できる
|-
|}
==データ型==
*型の実体はインスタンス
*値型と参照型
*===基本データ型=======Any====*全ての型が暗黙的に準拠しているプロトコル*どのような型の値も代入できるため、値を代入する型が決まっていない場合に使用できる<pre> 1> var a:Any = 123a: Int= 123 2> a = "123" 3> a$R0: String = "123"</pre> ====Bool==== *true/false====Character==== *UIntUnicodeの1文字*リテラルは、Stringと同様なので、アノテーションするなどが必要<pre> 1> let s = "s"s: String = "s" 2> let c: Character = "c"c: Character = "c"</pre> ====String ====*Float値型*== 比較、+ 結合*Double高度な操作は、Foundationで提供される<pre> 1> let r = "abc".compare("def") == ComparisonResult.orderedAscending**r: Bool : = true</falsepre> ====数値型====*変換にはイニシャライザを使用する<pre>let d = 1.5let i = Int64(d)</pre>*Character 演算は両辺の型が一致しないとエラーとなる =====Int==========UInt==========Float==========Double===== ====型を調べる====<pre>1> print(type(of: Unicodeの1文字"hello"))**String : 値型</pre> 
===変数の定義===
----
*変数
var 変数名 : 型 = 式
let age = 49
</pre>
 
===型変換===
----
*暗黙の型変換は行われない
*Double() や Int() などのイニシャライザを利用する
$R0: Double = 5
</pre>
 
===型を確認する===
----
<pre>
var greeting = "Hello, playground"
type(of: greeting)
</pre>
 
===文字===
----
*https://qiita.com/gfx/items/e739b2d38b3a7423a86d
 
===文字列===
----
*連結は +
*リテラルは、"で囲む
*\n 改行など\でエスケープ
====書式====
<pre>
let frmt = String(format: "%03d", 1)
</pre>
====トリミング====
----
<pre>
let trimmed = " abc ".trimmingCharacters(in: .whitespaces)
</pre>
 
====文字列埋め込み====
----
*\(式) で展開される
<pre>
s: String = "Number 15"
</pre>
 ====Raw文字列====----*文字列の前に1つ以上の#記号を配置<pre>let pattern = #"[0-9]{0,3}"#</pre> ====[https://developer.apple.com/documentation/swift/substring/ Substring]====----*Stringをスライスした場合など、Substringのインスタンスが結果として返される ====ヒアドキュメント====----*三重引用符で開始位置を揃える<pre> let magicpacket = service.createData(mac:"64:00:6a:20:EC:60") print(""" MAGIC PACKET ======== FROM ============ \(magicpacket) MAGIC PACKET ======== TO ============ """)</pre>====エンコーディング====*https://developer.apple.com/documentation/swift/string/encoding ===nil===----*扱う値が存在しないことを表すため、'''nil''' が用意されている*Swiftでは基本的に、変数や定数にnilを許容しない*nil 値を持つことがある場合、Int? 型などで表す -> オプショナル型 ===オプショナル型 Optional<T>===----*パラメータ付型指定では、Optional<Int>*オプショナル型からデータを取り出 -> アンラップ*オプショナルをprintする場合、明示的にString(describing:)を使用する<pre>var opi:Int?print("\(String(describing:opi))")opi = 1var i:Int = opi!print("\(i)") </pre>*実行<pre>nil1</pre>*Optional<T>.none と nilが対応づけられている<pre> 3> let n:String? = niln: String? = nil 4> let n = Optional<String>.nonen: String? = nil</pre> ====初期化====----*Optional<T> は T?で宣言できる : String?*Optional<T> を T! で宣言すると、使用時に自動的にアンラップされる : String!<pre> 5> let i = Optional(1)i: Int? = 1 6> let j = Optional<Int>.some(2)j: Int? = 2 7> let k:Int? = 3k: Int? = 3</pre> *Int?<pre> 1 > let i:Int? = 1 2 > let j = i + 1error: repl.swift:33:9: error: value of optional type 'Int?' must be unwrapped to a value of type 'Int'let j = i + 1</pre>*Int! <pre> 1 > var i: Int! = 1 2 > let j = i + 1j: Int = 2</pre> ====アンラップ====----*オプショナルバインディング*??演算子*強制アンラップ=====オプショナルバインディング(if-let文)=====*オプショナルがnilでない場合、すぐに処理で使用したい場合*if や while の条件部にのみ記述できる<pre>if let 定数名 = Optional<T>の値 { }</pre>*例<pre>let optNum: Int? = hoge()if let num = optNum { // nil でない場合} else { // nil の場合}</pre>*複数同時にも使用できる<pre>let optNum1: Int? = hoge()let optNum2: Int? = hoge() if let num1 = optNum1, let num2 = optNum2 { // nil でない場合} else { // nil の場合}</pre>=====??演算子=====*Optional<T>の値 ?? none の場合<pre> 8> let i:Int? = 1i: Int? = 1 9> let j = i ?? 2j: Int = 1</pre> =====強制アンラップ=====*Optional<T>の値!*強制的に取り出す。値がなければエラー<pre> 10> let i = Optional<Int>.nonei: Int? = nil 11> i!__lldb_expr_24/repl.swift:11: Fatal error: Unexpectedly found nil while unwrapping an Optional value</pre>====オプショナルチェイン====*Optional<T>の値?.メソッド  でアンラップせずにプロパティやメソッドにアクセスできる。値が存在しない場合、nil ====map(_:),flatMap(_:) アンラップを行わずに値の変換を行う====*クロージャを引数に渡す*flatMap はクロージャの戻り値もOptional<pre> 1> let i: Int? = 1i: Int? = 1 2> i.map({ v in v + 1})$R4: Int? = 21})$R5: Int? = 1 3> let j: Int? = nilj: Int? = nil 4> j.map({ v in v + 1})$R6: Int? = nil</pre> ===Data型===*メモリ上のバイト列を管理*Foundationのimportが必要====Stringとの相互変換====<pre>let data: Data? = str.data(using: .utf8)let str: String? = String(data: data, encoding: .utf8)</pre> ==コレクション==*以下にはコレクションとしての共通機能がある**配列 Array<Element>**辞書 Dictionary<Key,Value>**範囲 Range<Bound>**文字列 String*操作できるインターフェースについては、プロトコル参照===配列Array<T>===----*T型の配列は、Array<T> もしくは、[T]*順序を持ったコレクション 
*型省略初期化
<pre>
sa2: [String] = 0 values
</pre>
====参照と追加参照と追加と削除====<pre> 1> var ia = [1,2,3]ia: [Int] = 3 values { [0] = 1 [1] = 2 [2] = 3}</pre>*追加
<pre>
> var ia = [1,2,3] > ia[1]$R0: Int = 2 > ia.append(4) 3> ia$R1R0: [Int] = 4 values {
[0] = 1
[1] = 2
[2] = 3
[3] = 4
}
</pre>
*削除
<pre>
4> ia.remove(at:2)
$R1: Int = 3
5> ia
$R2: [Int] = 3 values {
[0] = 1
[1] = 2
[2] = 4
}
</pre>
*追加
<pre>
6> ia.insert(-1, at:2)
7> ia
$R3: [Int] = 4 values {
[0] = 1
[1] = 2
[2] = -1
[3] = 4
}
</pre>
 
====スライス====
<pre>
8> l
$R5: [Int] = 8 values {
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
[5] = 6
[6] = 7
[7] = 8
}
9> l[2..<5]
$R6: ArraySlice<Int> = 3 values {
[2] = 3
[3] = 4
[4] = 5
</pre>
 
====map関数====
*元の配列内の全てのアイテムに対してアクションを行い結果に基づいた配列を作成
<pre>
let base = [1,2,3]
let dbl = base.map {
(val) -> Int in
return val * 2
}
print(dbl)
結果:[2, 4, 6]
</pre>
*省略形
<pre>
let base = [1,2,3]
let dbl = base.map({ $0 * 2 })
print(dbl)
結果:[2, 4, 6]
</pre>
====filter関数====
*元配列から条件に一致する配列を生成
<pre>
let nums = [0,1,2,3,4,5,6]
print(nums.filter({ (num) -> Bool in
return num % 2 == 0
}))
結果:[0, 2, 4, 6]
</pre>
*省略形
<pre>
let nums = [0,1,2,3,4,5,6]
print(nums.filter({ $0 % 2 == 0 }))
</pre>
====reduce関数====
*配列内の全ての値を単一の結果に集約
<pre>
let nums = [1,2,3]
let initial = 10;
print(nums.reduce(initial, { (curTotal, val) -> Int in
return curTotal + val
}))
結果:16
</pre>
*省略形
<pre>
let nums = [1,2,3]
let initial = 10;
print(nums.reduce(initial,{ $0 + $1 }))
結果:16
</pre>
 
===タプル===
----
<pre>
(型名1, 型名2, 型名3, ・・・)
</pre>
 
*インデックスでアクセス
<pre>
let prof : (String, Double, Int) = ("Yagi", 173.0, 49)
print("\(prof.0) \(prof.1) \(prof.2)")
</pre>
*変数に代入
<pre>
let (name, tall, age) = prof
print("name is \(name) \(tall) cm \(age) yo")
</pre>
*名前付タプル
<pre>
let prof2 = (name:"Yagi",tall:173.0, age:49)
print("name is \(prof2.name) \(prof2.tall) cm \(prof2.age) yo")
</pre>
*結果
<pre>
Yagi 173.0 49
name is Yagi 173.0 cm 49 yo
name is Yagi 173.0 cm 49 yo
</pre>
 
===辞書 [https://developer.apple.com/documentation/swift/dictionary Dictionary<Key, Value>]===
----
*キーと値をもつコレクション
<pre>
1> var sex = ["1":"male","2":"female"]
sex: [String : String] = 2 key/value pairs {
[0] = {
key = "2"
value = "female"
}
[1] = {
key = "1"
value = "male"
}
}
2> sex["1"]
$R0: String? = "male"
3>
</pre>
====削除====
<pre>
3> sex.removeValue(forKey:"1")
$R1: String? = "male"
4> sex
$R2: [String : String] = 1 key/value pair {
[0] = {
key = "2"
value = "female"
}
}
</pre>
====空の辞書====
<pre>
var emptyDict: [String: String] = [:]
</pre>
 
===Set===
----
<pre>
var setOfStrings = Set<String>()
var fruitSet : Set = ["apple","orange","orange","banana"]
</pre>
 
===範囲 Range<Bound>===
----
*末尾の値を含まない、..< もしくは含む ... により生成される
 
====..<====
*Range<Bound>
*CountableRange<Bound>
*PartialRangeUpTo<Bound>
 
<pre>
1> let r = 0 ..< 10
r: Range<Int> = 0..<10
</pre>
 
====...====
*ClosedRange<Bound>
*CountableClosedRange<Bound>
*PartialRangeThrough<Bound>
*ParatialRangeFrom<Bound>
*CountablePartialRangeFrom<Bound>
 
<pre>
2> let s = "a" ... "z"
s: ClosedRange<String> = {
lowerBound = "a"
upperBound = "z"
}
</pre>
*前置
<pre>
4> let t = ...1.95
t: PartialRangeThrough<Double> = {
upperBound = 1.95
}
</pre>
*後置
<pre>
5> let f = 1.95...
f: PartialRangeFrom<Double> = {
lowerBound = 1.95
}
</pre>
*範囲に含まれるか
<pre>
6> f.contains(3.0)
$R0: Bool = true
</pre>
===イテレーター===
[https://developer.apple.com/documentation/swift/iteratorprotocol IteratorProtcol]
<pre>
let animals = ["Antelope", "Butterfly", "Camel", "Dolphin"]
var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
print(animal)
}
</pre>
*ポインタはないため、*、&、-> は異なった意味
*インクリメント(++)、デクリメント(--) は廃止された
===高度な演算===
----
*Foundation をインポートすることで、sin、logなど利用可能になる
===型の判定===
----
*is
<pre>
11> let a:Any = "123"
a: String = "123"
12> a is String
$R1: Bool = true
</pre>
 
===キャスト===
----
<pre>
式 as 型
</pre>
====アップキャスト(as)====
----
*as
*コンパイル可能なアップキャストは常に成功
*暗黙も可
<pre>
1> let a: Any = "abc" as Any
a: String = "abc"
2> let b: Any = "abc"
b: String = "abc"
</pre>
 
====ダウンキャスト(as? as!)====
----
*as? キャストが正しく行われた場合オプショナル型、不正な場合、nil
*as! 強制キャスト失敗した場合、実行時エラー
*コンパイルできても実行時に失敗する可能性あり
 
<pre>
8> let a:Any = "123"
a: String = "123"
9> let s = a as? String
s: String? = "123"
10> let i = a as? Int
i: Int? = nil
11> let j= a as! Int
error: repl.swift:11:6: error: '=' must have consistent whitespace on both sides
let j= a as! Int
^
</pre>
 
====独自演算子の定義====
----
*https://qiita.com/shimesaba/items/cb36c0120a0a9f509da7
<pre>
infix operator *** {
associativity none
precedence 130
}
func ***(lhs: Int, rhs: Int) -> Int {
return lhs * rhs
}
</pre>
 
==モジュールと名前空間==
===モジュールのインポート===
*フレームワークや外部モジュールからクラスや関数情報をimportで取込む
*[[Xcode]]にある、*.swiftdoc や *.swiftmodule がSwiftが利用できるモジュール情報
<pre>
import Foundation
</pre>
===名前空間===
*ドットで区切って修飾
 
==制御構文==
===if===
*条件の()は不要
*1文しかなくても{}は省略不可
*条件はBool型である必要がある
<pre>
if 条件 {
} else if 条件 {
} else {
}
</pre>
===for===
*シーケンスプロトコルに準拠したオブジェクトを順次処理
<pre>
for 変数 in シーケンス {
}
</pre>
====整数のレンジ====
*開始値...終了値
<pre>
for i in 0...10 {
print(i)
}
</pre>
*開始値..<終了値
<pre>
for i in 0..<10 {
print(i)
}
</pre>
*ステップでシーケンス作成(to値を含まない)
<pre>
for num in stride(from: 0, to: 10, by: 2) {
print(num)
}
//結果
0
2
4
6
8
</pre>
*ステップでシーケンス作成(through値を含む)
<pre>
for num in stride(from: 0, through: 10, by: 2) {
print(num)
}
//結果
0
2
4
6
8
10
</pre>
 
===for-in===
*where以下は省略可能
<pre>
for 定数 in 式 where 式 {
}
</pre>
<pre>
for i in 0 ... 10 where i % 2 == 0 {
print(i)
}
</pre>
 
===while===
<pre>
while 条件 {
}
</pre>
 
===repeat-while===
*必ず一回は処理したい場合
<pre>
repeat {
} while 条件
</pre>
 
===switch===
*break不要
*分岐に文字列や構造を持つ型も利用できる
*列挙/範囲指定できる
*breakがなくてもcaseブロック終了でswitchを抜ける
*break書いても良い
*fall through は明示することで可能
*式がとりうる値を網羅していないとコンパイルエラー
<pre>
switch 式 {
case ラベルn:
default:
}
</pre>
<pre>
for i in 0 ... 10 {
switch i {
case 2, 4: // 列挙できる
print("twe,four\(i)") // breakがなくてもcaseブロック終了でswitchを抜ける
case 5:
print("five \(i)")
case 6:
break // break書いても良い
case 7 ..< 9: // 範囲指定
print("7 <= i < 9: \(i)")
fallthrough // fall through は明示することで可能
default: // 式がとりうる値を網羅していないとコンパイルエラー
print("other \(i)")
}
}
結果:
other 0
other 1
twe,four2
other 3
twe,four4
five 5
7 <= i < 9: 7
other 7
7 <= i < 9: 8
other 8
other 9
other 10
</pre>
====タプルに適用====
<pre>
let switchingTuple = ("Yes", 123)
switch switchingTuple {
case ("Yes", 123):
print("Tuple contains 'Yes' and '123'")
case ("Yes", _):
print("Tuple contains 'Yes' and something else")
case (let string, _):
print("Tuple contains the string '\(string)' and something else")
}
</pre>
===ラベル===
*break はラベルのブロックを抜ける
*continue はラベルの次繰り返しへ
====ループ====
<pre>
loop1: while 式 {
loop2: while 式 {
while 式 {
break loop1
continue loop1
break loop2
continue loop2
}
}
}
</pre>
====if switch====
<pre>
cond1: if 式 {
cond2: if 式 {
if 式 {
break cond2
}
break cond1
}
}
</pre>
 
===do===
 
 
===エラー処理===
----
====Optional<Wrapped>型によるエラー処理====
----
*値ありを成功、なしを失敗とみなす
*エラーが発生する側は、戻り値の型をOptional<Wrapped>とするだけで良い
*エラー処理側で、オプショナルチェイン、オプショナルバインディングなど簡潔に記述できる
*呼び出し元にエラーの詳細情報を提供できない
 
====Result<Success,Failure>型によるエラー処理====
----
*成功を結果の値、失敗を詳細で表す
<pre>
enum ErrReson : Error {
case overflow
case even
}
 
func someProc(_ underTenOddNum: Int) -> Result<String, ErrReson> {
if underTenOddNum >= 10 {
return .failure(.overflow)
}
if underTenOddNum % 2 == 0 {
return .failure(.even)
}
return .success("Success!! odd number & under ten")
}
 
func checkResult(_ result: Result<String, ErrReson>) {
switch result {
case let .success(message):
print(message)
case let .failure(err):
print("\(err)")
}
}
 
checkResult(someProc(11))
checkResult(someProc(8))
checkResult(someProc(9))
</pre>
*結果
<pre>
overflow
even
Success!! odd number & under ten
</pre<
 
====エラーを投げる====
----
*プロトコル Errorに適合している必要がある
<pre>
throw 式
</pre>
*エラーを投げる関数は直後に、'''throws'''宣言が必要
 
====do-catchによるエラー処理====
----
*Swift標準のエラー処理機構
*エラーを投げる関数を呼び出すには、関数名の前に'''try'''を付与して呼び出す必要がある
*throw分のエラーを表現するには、エラーであることを表す、Errorプロトコルに準拠する必要がある
*Errorプロトコルに準拠させる場合、列挙を使用するのが一般的
 
=====tryを使って呼び出すだけ =====
さらに上位に伝播
=====do-catch=====
捕捉
=====try?=====
*戻り値をオプショナルとして扱う。エラーが発生した場合値が nil となる
*「エラーは起きるかもしれない。でも起きたとしても無視する」
 
=====try! =====
戻り値を通常型として扱う。エラー発生時は実行エラー
=====パターン=====
*定数または変数のマッチング
*パターンを記述しないcatchも可能。発生したエラーは、'''error'''で参照できる
 
<pre>
do {
try 関数
} catch パターン where節 {
} catch {
}
</pre>
*例
<pre>
enum ParaErr : Error {
case NIL
case OVERFLOW
}
 
func sub(para:Int?) throws {
if let val = para {
if val > 5 {
throw ParaErr.OVERFLOW
}
print("para is \(val).")
} else {
throw ParaErr.NIL
}
}
 
func test() {
let paras: [Int?] = [1, nil, 10]
for para in paras {
do {
try sub(para:para)
} catch ParaErr.NIL {
print("Parameter is nil.")
} catch {
print(error)
}
}
}
test()
</pre>
*結果
<pre>
para is 1.
Parameter is nil.
OVERFLOW
</pre>
====throwsキーワード====
----
*関数、イニシャライザ、クロージャの定義にthrowsを追加すると、それらの中で、do-catchを用いずにthrow文によるエラーを発生させることができる
 
===guard文===
----
*想定外の状況が発生した場合に、その状況から抜け出す(早期退出する[[リファクタリング|ガード節]])
*条件には、通常の条件やオプショナル束縛などを記述できる
*if - let 同様、guard - let が利用できる。ifと異なり、ブロック外でも利用可能
<pre>
guard 条件 else {
/* break や return、throw など制御が戻らないことが自明な文 */
}
</pre>
*例
<pre>
10> func printAge(age: Int) {
11. guard age >= 0 else {
12. print("不正な年齢\(age)")
13. return
14. }
15. print("\(age)歳")
16. }
17> printAge(age: -1)
不正な年齢-1
18> printAge(age: 50)
50歳
</pre>
 
===遅延実行===
----
*記述されている箇所より後で実行する
====defer文====
----
*スコープを抜ける際に実行したい処理を定義する。
*複数のdefer文を記述した場合は逆順で実行される。
 
===[https://docs.swift.org/swift-book/ReferenceManual/Patterns.html パターンマッチ]===
----
*値の持つ構造や性質を表現するパターンのマッチングによって制御を行うことができる
*if,guard,for-in,while,doのcatchでも case キーワードを使えば利用できる
 
<pre>
if case パターン = 制御式 {}
</pre>
<pre>
guard case パターン = 制御式 else {}
</pre>
*一致したもののみ処理される
<pre>
for case パターン in シーケンス {}
</pre>
<pre>
while case パターン = 制御式 {}
</pre>
 
====[https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#grammar_expression-pattern 式パターン]====
*~= により評価
*~= は型別に定義されていて、範囲型では、contains(_:)メソッドで評価される
<pre>
19> let n = 3
20> let r = 1...3
21> r ~= n
$R4: Bool = true
</pre>
====[https://docs.swift.org/swift-book/ReferenceManual/Patterns.html バリューバインディングパターン]====
*値を変数や定数に代入する
*var、let と他のパターンを組み合わせマッチしたら代入
<pre>
1> let point = (3, 2)
2> switch point {
3. case let (x, y):
4. print("\(x),\(y)")
5. }
3,2
</pre>
 
====[https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#grammar_optional-pattern オプショナルパターン]====
*Optional<T>の値有無を評価
<pre>
6> let o: Int? = 42
7> if case .some(let x) = o {
8. print(x)
9. }
42
</pre>
====[https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#grammar_enum-case-pattern 列挙型ケースパターン]====
*列挙型ケースとの一致
*列挙型参照
 
====is/as演算子によるキャスティングパターン====
*is による判定
<pre>
12> let a: Any = "s"
a: String = "s"
13> switch a {
14. case is String:
15. print("str")
16. default:
17. print("other")
18. }
str
</pre>
*as によるダウンキャスト
<pre>
19> let a: Any = 1
20> switch a {
21. case let s as String:
22. print("str:\(s)")
23. case let i as Int:
24. print("int:\(i)")
25. default:
26. print("other")
27. }
int:1
</pre>
 
==構造体==
*structは値型
*プロトコルは、実装すべきメソッドやプロパティが定まっている
*構造体の値を変更するメソッドは先頭に mutating を付与
<pre>
struct 名前 : プロトコル{
変数・定数定義
イニシャライザ定義
メソッド定義
その他定義
}
</pre>
*例
*init() イニシャライザ
<pre>
 
struct Person {
var name: String
var age: Int
init(name :String , age : Int) {
self.name = name
self.age = age
}
mutating func addAge(num: Int) {
self.age += num
}
}
struct Team {
let name: String
let members: [Person]
func printMembers(){
for member in members {
print("\(member.name) \(member.age)")
}
}
}
 
func test() {
var me = Person(name: "Yagi", age: 49)
me.addAge(num: 3)
print(me)
 
let team = Team(name:"family", members: [me])
team.printMembers()
}
 
test()
</pre>
*実行
<pre>
$ swift struct.swift
Person(name: "Yagi", age: 52)
Yagi 52
</pre>
 
===イニシャライザ===
----
*カスタムイニシャライザは値を返さない関数のような形式で定義
*func や関数名はかかず、init というキーワードを記述し、初期化のための手続きをコードブロックに記述する
*構造体のプロパティに自由にアクセス可能
*初期値のある定数の初期化はできないが、初期値がなければ一度だけ可能
*イニシャライザで構造体のメソッドを利用できるのは、全てのプロパティの初期化が完了してから(メソッドを利用してプロパティの初期値を設定することはできない)
*失敗可能イニシャライザは、init? で定義、失敗した場合、nilを返す
====複数のイニシャライザ====
----
*関数のオーバーロードのように、必要に応じて複数のイニシャライザを定義できる
*引数のないイニシャライザ、全項目のイニシャライザは便宜的にコードをかかなくても既定イニシャライザとして、利用できるが、特定のイニシャライザを作成した時点で利用できなくなるので、利用するには別途記述する必要が生じる
 
====メンバーワイズイニシャライザ====
----
*デフォルトで用意されているイニシャライザ
*型が持っているストアドプロパティと同名の引数をとるイニシャライザ
*ストアドプロパティが初期化式と同時に定義されている場合、デフォルト引数となり、呼び出し時に省略できる
<pre>
1> struct Foo {
22. var i: Int
23. var s: String = ""
24. }
25> let f = Foo(i:1, s:"a")
f: Foo = {
i = 1
s = "a"
}
26> let f = Foo(i:2)
f: Foo = {
i = 2
s = ""
}
</pre>
 
==クラス==
*self を通してインスタンスにアクセスできる
*大文字のSelfで、型自身にアクセスでき、スタティックメソッドなどへのアクセスが容易になる
 
===宣言===
----
<pre>
class クラス名: スーパークラス, プロトコル {
var 変数: 型 = 初期値
init(引数: 型) {
super.init(引数)
self.変数 = 引数
}
func 関数(引数: 型) -> 戻り値の型 {
return 戻り値
}
}
</pre>
*例
<pre>
class Person {
var name: String = ""
var age: Int = 0
init(name :String , age : Int) {
self.name = name
self.age = age
}
func addAge(num: Int) {
self.age += num
}
}
 
class Team {
let name: String = ""
var members: [Person] = []
func addMember(_ person: Person) {
self.members.append(person)
}
func printMembers(){
for member in members {
print("\(member.name) \(member.age)")
}
}
}
 
func test() {
let me = Person(name: "Yagi", age: 49)
me.addAge(num: 3)
print(me)
 
let team = Team()
team.addMember(me)
team.printMembers();
}
 
test()
</pre>
*結果
<pre>
$ swift classes.swift
classes.Person
Yagi 52
</pre>
===継承===
----
<pre>
class クラス名 : スーパークラス名 {
クラス定義
}
</pre>
====オーバーライド====
----
*オーバーライドを行うには、override キーワードを用いて、スーパークラスの要素を再定義する
*super でスーパークラスの実装を呼び出すことができる
*final を記述することで、オーバーライドを禁止できる
*final クラスにすることで、継承を禁止できる
<pre>
class クラス名 : スーパークラス名 {
override func メソッド名 ・・・
override var プロパティ名 ・・・
}
</pre>
 
===文字列表現===
----
<pre>
class SomeClass: CustomStringConvertible {
var description: String { return "This is SomeClass" }
}
</pre>
 
==列挙型==
<pre>
enum FoodChoice {
case cereal,salad,sandwich,pizza,chiken,pie
}
enum Meal {
case breakfast,lunch,dinner,snack
func foodChoices() -> [FoodChoice] {
switch self {
case .breakfast:
return [.cereal]
case .lunch:
return [.salad,.sandwich,.pizza]
case .dinner:
return [.sandwich, .pizza, .pie]
case .snack:
return [.cereal,.pie]
}
}
}
 
let meal: Meal = .lunch
print(meal.foodChoices())
結果:[__lldb_expr_29.FoodChoice.salad, __lldb_expr_29.FoodChoice.sandwich, __lldb_expr_29.FoodChoice.pizza]
</pre>
 
===連想値===
----
*列挙型では、どのケースかに加え付加情報として連想値を持つことができる
*連想値として持つ型には制限はない
<pre>
enum SomeError : Error {
case notfound(searchkey: String)
case invalidNum(number: Int)
}
 
let errReason1 = SomeError.notfound(searchkey: "username")
let errReason2 = SomeError.invalidNum(number: 5)
 
print("\(errReason1)")
print("\(errReason2)")
</pre>
*結果
<pre>
notfound(searchkey: "username")
invalidNum(number: 5)
</pre>
 
==関数==
===定義===
----
<pre>
func 関数名(引数名:型 = 初期値, ...) -> 戻り値型{
ステートメント
return 戻り値
}
</pre>
====引数====
*外部引数名 内部引数名 : 型 と宣言する
*外部引数名を省略したい場合は、 _ を指定する
*デフォルト引数を指定できる
 
<pre>
import Foundation
 
// 関数定義
func goodmorning(name: String) -> String {
return "Good Morning \(name)!"
}
 
// return の省略、引数ラベルの別名
func hello(n name: String) -> String { "Hello \(name)!!" }
 
// 引数ラベルの省略
func goodbye(_ name: String) -> String { "Good-bye \(name)!!"}
 
 
// 関数定義 仮引数、戻り値は省略できる
func test() {
print(goodmorning(name:"Yagi"))
print(hello(n:"Hiroto"))
print(goodbye("Hiroto Yagi"))
}
 
test()
</pre>
*実行結果
<pre>
$ swift main.swift
Good Morning Yagi!
Hello Hiroto!!
Good-bye Hiroto Yagi!!
</pre>
 
===inoutによる参照渡し===
----
*引数のを値を変更する場合、inoutを付与
*実引数を&を付与して渡す
<pre>
import Foundation
 
func funcIntou(n: inout Int, m: inout Int) {
let temp = n
n = m
m = temp
}
 
func test() {
var a = 111;
var b = 999;
print(a, b)
funcIntou(n:&a, m:&b)
print(a, b)
}
 
test()
</pre>
*実行結果
<pre>
$ swift func_inout.swift
111 999
999 111
</pre>
 
===ネストした関数===
----
*https://docs.swift.org/swift-book/LanguageGuide/Functions.html
 
*デフォルトでは外部から隠されている
*囲っている関数からは呼び出すことができる
*囲っている関数は、ネストした関数を外部で使用するために返すことができる。
<pre>
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
</pre>
===可変長引数===
----
*...を記述
<pre>
func shell(_ outputPipe:Pipe, path:String ,args: String...) -> Int32 {
}
</pre>
 
==プロパティ==
*構造体、列挙、クラスに定義可能
*var、letで定義
===格納型プロパティ===
*定数、変数
===計算型プロパティ===
*手続きで構成
<pre>
var プロパティ名:型 {
get {
プロパティの値を返す文
}
set(仮引数) {
プロパティの値を更新する文
}
}
</pre>
*例
<pre>
struct Tentimes {
var origin: Int
var ten: Int {
get {
return self.origin * 10
}
set(num) {
origin = num / 10
}
}
}
 
 
func test() {
var n = Tentimes(origin: 10)
print(n.ten)
n.ten = 10
print(n.origin)
}
 
test()
</pre>
*結果
<pre>
$ swift property.swift
100
1
</pre>
===クラスプロパティ/静的プロパティ===
*class/static を付与
*サブクラスで変更される可能性があるかで使い分ける
*継承先で変更される場合、クラスプロパティ
*継承先でも同じ値を返す場合、静的プロパティ
 
*クラスプロパティ
<pre>
class Hoge {
class var foo: String {
return "foo"
}
}
</pre>
 
===プロパティオブザーバー===
*プロパティの変更をトリガーに手続きを起動
*willSet:プロパティに格納される直前の値を、newValueで参照できる。willSet(仮引数) と具体的に引数を指定もできる
*didSet:プロパティに今まで格納されていた値を、oldValueで参照できる。didSet(仮引数) と具体的に引数を指定もできる
<pre>
struct PropertyObserver {
var val:Int = 0 {
willSet {
print("new value:\(newValue)");
}
didSet {
print("old value:\(oldValue)");
}
}
}
 
func test() {
var po = PropertyObserver()
po.val = 1;
print("\(po.val)")
po.val = 2;
print("\(po.val)")
}
 
test()
</pre>
*実行
<pre>
ew value:1
old value:0
1
new value:2
old value:1
2
</pre>
 
===添字付け===
<pre>
subscript(仮引数) -> 型 {
get {
添字で指定したプロパティを返す
}
set (仮引数) {
プロパティの値を変更する
}
}
</pre>
*実行
<pre>
struct Upper {
var items:[String]
init(count: Int) {
self.items = Array(repeating:"", count:count);
}
subscript(pos: Int) -> String {
get {
return self.items[pos]
}
set {
self.items[pos] = newValue.uppercased()
}
}
}
 
func test() {
var u = Upper(count:3)
u[0] = "a"
u[1] = "b"
u[2] = "c"
print("\(u[0])\(u[1])\(u[2])")
}
 
test()
</pre>
*結果
<pre>
$ swift subscript.swift
ABC
</pre>
===レイジープロパティ===
*アクセスされるまで初期化を遅延
<pre>
lazy var プロパティ名 : プロパティ型 = 式
</pre>
 
===KeyPath===
*https://qiita.com/imchino/items/67a987681bca5ad0408b
*「あるデータ型に定義されたプロパティまでの参照(パス)」です。
*キーパス式は、型のプロパティまたは添え字を参照する。
*キー・値監視などの動的プログラミングタスクでは、キーパス式を使用する。
<pre>
\<#type name#>.<#path#>
</pre>
 
==サブスクリプト==
*コレクションへの統一的なアクセス手法
<pre>
subscript(引数) -> 戻り値の型 {
get {
return 文によって値を返す処理
}
set {
値を更新する処理
}
}
</pre>
*例
<pre>
1> struct Hoge {
2. var children: [Int]
3. subscript(index: Int) -> Int {
4. get {
5. return children[index]
6. }
7. set {
8. children[index] = newValue
9. }
10. }
11. }
12>
13> var hoge = Hoge(children: [1,2,3])
14> print(hoge[2])
3
</pre>
 
==エクステンション==
*既に存在している型にプロパティやメソッド、イニシャライザなどの構成要素を追加できる
*ストアドプロパティは追加できないが、コンピューテッドプロパティは追加できる
<pre>
extension 定義する対象の型 {
対象に追加したい要素
}
</pre>
*
<pre>
1> extension String {
2. func repeats(times: Int) -> String {
3. var ret = ""
4. for i in 0...times {
5. ret += self
6. }
7. return ret
8. }
9. }
10> let s = "abc"
11> s.repeats(times:3)
$R0: String = "abcabcabcabc"
</pre>
==型のネスト==
*型の中に型を定義できる
<pre>
12> struct Foo {
13. enum Bar {
14. case a
15. case b
16. case c
17. }
18. }
19> let bar = Foo.Bar.b
20> bar
$R1: Foo.Bar = b
</pre>
 
==プロトコル==
*Swiftではインターフェースを定義するためのプロトコルをクラスだけでなく、構造体や列挙にも適用可能
*プロトコルには実装を共有できない問題があるが、'''拡張'''機能を活用することで問題を軽減できる
*本来関係ない型どうしをプロトコルと拡張を用いて後から連携するようにできる
*Swiftでは、プロトコル指向開発を進めることが可能
*プロトコルの機能を実装している場合、プロトコルに、適合(conform)、準拠しているという
 
===定義===
----
<pre>
public protocol Hogeable {
public var foo: String { get } // foo という名前でStringを返すプロパティ
}
</pre>
 
===採用===
----
*プロトコルを使用して、メソッドなどを実装することを、プロトコルを採用(adopt)するという
*構造体で採用
<pre>
struct 型名 : プロトコル名, プロトコル名, ・・・ {
} 
</pre>
===エクステンションによる準拠===
----
*エクステンションでプロトコルに準拠する事も可能
<pre>
extension エクステンションを定義する対象の型 : プロトコル名 {
プロトコルが要求する要素の定義
}
</pre>
 
===連想型===
----
*プロトコルの準拠時にプロパティ、引数、戻り値の型を指定できる
*連想型の実際の型は準拠する側で指定する
*1つの型に依存しないより抽象的なプロトコルを定義できる
<pre>
protocol プロトコル名 {
associated type 連装型名
var プロパティ名 : 連想型名
func メソッド名(引数名: 連想型名)
func メソッド名() -> 連想型名
}
</pre>
*例
<pre>
protocol Foo {
associatedtype Bar
func info(value: Bar)
}
struct Hoge : Foo {
func info(value: Int) {
print("\(value)")
}
}
 
let hoge = Hoge()
hoge.info(value:10)
</pre>
 
===同値、比較===
----
====Equatableプロトコル====
*==、!= により同値性を検証する
====Comparableプロトコル====
*値の大小比較を行う
 
===シーケンスとコレクション===
----
====Sequenceプロトコル====
----
*以下を提供
=====forEach(_:) : 順次アクセス=====
<pre>
1> let c = [1,2,3]
2> c.forEach({v in print(v)})
1
2
3
</pre>
 
=====filter(_:) : 要素の絞り込み=====
<pre>
1> let r = 0...8
2> r.filter({ v in v % 2 == 0 })
$R0: [ClosedRange<Int>.Element] = 5 values {
[0] = 0
[1] = 2
[2] = 4
[3] = 6
[4] = 8
}
</pre>
 
=====map(_:) : 要素を変換=====
=====flatMap(_:) : 要素をシーケンスに変換し1つのシーケンスに連結=====
<pre>
5> let n = [1,2]
6> n.flatMap({ v in [v , v * 2]})
$R2: [Int] = 4 values {
[0] = 1
[1] = 2
[2] = 2
[3] = 4
}
</pre>
 
=====cmpactMap(_:) : 要素を失敗する可能性のある処理を用いて変換=====
<pre>
3> let mix = ["a","1","2","d"]
4> mix.compactMap({ v in Int(v) })
$R1: [Int] = 2 values {
[0] = 1
[1] = 2
}
</pre>
 
=====reduce(_:) : 要素を一つの値にまとめる=====
<pre>
7> let r = [1,2,3]
8> r.reduce("", {result, element in result + "-" + String(element) })
$R3: String = "-1-2-3"
</pre>
=====カウントを取る =====
<pre>
let count = sequence.reduce(0) { acc, row in acc + 1 }
</pre>
 
====Collectionプロトコル====
----
*isEmptyプロパティ
*countプロパティ
*firstプロパティ
*lastプロパティ
 
====変換====
=====Array -> Set =====
<pre>
1> let ary: [Int] = [3,2,3,2]
ary: [Int] = 4 values {
[0] = 3
[1] = 2
[2] = 3
[3] = 2
}
2> print(Set(ary))
[3, 2]
</pre>
===== Set -> Array =====
<pre>
6> let set: Set<Int> = [3,4,5,6]
set: Set<Int> = 4 values {
[0] = 4
[1] = 6
[2] = 3
[3] = 5
}
7> print(Array(set))
[4, 6, 3, 5]
</pre>
 
==クロージャ==
*基本
*https://qiita.com/atsuastu_jr/items/e3a6990127d96c39a167
<pre>
{ 引数 in 戻り値を返す式 }
</pre>
 
*実行する文を記述する前に、'''in'''キーワードが必要
*仮引数には、inout、可変長引数も利用可能
<pre>
{
( 仮引数: 型 ) -> 型 in // 省略可
}
</pre>
*例(引数、戻り値なし)
<pre>
var hello = { () -> () in print("hello")}
hello()
</pre>
*結果
<pre>
hello
</pre>
===関数もクロージャの一つ===
----
*関数もクロージャとして扱える
*関数名を変数に代入
*引数も含めて識別できる
<pre>
1> func Multi(x:Int,y:Int) -> Int {
2. x * y
3. }
4>
5> let m = Multi
6> m(2,3)
$R0: Int = 6
</pre>
 
===キャプチャリスト===
----
*クロージャはローカル変数を共有する
*共有の必要がない場合、クロージャのインスタンス生成時にローカル変数の値をコピーする
<pre>
{
[キャプチャリスト]
( 仮引数: 型 ) -> 型 in // 省略可
}
</pre>
*例
<pre>
var message = "Start"
let c1 = { () -> () in print("\(message)") }
let c2 = { [message] () -> () in print("\(message)") }
message = "End"
c1()
c2()
</pre>
*結果
<pre>
End
Start
</pre>
===クロージャを引数にとる関数===
----
<pre>
// クロージャ(c1)を引数に取る
func hoge(_ s1:String, _ s2:String, _ c1:(String, String) -> String) {
print(c1(s1,s2))
}
 
// クロージャを引数に取る関数の呼び出し
func testHoge() throws {
hoge("hello", "swift", {(m1:String, m2:String) -> String in "\(m1),\(m2)!" })
}
</pre>
*結果
<pre>
hello,swift!
</pre>
 
===コールバックにクロージャを利用する例===
*非同期処理の結果をクロージャを用いて呼び出し元に返す
<pre>
static func accessRequest(completion:((String?) -> Void)?) {
let allTypes = Set([
HKQuantityType.quantityType(forIdentifier: .bodyMass)!,
/* HKQuantityType.quantityType(forIdentifier: .stepCount)!,*/
])
 
self.getHealthStore()?.requestAuthorization(toShare: allTypes, read: allTypes) { (success, error) in
if success {
completion?("アクセス許可: \(String(describing: success))")
} else {
completion?("アクセス不許可: \(String(describing: error?.localizedDescription))")
}
}
}
</pre>
 
===接尾(トレイリング)クロージャ===
----
*クロージャを関数の引数として使用する場合、末尾であれば特別な書き方をすることができる
*クロージャを()の外に出し、可読性を高めることができる
<pre>
// クロージャ(c1)を引数に取る
func hoge(_ s1:String, _ s2:String, _ c1:(String, String) -> String) {
print(c1(s1,s2))
}
 
// 接尾クロージャを使用した、クロージャを最後の引数として引数取る関数の呼び出し
func testHoge() throws {
// クロージャを()の外に出し、可読性を高めることができる
hoge("hello", "swift") { (m1, m2) in
"\(m1),\(m2)!"
}
}
</pre>
*結果
<pre>
hello,swift!
</pre>
 
===例===
* Message.user.name の重複を取り除き、","で連結して返す
<pre>
func joinAndUniqueUserNames(messages: [Message]) -> String {
var names:Set<String> = []
 
// クロージャ例
messages.forEach({message in names.insert(message.user.name)})
 
// トレイリングクロージャ例
messages.forEach { message in
names.insert(message.user.name)
}
return names.joined(separator: ",")
}
</pre>
 
===属性===
----
Swiftにおけるクロージャには、いくつかの属性を指定できます
====@escapeing属性====
----
https://qiita.com/imchino/items/48564b0c23a64f539060
*クロージャは、関数に引数として渡されたとき「関数をエスケープ」できます。
*エスケープしたクロージャは、関数に戻った後でも呼び出し可能です。
*エスケープクロージャのよくある例として、非同期処理をする完了ハンドラとしてのクロージャがあります。
*関数の実行が完了した後にクロージャを呼び出すためには、エスケープしておく必要があります。
<pre>
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
handler(nil)
}
</pre>
 
==ジェネリクス==
 
<pre>
func isEquals<T: Equatable> (_ x: T, _ y: T) -> Bool {
return x == y
}
print("\(isEquals("a", "b"))")
print("\(isEquals(1.0, 1.0))")
</pre>
===定義方法===
----
 
<pre>
func 関数名<型引数> (引数名 : 型引数) -> 戻り値型 {
関数呼び出し実行文
}
</pre>
*例
<pre>
func genericSample<T, U> (x: T, y: U) -> U {
let a : T = x
let b = y
print("\(a),\(b)")
return y
}
print(genericSample(x:1.2, y:"abc"))
</pre>
 
===some,any===
----
https://jimaru.blog/programming/swift/swift-some-any/
 
==並行処理==
*https://developer.apple.com/jp/news/?id=2o3euotz
*コアライブラリのFoundationでThreadクラスを提供
 
===[https://ticklecode.com/swfitgdp/ ディスパッチキュー]===
----
*UIスレッド
*[https://ticklecode.com/swfitgdp/ Swiftでの非同期処理GDP|ディスパッチキューの解説|DispatchQueue.globalとmain]
*[[イベントディスパッチスレッド]]
<pre>
Button(action: {
let rootDir = EncConverterService.chooseDir()
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
EncConverterService.loadFile(directoryPath: rootDir, filepaths: self.filePaths)
}
}) {
Text("Choose dir")
}
</pre>
====Grand Central Dispatch====
----
*GCDのキューはディスパッチキュー、以下の2種
**直列ディスパッチキュー(serial dispatch queue)
**並列ディスパッチキュー(concurrent dispatch queue)
*利用するには既存のディスパッチキューを取得するか新規に生成
*GCDは1つのメインキュート複数のグローバルキューを提供
*メインキューはメインスレッドでタスクを実行する直列ディスパッチキュー
*iOS/macOSでは、UIの更新は常にメインキューから行われる
*取得したディスパッチキューにタスクを追加するには、DispatchQueue.async(execute:) メソッドを用いる
*
<pre>
import Foundation
import Dispatch
 
let mq = DispatchQueue.main // メインディスパッチキューを取得
let gq = DispatchQueue.global(qos: .userInitiated) // グローバルキューを取得
let cq = DispatchQueue(
label: "info.typea.hoge.queue",
qos: .default,
attributes: [.concurrent]) // info.typea.hoge.queue という名前の並列ディスパッチキューを生成
</pre>
====Operation,OperationQueue====
----
*Operationは実行タスクと情報をカプセル化したものOperationQueueがキューの役割
 
====Mainスレッドで実行====
----
*UIスレッド
<pre>
DispatchQueue.main.async {
self.hogeLabel.text = result
}
</pre>
 
==ユニットテスト==
===非同期処理をテストする===
<pre>
func testAccessRequest() {
var result : String?
let expectation = expectation(description: "非同期待ち")
// 非同期処理の呼び出しとコールバック
HealthKitHelper.accessRequest() { accessResult in
result = accessResult
expectation.fulfill() // 非同期待ち解除
}
waitForExpectations(timeout: 4) // 指定時間応答がなければ失敗
 
print("ACCESS REQUEST : \(String(describing: result))")
assert(result != nil)
}
</pre>
 
==[[正規表現]]==
*https://www.hackingwithswift.com/example-code/strings/nsregularexpression-how-to-match-regular-expressions-in-strings
*https://www.2nd-walker.com/2020/04/21/swift-how-to-use-nsregularexpression/
<pre>
do {
let pattern = #"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"#
let regex = try NSRegularExpression(pattern: pattern, options:[])
for subinput in input.split(separator: "\r\n") {
let line = String(subinput)
let maches = regex.matches(in: line, options: [], range: _NSRange(0..<line.count))
print(line)
for mach in maches {
for i in 0 ..< mach.numberOfRanges {
let start = line.index(line.startIndex, offsetBy: mach.range(at: i).location)
let end = line.index(start, offsetBy: mach.range(at: i).length)
let text = String(line[start..<end])
print(text)
}
}
}
} catch {
print("RegEx fail.")
}
</pre>
 
===IsMatch===
----
*https://www.hackingwithswift.com/articles/108/how-to-use-regular-expressions-in-swift
 
<pre>
do {
let regex = try NSRegularExpression(pattern: MATCH_PATTERN, options:[])
return regex.firstMatch(in: stringValue, options: [], range: NSRange(location: 0, length: stringValue.utf16.count)) != nil
} catch {
print("\(error)");
}
</pre>
 
==[[SwiftUI]]==
*[[SwiftUI]]
===UIKitとのインターフェース===
*https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit
SwiftUIは既存のApplieプラットフォームUIフレームワークとシームレスに動作する。
 
==[[Mac]]OS==
*https://developer.apple.com/tutorials/swiftui/creating-a-macos-app
 
==Playground==
*[https://www.typea.info/blog/index.php/2020/12/06/xcode_macos_proguramming/ Playground]
===値の変化をグラフで見る===
[[File:Playground_view_result.png|600px]]
==SwiftUI==
*https://developer.apple.com/jp/xcode/swiftui/
*1セットのツールとAPIを使用するだけで、あらゆるAppleデバイス向けのユーザーインターフェイスを構築
*宣言型シンタックスを使
*宣言型のスタイルは、アニメーションなどの複雑な概念にも適用
===デザインツール===
*Xcodeには、SwiftUIでのインターフェイス構築をドラッグ&ドロップのように簡単に行える直感的な新しいデザインツールが含まれています
*デザインキャンバスでの編集内容と、隣接するエディタ内のコードはすべて完全に同期されます
====ドラッグ&ドロップ====
*ユーザーインターフェイス内のコンポーネントの位置は、キャンバス上でコントロールをドラッグするだけで調整できます
====ダイナミックリプレースメント====
*wiftのコンパイラとランタイムはXcode全体に完全に埋め込まれているため、Appは常にビルドされ実行されます
*表示されるデザインキャンバスは、単にユーザーインターフェイスに似せたものではなく、実際のAppそのもの
*Xcodeは編集したコードを実際のAppに直接組み入れることができます
====プレビュー====
*プレビューを1つまたは複数作成して、サンプルデータを取得できる
===チュートリアル===
*https://developer.apple.com/tutorials/swiftui/
*[https://www.typea.info/blog/index.php/2020/12/09/swiftui_tutorial_youtube_memo/ Canvas,TextView]
*[https://www.typea.info/blog/index.php/2020/12/19/swiftui_tutorial_custom_image_view/ Custom Image View]
 
==Objective-Cの動作モデルとセレクタ==
*Apple製品はObjective-Cの動作モデルに基づいている
*オブジェクトがプログラムを構成しオブジェクトが相互にメッセージを送信し合うことで動作する
*このモデルではメッセージの送り先について詳しく知る必要がないため、組み合わせの自由度が高い
*Objective-Cでは、メッセージ送信の際、'''セレクタ'''と呼ばれるデータを使ってメソッドを指定し動的な起動を可能としている
*SwiftではSelector型が提供されており、Selector型でメソッドを呼び出す仕組みは、Objective-CのNSObjectで定義されている
*Selector型は構造体だが、'''#selector'''という特殊なイニシャライザを使用する
 
==Swift Package Manager==
*[https://swift.org/package-manager/ Swift Package Manager]
 
==Storyboard==
===[https://www.typea.info/blog/index.php/2022/03/08/xamarin_mac_app_storyboard/ Storyboard]===
===[https://www.typea.info/blog/index.php/2022/03/11/nstableview_celbased/ Table View]===
===[https://www.typea.info/blog/index.php/2022/03/12/interfacebuilder_storyboard_tabview_screentransition/ Tab View画面遷移]===
 
==Tips==
====watchOS====
=====[https://www.typea.info/blog/index.php/2022/06/04/ios_watchos_dev_env/ watchOS を iOS プロジェクトに統合]=====
 
=====[https://www.typea.info/blog/index.php/2022/06/07/watchos_app_prestudy/ watchOS 開発概要]=====
----
[https://developer.apple.com/documentation/watchos-apps Developer Document]
 
=====[https://www.typea.info/blog/index.php/2022/06/08/swift_watchos_simple_app_connect_healthkit/ HealthKit利用 watchOS アプリ作成]=====
 
=====[https://www.typea.info/blog/index.php/2022/06/11/swift_watchos_complication_watchface_run_app/ コンプリケーションを文字盤に追加する]=====
 
=====[https://www.typea.info/blog/index.php/2022/06/14/swift_watchos_watchface_complication/ 文字盤に画像や情報を表示する]=====
 
=====[https://developer.apple.com/documentation/watchos-apps/setting-up-tests-for-your-watchos-app Unit Tests]=====
 
=====[https://qiita.com/am10/items/e58dfe28f024b3dc39ad WatchKitオブジェクト]=====
 
====HealthKit====
=====[https://www.typea.info/blog/index.php/2022/05/31/ios_swift_healthkit/ iOS SwiftにHealthKitを組み込む]=====
=====[https://www.typea.info/blog/index.php/2022/06/04/swift_ios_healthkit_healthdata/ ヘルスデータを読む]=====
=====[https://www.typea.info/blog/index.php/2022/06/09/swift_watchos_save_healthdata/ ヘルスデータを書き込む]=====
 
====チャート====
[https://www.typea.info/blog/index.php/2022/05/30/swift_ios_chart/ iOS Swiftにチャートを組み込む]
====[[Sqlite]]====
[https://www.typea.info/blog/index.php/2022/05/19/ios_swift_sqlite_via_typesafe_wapper/ iOS SwiftでSQLite使用する]
 
====ランダム====
<pre>
2> Int.random(in:1 ... 9999)
$R1: Int = 8549
</pre>
====Sum/Average====
<pre>
1> let l = [1,2,3,4,5]
l: [Int] = 5 values {
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
}
2> l.reduce(0,+)
$R0: Int = 15
3> l.reduce(0,+) / l.count
$R1: Int = 3
</pre>
 
====[[デザイン]]====
=====[https://photoshopvip.net/102903 配色]=====
*http://photoshopvip.net/95427
*https://colorhunt.co/palette/196224
 
=====アイコン=====
*https://www.flaticon.com
 
====書式====
=====カンマつき数値=====
<pre>
2> import Foundation
3> let nfmt = NumberFormatter()
4> nfmt.numberStyle = .decimal
5> print(nfmt.string(from: NSNumber(value:12345)))
Optional("12,345")
</pre>
 
=====小数点桁揃え=====
<pre>
print(String(format:"%.1f", (5.0 / 3.0)))
1.7
</pre>
 
=====桁揃え=====
<pre>
print(String(format:"%05d", 99))
00099
</pre>
 
===日付===
====書式====
<pre>
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter.string(from: day)
</pre>
 
====曜日判定====
<pre>
let calendar = Calendar(identifier: .gregorian)
return calendar.component(.weekday, from: date)
</pre>
====日付計算====
<pre>
3> let today = Date()
today: Foundation.Date = 2022-06-18 02:38:05 UTC
4> let tommorow = Calendar.current.date(byAdding: .day, value:1, to: today)
tommorow: Foundation.Date? = 2022-06-19 02:38:05 UTC
</pre>
*NSDateに変換
<pre>
2> let d = Calendar.current.date(byAdding: .month, value:1, to: Date()) as! NSDate
d: NSDate = 2022-07-03 15:38:43 UTC
</pre>
 
===[[JSON]]===
[[Swift Sample]]
 
===print改行させない===
----
<pre>
print(value, terminator: "")
</pre>
 
===ファイルを読む===
----
<pre>
do {
let data = try String(contentsOfFile: filePath, encoding: .shiftJIS)
let lines = data.components(separatedBy: .newlines)
for line in lines {
print(line)
}
} catch {
print("\(error)")
}
</pre>
 
===エンコードエラーがあってもShift-Jisファイルを読む===
----
<pre>
let bufsize = 1024
let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: bufsize)
defer {
buf.deallocate()
}
 
let reader = InputStream(fileAtPath: file.path)!
reader.open()
defer {
reader.close()
}
 
while reader.hasBytesAvailable {
let ret = reader.read(buf, maxLength: bufsize)
if ret == 0 {
break;
}
// shift-jis 1バイト目 0x81-0x9F , 0xE0-0xFC
let f1 = UInt8("81", radix: 16)!
let t1 = UInt8("9f", radix: 16)!
let f2 = UInt8("e0", radix: 16)!
let t2 = UInt8("fc", radix: 16)!
var bytes: [UInt8] = []
var isNext = false
for i in 0 ... bufsize {
let byte = buf[i]
if isNext {
bytes.append(byte)
isNext = false
} else {
if (f1 <= byte && byte <= t1) || (f2 <= byte && byte <= t2) {
bytes.append(byte)
isNext = true
continue
} else {
bytes.append(byte)
}
}
let s = String(bytes:bytes, encoding: .shiftJIS)
bytes.removeAll()
print(s ?? "??", terminator: "")
}
}
</pre>
 
===再起的にファイルパスを表示===
----
<pre>
static func printFiles(directoryPath: String) {
print(directoryPath)
do {
let fm = FileManager.default
let fileNames = try fm.contentsOfDirectory(atPath: directoryPath)
for fileName in fileNames {
let childPath = directoryPath + "/" + fileName
var isDir = ObjCBool(false)
fm.fileExists(atPath: childPath, isDirectory: &isDir)
if isDir.boolValue {
printFiles(directoryPath: childPath)
}
print("\t" + childPath)
}
} catch {
print(error)
}
}
</pre>
 
===UnitTest([[XCode]])===
----
[[File:swift_unittest_xcode.png|200px]]
[[File:swift_unittest_structure_xcode.png|200px]]
 
===Network===
----
====URLからデータを取得====
<pre>
let url = URL(string: "https://www.typea.info/blog/")!
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url){data, response, error in
guard error == nil else {
return
}
if let httpResponse = response as? HTTPURLResponse {
guard httpResponse.statusCode == 200 else {
return
}
if let recieveData = data {
if let text = String(bytes: recieveData, encoding: .utf8) {
print(text)
}
}
}
}
task.resume()
</pre>
 
====[[Bonjour]]====
----
*https://developer.apple.com/bonjour/
*[https://ja.wikipedia.org/wiki/Bonjour 概要]
*[http://www.asahi-net.or.jp/~cy5d-ks/colum013.html TCP/IP環境に革新もたらすZeroConfネットワーク「Bonjour」]
*[https://developer.apple.com/bonjour/ Bonjour]
*[https://github.com/ecnepsnai/BonjourSwift/blob/master/Bonjour.swift サンプル]
*[https://developer.apple.com/library/mac/qa/qa1312/_index.html サービスタイプ]
*[https://support.apple.com/ja-jp/guide/deployment-reference-ios/apd2b9e1ffaa/1/web/1.0 DNS Service Discoveryの仕組み]
*[https://help.dyn.com/bonjour-and-dns-discovery/ DNS Service Discovery]
*[https://docs.oracle.com/cd/E56342_01/html/E54076/dns-sd-1m.html dns-sd]
**macOSのdns-sdコマンドではDNSの逆引きに 相当することが出来ない
*[https://stackoverflow.com/questions/31835418/how-to-get-mac-address-from-os-x-with-swift How to get MAC address from OS X with Swift]
<pre>
$ dns-sd
dns-sd -E (Enumerate recommended registration domains)
dns-sd -F (Enumerate recommended browsing domains)
dns-sd -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)
dns-sd -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Register Proxy)
dns-sd -B <Type> <Domain> (Browse for service instances)
dns-sd -Z <Type> <Domain> (Output results in Zone File format)
dns-sd -L <Name> <Type> <Domain> (Resolve (‘lookup’) a service instance)
dns-sd -Q <name> <rrtype> <rrclass> (Generic query for any record type)
dns-sd -q <name> <rrtype> <rrclass> (Generic query, using SuppressUnusable)
dns-sd -G v4/v6/v4v6 <hostname> (Get address information for hostname)
dns-sd -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL> (NAT Port Mapping)
dns-sd -H (Print usage for complete command list)
dns-sd -V (Get version of currently running daemon / system service)
dns-sd -O [-compress|-stdout](Dump the state of mDNSResponder to file / STDOUT)
</pre>
<pre>
calcutta:~ hirotoyagi$ dns-sd -B
Browsing for _http._tcp
DATE: ---Wed 24 Feb 2021---
22:20:24.598 ...STARTING...
Timestamp A/R Flags if Domain Service Type Instance Name
22:20:24.599 Add 2 6 local. _http._tcp. Brother DCP-J973N
</pre>
 
<pre>
$ dns-sd -q puli.local
DATE: ---Sat 01 May 2021---
10:52:54.599 ...STARTING...
Timestamp A/R Flags if Name Type Class Rdata
10:52:54.771 Add 2 6 puli.local. Addr IN 192.168.0.45
</pre>
 
 
=====[https://serverfault.com/questions/143184/how-do-i-get-the-machine-name-from-an-ip-via-multicast-dns digで逆引き]=====
*@224.0.0.251 -p 5353 はBonjourのアドレス
<pre>
$ dig +short -x 192.168.0.45 @224.0.0.251 -p 5353
puli.local.
</pre>
 
====Error Domain=NSURLErrorDomain Code=-1022 の対処====
----
*info.plistのソース編集で以下を追加
<pre>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</pre>
====UDP====
----
*https://stackoverflow.com/questions/54640996/how-to-work-with-udp-sockets-in-ios-swift
====bind: Operation not permitted====
----
*https://github.com/httpswift/swifter/issues/292
<pre>
/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/bind9/bind9-57.5/bind9/lib/isc/unix/socket.c:5580: bind: Operation not permitted
/usr/bin/dig: isc_socket_bind: unexpected error
</pre>
[[File:swift_bind_permission_err.png|600px]]
 
 
[[category:プログラミング言語]]

案内メニュー