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

MyMemoWiki

Swift

提供: MyMemoWiki
ナビゲーションに移動 検索に移動

| Xcode | Mac | IPhone Xcode | SwiftUI | Swift Sample | Cocoa | リファクタリング |

目次

環境

Document


Swift言語ガイド

Swift言語ガイド

Hacking With Swift

Hacking With Swift

ライブラリ


標準ライブラリ


  • 言語の一部として基本機能を提供
  • インポートせずに常に利用可能

コアライブラリ


  • モジュールとしてインポートすることで利用可能となる
Foundation
  • 多くのアプリケーションに必要になる機能を提供
  • URL Loading
libdispatch
  • マルチコアハードウェア並列処理を抽象化
XCTest
  • ユニットテスト用

Mac Catalyst

Mac Catalystを使って構築したMac向けのネイティブAppは、iPad向けAppとコードを共有でき、Mac専用の機能を追加することもできます。macOS Montereyでは、最新のAPIを使用して、ウインドウのタイトルバーにポップアップボタン、ツールチップ、サブタイトルを表示することができます。また、タッチ操作の代替機能、キーボードで操作する機能、SiriのIntentのサポートを提供できるほか、Commandキーを押しながら「P」を押していつでもプリントできるようにするなど、さまざまな操作への対応が追加されました

Swift Package Manager


Swift Package Index

Ubuntuへインストール


  • ツールチェイン依存ライブラリのインストール
$ sudo apt-get update
$ sudo apt-get install clang libicu-dev
$ 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 
  • パスを通す(解凍されたディレクトリのusr/bin)
  • ~/.bashrc に追記
export PATH=/home/ubuntu/opt/swift-5.5.1-RELEASE-ubuntu20.04/usr/bin:"${PATH}"

Swift ubuntu.png

Xcode

ツールチェイン


  • ツールチェインとは、コンパイラやデバッガなど開発に必要なツールを一通りまとめたもの
  • SwiftのツールチェインはXcode に含まれる

開発ツール


Swift Package Manager


  • パッケージとはソースコードとマニフェストをまとめたもの
  • マニフェストファイルは、依存関係などをまとめたもの
  • Swift Package Manager はこれらをもとにビルドする
  • マニフェストに記述された外部パッケージはビルド時にダウンロードされる

LLDB


  • オープンソースデバッガ

Interface Builder


storyboard
NSTableView CellBased
NSTableView ViewBased
NSAlert ダイアログ

AutoLayout


AutoLayout概要
AutoLayout操作
AutoLayout簡易操作 TinyConstraint

画面遷移


子画面表示
segue(セグエ)で画面遷移

//呼び出し元
performSegue(withIdentifier: "some_segue_id", sender: nil)
Navigation Controller ナビゲーションバー
Tab Viewの利用と画面遷移

コマンドライン

  • swiftと入力
  • 終了には、:q もしくは、ctrl+D
  • パスが通ったところに配置されていれば実行できる
$ swift
Welcome to Apple Swift version 5.3.1 (swiftlang-1200.0.41 clang-1200.0.32.8).
Type :help for assistance.
  1> print("test")
test

コマンドラインツールを実行

  • アクティブ開発ディレクトリのコマンドラインツールを見つけて実行する
  • アクティブ開発ディレクトリは、xcode-select で指定する
  • アクティブ開発ディレクトリを表示
$ xcode-select -p
/Applications/Xcode.app/Contents/Developer
  • 以下のようにswiftコマンドを実行もできる
$ xcrun swift

プロジェクトの作成と実行

  • プロジェクトの作成
$ mkdir swift_demo
$ cd swift_demo
$ swift package init --type executable
  • ビルドして実行
$ swift run
[0/0] Build complete!
Hello, world!
  • .build/debug に実行ファイルが出力される
$ swift build
[0/0] Build complete!
$ .build/debug/swift_demo
Hello, world!

SDK

  • SDKを指定してswiftを起動するときに、指定できるSDK一覧を表示
$ xcodebuild -showsdks
iOS SDKs:
	iOS 14.2                      	-sdk iphoneos14.2

iOS Simulator SDKs:
	Simulator - iOS 14.2          	-sdk iphonesimulator14.2

macOS SDKs:
	DriverKit 20.0                	-sdk driverkit.macosx20.0
	macOS 11.0                    	-sdk macosx11.0
       :

SDKを指定して、swiftを起動

$ xcrun -sdk macosx11.0 swift

環境変数にSDKパスを指定

  • 以下でSDKのパスを取得し、SDKROOT環境変数に設定し、swiftを起動
$ xcrun -sdk macosx11.0 --show-sdk-path
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk

コメント

クイックヘルプ


    /// arp -a の結果をHostListに格納する
    ///
    /// - Parameters:
    ///     - hosts: 結果を格納する Host のリストを渡す
    /// - Throws:
    /// - Returns:

Swift quick help comment.png

アクセス制御

アクセス制御 意味
open モジュール情報をインポートすればどんなソースファイルからでもアクセス可能、サブクラスの作成や上書き定義も自由にできる
public モジュール情報をインポートすればどんなソースファイルからでもアクセス可能、サブクラスの作成や上書き定義はそのクラスを定義したモジュール内でのみ可能
internal 定義を含むソースファイルと同じモジュール内からはどのソースファイルからもアクセス可能。アクセス制御規定値
fileprivate 定義されたソースファイルの中だけで使用できる
private 定義単位の内部のみで利用できる

データ型

  • 型の実体はインスタンス
  • 値型と参照型

基本データ型

Any

  • 全ての型が暗黙的に準拠しているプロトコル
  • どのような型の値も代入できるため、値を代入する型が決まっていない場合に使用できる
  1> var a:Any = 123
a: Int = 123
  2> a = "123"
  3> a
$R0: String = "123"

Bool

  • true/false

Character

  • Unicodeの1文字
  • リテラルは、Stringと同様なので、アノテーションするなどが必要
  1> let s = "s"
s: String = "s"
  2> let c: Character = "c"
c: Character = "c"

String

  • 値型
  • == 比較、+ 結合
  • 高度な操作は、Foundationで提供される
  1> let r = "abc".compare("def") == ComparisonResult.orderedAscending
r: Bool = true

数値型

  • 変換にはイニシャライザを使用する
let d = 1.5
let i = Int64(d)
  • 演算は両辺の型が一致しないとエラーとなる
Int
UInt
Float
Double

型を調べる

1> print(type(of:"hello"))
String

変数の定義


  • 変数

var 変数名 : 型 = 式

var age : Int = 49
  • 定数(単一代入)

let 変数名 : 型 = 式

let age = 49

型変換


  • 暗黙の型変換は行われない
  • Double() や Int() などのイニシャライザを利用する
  1> var i:Int = 5
  2> var d:Double = 0;
  3> d = i
error: repl.swift:3:5: error: cannot assign value of type 'Int' to type 'Double'
  3> d = Double(i)
  4> d
$R0: Double = 5

型を確認する


var greeting = "Hello, playground"
type(of: greeting)

文字


文字列


  • 連結は +
  • リテラルは、"で囲む
  • \n 改行など\でエスケープ

書式

let frmt = String(format: "%03d", 1)

トリミング


let trimmed = " abc ".trimmingCharacters(in: .whitespaces)

文字列埋め込み


  • \(式) で展開される
  > var i:Int = 5
i: Int = 5
  > let s: String = "Number \(i * 3)"
s: String = "Number 15"

Raw文字列


  • 文字列の前に1つ以上の#記号を配置
let pattern = #"[0-9]{0,3}"#

Substring


  • Stringをスライスした場合など、Substringのインスタンスが結果として返される

ヒアドキュメント


  • 三重引用符で開始位置を揃える
        let magicpacket = service.createData(mac:"64:00:6a:20:EC:60")
        print("""
        MAGIC PACKET ======== FROM ============
        \(magicpacket)
        MAGIC PACKET ======== TO ============
        """)

エンコーディング

nil


  • 扱う値が存在しないことを表すため、nil が用意されている
  • Swiftでは基本的に、変数や定数にnilを許容しない
  • nil 値を持つことがある場合、Int? 型などで表す -> オプショナル型

オプショナル型 Optional<T>


  • パラメータ付型指定では、Optional<Int>
  • オプショナル型からデータを取り出 -> アンラップ
  • オプショナルをprintする場合、明示的にString(describing:)を使用する
var opi:Int?
print("\(String(describing:opi))")
opi = 1
var i:Int = opi!
print("\(i)")

  • 実行
nil
1
  • Optional<T>.none と nilが対応づけられている
  3> let n:String? = nil
n: String? = nil
  4> let n = Optional<String>.none
n: String? = nil

初期化


  • Optional<T> は T?で宣言できる : String?
  • Optional<T> を T! で宣言すると、使用時に自動的にアンラップされる : String!
  5> let i = Optional(1)
i: Int? = 1
  6> let j = Optional<Int>.some(2)
j: Int? = 2
  7> let k:Int? = 3
k: Int? = 3
  • Int?
 1 > let i:Int? = 1
 2 > let j = i + 1
error: repl.swift:33:9: error: value of optional type 'Int?' must be unwrapped to a value of type 'Int'
let j = i + 1
  • Int!
 1 > var i: Int! = 1
 2 > let j = i + 1
j: Int = 2

アンラップ


  • オプショナルバインディング
  • ??演算子
  • 強制アンラップ
オプショナルバインディング(if-let文)
  • オプショナルがnilでない場合、すぐに処理で使用したい場合
  • if や while の条件部にのみ記述できる
if let 定数名 = Optional<T>の値 {

}
let optNum: Int? = hoge()
if let num = optNum {
    // nil でない場合
} else {
    // nil の場合
}
  • 複数同時にも使用できる
let optNum1: Int? = hoge()
let optNum2: Int? = hoge()

if let num1 = optNum1, let num2 = optNum2 {
    // nil でない場合
} else {
    // nil の場合
}
??演算子
  • Optional<T>の値 ?? none の場合
  8> let i:Int? = 1
i: Int? = 1
  9> let j = i ?? 2
j: Int = 1
強制アンラップ
  • Optional<T>の値!
  • 強制的に取り出す。値がなければエラー
 10> let i = Optional<Int>.none
i: Int? = nil
 11> i!
__lldb_expr_24/repl.swift:11: Fatal error: Unexpectedly found nil while unwrapping an Optional value

オプショナルチェイン

  • Optional<T>の値?.メソッド  でアンラップせずにプロパティやメソッドにアクセスできる。値が存在しない場合、nil

map(_:),flatMap(_:) アンラップを行わずに値の変換を行う

  • クロージャを引数に渡す
  • flatMap はクロージャの戻り値もOptional
 1> let i: Int? = 1
i: Int? = 1
 2> i.map({ v in v + 1})
$R4: Int? = 2
1})
$R5: Int? = 1
 3> let j: Int? = nil
j: Int? = nil
 4> j.map({ v in v + 1})
$R6: Int? = nil

Data型

  • メモリ上のバイト列を管理
  • Foundationのimportが必要

Stringとの相互変換

let data: Data? = str.data(using: .utf8)
let str: String? = String(data: data, encoding: .utf8)

コレクション

  • 以下にはコレクションとしての共通機能がある
    • 配列 Array<Element>
    • 辞書 Dictionary<Key,Value>
    • 範囲 Range<Bound>
    • 文字列 String
  • 操作できるインターフェースについては、プロトコル参照

配列 Array<T>


  • T型の配列は、Array<T> もしくは、[T]
  • 順序を持ったコレクション
  • 型省略初期化
 > var ia = [1,2,3,4,5] 
  • 型宣言初期化
 > var ia2: [Int] = [1,2,3,4,5]
  • 要素0で初期化
  > var sa = [String]()
sa: [String] = 0 values
  • もしくは
  > var sa2: [String] = []
sa2: [String] = 0 values

参照と追加と削除

  1> var ia = [1,2,3]
ia: [Int] = 3 values {
  [0] = 1
  [1] = 2
  [2] = 3
}
  • 追加
  2> ia.append(4)
  3> ia
$R0: [Int] = 4 values {
  [0] = 1
  [1] = 2
  [2] = 3
  [3] = 4
}
  • 削除
  4> ia.remove(at:2)
$R1: Int = 3
  5> ia
$R2: [Int] = 3 values {
  [0] = 1
  [1] = 2
  [2] = 4
}
  • 追加
6> ia.insert(-1, at:2)
  7> ia
$R3: [Int] = 4 values {
  [0] = 1
  [1] = 2
  [2] = -1
  [3] = 4
}

スライス

  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

map関数

  • 元の配列内の全てのアイテムに対してアクションを行い結果に基づいた配列を作成
let base = [1,2,3]
let dbl = base.map {
    (val) -> Int in
    return val * 2
}
print(dbl)
結果:[2, 4, 6]
  • 省略形
let base = [1,2,3]
let dbl = base.map({ $0 * 2 })
print(dbl)
結果:[2, 4, 6]

filter関数

  • 元配列から条件に一致する配列を生成
let nums = [0,1,2,3,4,5,6]
print(nums.filter({ (num) -> Bool in
    return num % 2 == 0
}))
結果:[0, 2, 4, 6]
  • 省略形
let nums = [0,1,2,3,4,5,6]
print(nums.filter({ $0 % 2 == 0 }))

reduce関数

  • 配列内の全ての値を単一の結果に集約
let nums = [1,2,3]
let initial = 10;
print(nums.reduce(initial, { (curTotal, val) -> Int in
    return curTotal + val
}))
結果:16
  • 省略形
let nums = [1,2,3]
let initial = 10;
print(nums.reduce(initial,{ $0 + $1 }))
結果:16

タプル


(型名1, 型名2, 型名3, ・・・)
  • インデックスでアクセス
    let prof : (String, Double, Int) = ("Yagi", 173.0, 49)
    print("\(prof.0) \(prof.1) \(prof.2)")
  • 変数に代入
    let (name, tall, age) = prof
    print("name is \(name) \(tall) cm \(age) yo")
  • 名前付タプル
    let prof2 = (name:"Yagi",tall:173.0, age:49)
    print("name is \(prof2.name) \(prof2.tall) cm \(prof2.age) yo")
  • 結果
Yagi 173.0 49
name is Yagi 173.0 cm 49 yo
name is Yagi 173.0 cm 49 yo

辞書 Dictionary<Key, Value>


  • キーと値をもつコレクション
  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>  

削除

 3> sex.removeValue(forKey:"1") 
$R1: String? = "male"
  4> sex
$R2: [String : String] = 1 key/value pair {
  [0] = {
    key = "2"
    value = "female"
  }
}

空の辞書

var emptyDict: [String: String] = [:]

Set


var setOfStrings = Set<String>()
var fruitSet : Set = ["apple","orange","orange","banana"]

範囲 Range<Bound>


  • 末尾の値を含まない、..< もしくは含む ... により生成される

..<

  • Range<Bound>
  • CountableRange<Bound>
  • PartialRangeUpTo<Bound>
  1> let r = 0 ..< 10
r: Range<Int> = 0..<10

...

  • ClosedRange<Bound>
  • CountableClosedRange<Bound>
  • PartialRangeThrough<Bound>
  • ParatialRangeFrom<Bound>
  • CountablePartialRangeFrom<Bound>
  2> let s = "a" ... "z"
s: ClosedRange<String> = {
  lowerBound = "a"
  upperBound = "z"
}
  • 前置
  4> let t = ...1.95
t: PartialRangeThrough<Double> = {
  upperBound = 1.95
}
  • 後置
  5> let f = 1.95...
f: PartialRangeFrom<Double> = {
  lowerBound = 1.95
}
  • 範囲に含まれるか
  6> f.contains(3.0)
$R0: Bool = true

イテレーター

IteratorProtcol

let animals = ["Antelope", "Butterfly", "Camel", "Dolphin"]
var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
    print(animal)
}

演算子

  • C言語の演算子はほぼ使える
  • ポインタはないため、*、&、-> は異なった意味
  • インクリメント(++)、デクリメント(--) は廃止された

高度な演算


  • Foundation をインポートすることで、sin、logなど利用可能になる

型の判定


  • is
 11> let a:Any = "123"
a: String = "123"
 12> a is String
$R1: Bool = true

キャスト


式 as 型

アップキャスト(as)


  • as
  • コンパイル可能なアップキャストは常に成功
  • 暗黙も可
  1> let a: Any = "abc" as Any
a: String = "abc"
  2> let b: Any = "abc"
b: String = "abc"

ダウンキャスト(as? as!)


  • as? キャストが正しく行われた場合オプショナル型、不正な場合、nil
  • as! 強制キャスト失敗した場合、実行時エラー
  • コンパイルできても実行時に失敗する可能性あり
  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
     ^

独自演算子の定義


infix operator *** {
    associativity none
    precedence 130
}
func ***(lhs: Int, rhs: Int) -> Int {
    return lhs * rhs
}

モジュールと名前空間

モジュールのインポート

  • フレームワークや外部モジュールからクラスや関数情報をimportで取込む
  • Xcodeにある、*.swiftdoc や *.swiftmodule がSwiftが利用できるモジュール情報
import Foundation

名前空間

  • ドットで区切って修飾

制御構文

if

  • 条件の()は不要
  • 1文しかなくても{}は省略不可
  • 条件はBool型である必要がある
if 条件 {
} else if 条件 {
} else {
}

for

  • シーケンスプロトコルに準拠したオブジェクトを順次処理
for 変数 in シーケンス {
}

整数のレンジ

  • 開始値...終了値
for i in 0...10 {
    print(i)
}
  • 開始値..<終了値
for i in 0..<10 {
    print(i)
}
  • ステップでシーケンス作成(to値を含まない)
for num in stride(from: 0, to: 10, by: 2) {
    print(num)
}
//結果
0
2
4
6
8
  • ステップでシーケンス作成(through値を含む)
for num in stride(from: 0, through: 10, by: 2) {
    print(num)
}
//結果
0
2
4
6
8
10

for-in

  • where以下は省略可能
for 定数 in 式 where 式 {
}
for i in 0 ... 10 where i % 2 == 0 {
    print(i)
}

while

while 条件 {
}

repeat-while

  • 必ず一回は処理したい場合
repeat {
} while 条件

switch

  • break不要
  • 分岐に文字列や構造を持つ型も利用できる
  • 列挙/範囲指定できる
  • breakがなくてもcaseブロック終了でswitchを抜ける
  • break書いても良い
  • fall through は明示することで可能
  • 式がとりうる値を網羅していないとコンパイルエラー
switch 式 {
case ラベルn:
  文
default:
  文
}
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

タプルに適用

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")
}

ラベル

  • break はラベルのブロックを抜ける
  • continue はラベルの次繰り返しへ

ループ

loop1: while 式 {
  loop2: while 式 {
    while 式 {
      break loop1
      continue loop1
      break loop2
      continue loop2
    }
  }
}

if switch

cond1: if 式 {
  cond2: if 式 {
    if 式 {
      break cond2
    }
    break cond1
  }
}

do

エラー処理


Optional<Wrapped>型によるエラー処理


  • 値ありを成功、なしを失敗とみなす
  • エラーが発生する側は、戻り値の型をOptional<Wrapped>とするだけで良い
  • エラー処理側で、オプショナルチェイン、オプショナルバインディングなど簡潔に記述できる
  • 呼び出し元にエラーの詳細情報を提供できない

Result<Success,Failure>型によるエラー処理


  • 成功を結果の値、失敗を詳細で表す
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))
  • 結果
overflow
even
Success!! odd number & under ten
</pre<

====エラーを投げる====
----
*プロトコル Errorに適合している必要がある
<pre>
throw 式
  • エラーを投げる関数は直後に、throws宣言が必要

do-catchによるエラー処理


  • Swift標準のエラー処理機構
  • エラーを投げる関数を呼び出すには、関数名の前にtryを付与して呼び出す必要がある
  • throw分のエラーを表現するには、エラーであることを表す、Errorプロトコルに準拠する必要がある
  • Errorプロトコルに準拠させる場合、列挙を使用するのが一般的
tryを使って呼び出すだけ

さらに上位に伝播

do-catch

捕捉

try?
  • 戻り値をオプショナルとして扱う。エラーが発生した場合値が nil となる
  • 「エラーは起きるかもしれない。でも起きたとしても無視する」
try!

戻り値を通常型として扱う。エラー発生時は実行エラー

パターン
  • 定数または変数のマッチング
  • パターンを記述しないcatchも可能。発生したエラーは、errorで参照できる
do {
    try 関数
} catch パターン where節 {
   文
} catch {
  文
}
    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()
  • 結果
para is 1.
Parameter is nil.
OVERFLOW

throwsキーワード


  • 関数、イニシャライザ、クロージャの定義にthrowsを追加すると、それらの中で、do-catchを用いずにthrow文によるエラーを発生させることができる

guard文


  • 想定外の状況が発生した場合に、その状況から抜け出す(早期退出するガード節)
  • 条件には、通常の条件やオプショナル束縛などを記述できる
  • if - let 同様、guard - let が利用できる。ifと異なり、ブロック外でも利用可能
guard 条件 else { 
    /* break や return、throw など制御が戻らないことが自明な文  */ 
}
 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歳

遅延実行


  • 記述されている箇所より後で実行する

defer文


  • スコープを抜ける際に実行したい処理を定義する。
  • 複数のdefer文を記述した場合は逆順で実行される。

パターンマッチ


  • 値の持つ構造や性質を表現するパターンのマッチングによって制御を行うことができる
  • if,guard,for-in,while,doのcatchでも case キーワードを使えば利用できる
if case パターン = 制御式 {}
guard case パターン = 制御式 else {}
  • 一致したもののみ処理される
for case パターン in シーケンス {}
while case パターン = 制御式 {}

式パターン

  • ~= により評価
  • ~= は型別に定義されていて、範囲型では、contains(_:)メソッドで評価される
 19> let n = 3
 20> let r = 1...3
 21> r ~= n
$R4: Bool = true

バリューバインディングパターン

  • 値を変数や定数に代入する
  • var、let と他のパターンを組み合わせマッチしたら代入
  1> let point = (3, 2) 
  2> switch point { 
  3. case let (x, y): 
  4.     print("\(x),\(y)") 
  5. } 
3,2

オプショナルパターン

  • Optional<T>の値有無を評価
  6> let o: Int? = 42
  7> if case .some(let x) = o { 
  8.     print(x) 
  9. } 
42

列挙型ケースパターン

  • 列挙型ケースとの一致
  • 列挙型参照

is/as演算子によるキャスティングパターン

  • is による判定
 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
  • as によるダウンキャスト
 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

構造体

  • structは値型
  • プロトコルは、実装すべきメソッドやプロパティが定まっている
  • 構造体の値を変更するメソッドは先頭に mutating を付与
struct  名前 : プロトコル{
  変数・定数定義
  イニシャライザ定義
  メソッド定義
  その他定義
}
  • init() イニシャライザ

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()
  • 実行
$ swift struct.swift 
Person(name: "Yagi", age: 52)
Yagi 52

イニシャライザ


  • カスタムイニシャライザは値を返さない関数のような形式で定義
  • func や関数名はかかず、init というキーワードを記述し、初期化のための手続きをコードブロックに記述する
  • 構造体のプロパティに自由にアクセス可能
  • 初期値のある定数の初期化はできないが、初期値がなければ一度だけ可能
  • イニシャライザで構造体のメソッドを利用できるのは、全てのプロパティの初期化が完了してから(メソッドを利用してプロパティの初期値を設定することはできない)
  • 失敗可能イニシャライザは、init? で定義、失敗した場合、nilを返す

複数のイニシャライザ


  • 関数のオーバーロードのように、必要に応じて複数のイニシャライザを定義できる
  • 引数のないイニシャライザ、全項目のイニシャライザは便宜的にコードをかかなくても既定イニシャライザとして、利用できるが、特定のイニシャライザを作成した時点で利用できなくなるので、利用するには別途記述する必要が生じる

メンバーワイズイニシャライザ


  • デフォルトで用意されているイニシャライザ
  • 型が持っているストアドプロパティと同名の引数をとるイニシャライザ
  • ストアドプロパティが初期化式と同時に定義されている場合、デフォルト引数となり、呼び出し時に省略できる
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 = ""
}

クラス

  • self を通してインスタンスにアクセスできる
  • 大文字のSelfで、型自身にアクセスでき、スタティックメソッドなどへのアクセスが容易になる

宣言


class クラス名: スーパークラス, プロトコル {
    var 変数: 型 = 初期値
    init(引数: 型) {
        super.init(引数)
        self.変数 = 引数
    }
    func 関数(引数: 型) -> 戻り値の型 {
        return 戻り値
    }
}
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()
  • 結果
$ swift classes.swift 
classes.Person
Yagi 52

継承


class クラス名 : スーパークラス名 {
    クラス定義
}

オーバーライド


  • オーバーライドを行うには、override キーワードを用いて、スーパークラスの要素を再定義する
  • super でスーパークラスの実装を呼び出すことができる
  • final を記述することで、オーバーライドを禁止できる
  • final クラスにすることで、継承を禁止できる
class クラス名 : スーパークラス名 {
    override func メソッド名 ・・・
    override var プロパティ名 ・・・
}

文字列表現


class SomeClass: CustomStringConvertible {
    var description: String { return "This is SomeClass" }
}

列挙型

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]

連想値


  • 列挙型では、どのケースかに加え付加情報として連想値を持つことができる
  • 連想値として持つ型には制限はない
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)")
  • 結果
notfound(searchkey: "username")
invalidNum(number: 5)

関数

定義


func 関数名(引数名:型 = 初期値, ...) -> 戻り値型{
    ステートメント
    return 戻り値
}

引数

  • 外部引数名 内部引数名 : 型 と宣言する
  • 外部引数名を省略したい場合は、 _ を指定する
  • デフォルト引数を指定できる
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()
  • 実行結果
$ swift main.swift
Good Morning Yagi!
Hello Hiroto!!
Good-bye Hiroto Yagi!!

inoutによる参照渡し


  • 引数のを値を変更する場合、inoutを付与
  • 実引数を&を付与して渡す
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()
  • 実行結果
$ swift func_inout.swift 
111 999
999 111

ネストした関数


  • デフォルトでは外部から隠されている
  • 囲っている関数からは呼び出すことができる
  • 囲っている関数は、ネストした関数を外部で使用するために返すことができる。
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!

可変長引数


  • ...を記述
func shell(_ outputPipe:Pipe, path:String ,args: String...) -> Int32 {
}

プロパティ

  • 構造体、列挙、クラスに定義可能
  • var、letで定義

格納型プロパティ

  • 定数、変数

計算型プロパティ

  • 手続きで構成
var プロパティ名:型 {
    get {
         プロパティの値を返す文
    }
    set(仮引数) {
         プロパティの値を更新する文
    }    
}
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()
  • 結果
$ swift property.swift 
100
1

クラスプロパティ/静的プロパティ

  • class/static を付与
  • サブクラスで変更される可能性があるかで使い分ける
  • 継承先で変更される場合、クラスプロパティ
  • 継承先でも同じ値を返す場合、静的プロパティ
  • クラスプロパティ
class Hoge {
    class var foo: String {
         return "foo"
    }
}

プロパティオブザーバー

  • プロパティの変更をトリガーに手続きを起動
  • willSet:プロパティに格納される直前の値を、newValueで参照できる。willSet(仮引数) と具体的に引数を指定もできる
  • didSet:プロパティに今まで格納されていた値を、oldValueで参照できる。didSet(仮引数) と具体的に引数を指定もできる
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()
  • 実行
ew value:1
old value:0
1
new value:2
old value:1
2

添字付け

subscript(仮引数)  -> 型 {
    get {
         添字で指定したプロパティを返す
    }
    set (仮引数) {
         プロパティの値を変更する
    }
}
  • 実行
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()
  • 結果
$ swift subscript.swift 
ABC

レイジープロパティ

  • アクセスされるまで初期化を遅延
lazy var プロパティ名 : プロパティ型 = 式

KeyPath

  • https://qiita.com/imchino/items/67a987681bca5ad0408b
  • 「あるデータ型に定義されたプロパティまでの参照(パス)」です。
  • キーパス式は、型のプロパティまたは添え字を参照する。
  • キー・値監視などの動的プログラミングタスクでは、キーパス式を使用する。
\<#type name#>.<#path#>

サブスクリプト

  • コレクションへの統一的なアクセス手法
subscript(引数) -> 戻り値の型 {
  get {
    return 文によって値を返す処理
  }
  set {
    値を更新する処理
  }
}
  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

エクステンション

  • 既に存在している型にプロパティやメソッド、イニシャライザなどの構成要素を追加できる
  • ストアドプロパティは追加できないが、コンピューテッドプロパティは追加できる
extension 定義する対象の型 {
  対象に追加したい要素
}
  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"

型のネスト

  • 型の中に型を定義できる
 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

プロトコル

  • Swiftではインターフェースを定義するためのプロトコルをクラスだけでなく、構造体や列挙にも適用可能
  • プロトコルには実装を共有できない問題があるが、拡張機能を活用することで問題を軽減できる
  • 本来関係ない型どうしをプロトコルと拡張を用いて後から連携するようにできる
  • Swiftでは、プロトコル指向開発を進めることが可能
  • プロトコルの機能を実装している場合、プロトコルに、適合(conform)、準拠しているという

定義


public protocol Hogeable {
    public var foo: String { get }  // foo という名前でStringを返すプロパティ
}

採用


  • プロトコルを使用して、メソッドなどを実装することを、プロトコルを採用(adopt)するという
  • 構造体で採用
struct 型名 :  プロトコル名, プロトコル名, ・・・ {
  
} 

エクステンションによる準拠


  • エクステンションでプロトコルに準拠する事も可能
extension エクステンションを定義する対象の型 : プロトコル名 {
    プロトコルが要求する要素の定義
}

連想型


  • プロトコルの準拠時にプロパティ、引数、戻り値の型を指定できる
  • 連想型の実際の型は準拠する側で指定する
  • 1つの型に依存しないより抽象的なプロトコルを定義できる
protocol プロトコル名 {
    associated type 連装型名
    
    var プロパティ名 : 連想型名
    func メソッド名(引数名: 連想型名)
    func メソッド名() -> 連想型名
}
protocol Foo {
    associatedtype Bar
   
    func info(value: Bar) 
}
struct Hoge : Foo {
    func info(value: Int) {
        print("\(value)")
    }
}

let hoge = Hoge()
hoge.info(value:10)

同値、比較


Equatableプロトコル

  • ==、!= により同値性を検証する

Comparableプロトコル

  • 値の大小比較を行う

シーケンスとコレクション


Sequenceプロトコル


  • 以下を提供
forEach(_:) : 順次アクセス
  1> let c = [1,2,3]
  2> c.forEach({v in print(v)})
1
2
3
filter(_:) : 要素の絞り込み
  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
}
map(_:) : 要素を変換
flatMap(_:) : 要素をシーケンスに変換し1つのシーケンスに連結
  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
}
cmpactMap(_:) : 要素を失敗する可能性のある処理を用いて変換
  3> let mix = ["a","1","2","d"]
  4> mix.compactMap({ v in Int(v) })
$R1: [Int] = 2 values {
  [0] = 1
  [1] = 2
}
reduce(_:) : 要素を一つの値にまとめる
  7> let r = [1,2,3]
  8> r.reduce("", {result, element in result + "-" + String(element) })
$R3: String = "-1-2-3"
カウントを取る 
let count = sequence.reduce(0) { acc, row in acc + 1 }

Collectionプロトコル


  • isEmptyプロパティ
  • countプロパティ
  • firstプロパティ
  • lastプロパティ

変換

Array -> Set
  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]
Set -> Array
  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]

クロージャ

{ 引数 in 戻り値を返す式 }
  • 実行する文を記述する前に、inキーワードが必要
  • 仮引数には、inout、可変長引数も利用可能
{
    ( 仮引数: 型 ) -> 型 in   // 省略可
     文
}
  • 例(引数、戻り値なし)
var hello = { () -> () in print("hello")}
hello()
  • 結果
hello

関数もクロージャの一つ


  • 関数もクロージャとして扱える
  • 関数名を変数に代入
  • 引数も含めて識別できる
  1> func Multi(x:Int,y:Int) -> Int { 
  2.     x * y 
  3. } 
  4>  
  5> let m = Multi
  6> m(2,3)
$R0: Int = 6

キャプチャリスト


  • クロージャはローカル変数を共有する
  • 共有の必要がない場合、クロージャのインスタンス生成時にローカル変数の値をコピーする
{
    [キャプチャリスト]
    ( 仮引数: 型 ) -> 型 in   // 省略可
     文
}
        var message = "Start"
        let c1 = { () -> () in print("\(message)") }
        let c2 = { [message] () -> () in print("\(message)") }
        message = "End"
        c1()
        c2()
  • 結果
End
Start

クロージャを引数にとる関数


    // クロージャ(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)!" })
    }
  • 結果
hello,swift!

コールバックにクロージャを利用する例

  • 非同期処理の結果をクロージャを用いて呼び出し元に返す
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))")
            }
        }
    }

接尾(トレイリング)クロージャ


  • クロージャを関数の引数として使用する場合、末尾であれば特別な書き方をすることができる
  • クロージャを()の外に出し、可読性を高めることができる
    // クロージャ(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)!"
        }
    }
  • 結果
hello,swift!

  •  Message.user.name の重複を取り除き、","で連結して返す
    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: ",")
    }

属性


Swiftにおけるクロージャには、いくつかの属性を指定できます

@escapeing属性


https://qiita.com/imchino/items/48564b0c23a64f539060

  • クロージャは、関数に引数として渡されたとき「関数をエスケープ」できます。
  • エスケープしたクロージャは、関数に戻った後でも呼び出し可能です。
  • エスケープクロージャのよくある例として、非同期処理をする完了ハンドラとしてのクロージャがあります。
  • 関数の実行が完了した後にクロージャを呼び出すためには、エスケープしておく必要があります。
    func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
        // Call the handler with the current timeline entry
        
        
        handler(nil)
    }

ジェネリクス

func isEquals<T: Equatable> (_ x: T, _ y: T) -> Bool {
    return x == y
}
print("\(isEquals("a", "b"))")
print("\(isEquals(1.0, 1.0))")

定義方法


func 関数名<型引数> (引数名 : 型引数) -> 戻り値型 {
    関数呼び出し実行文
}
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"))

some,any


https://jimaru.blog/programming/swift/swift-some-any/

並行処理

ディスパッチキュー


        Button(action: {
            let rootDir = EncConverterService.chooseDir()
            let queue = DispatchQueue.global(qos: .userInitiated)
            queue.async {
                EncConverterService.loadFile(directoryPath: rootDir, filepaths: self.filePaths)
            }
        }) {
            Text("Choose dir")  
        }

Grand Central Dispatch


  • GCDのキューはディスパッチキュー、以下の2種
    • 直列ディスパッチキュー(serial dispatch queue)
    • 並列ディスパッチキュー(concurrent dispatch queue)
  • 利用するには既存のディスパッチキューを取得するか新規に生成
  • GCDは1つのメインキュート複数のグローバルキューを提供
  • メインキューはメインスレッドでタスクを実行する直列ディスパッチキュー
  • iOS/macOSでは、UIの更新は常にメインキューから行われる
  • 取得したディスパッチキューにタスクを追加するには、DispatchQueue.async(execute:) メソッドを用いる
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 という名前の並列ディスパッチキューを生成

Operation,OperationQueue


  • Operationは実行タスクと情報をカプセル化したものOperationQueueがキューの役割

Mainスレッドで実行


  • UIスレッド
            DispatchQueue.main.async {
                self.hogeLabel.text = result
            }

ユニットテスト

非同期処理をテストする

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)
    }

正規表現

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.")
}

IsMatch


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)");
}

SwiftUI

UIKitとのインターフェース

SwiftUIは既存のApplieプラットフォームUIフレームワークとシームレスに動作する。

MacOS

Playground

値の変化をグラフで見る

Playground view result.png

チュートリアル

Objective-Cの動作モデルとセレクタ

  • Apple製品はObjective-Cの動作モデルに基づいている
  • オブジェクトがプログラムを構成しオブジェクトが相互にメッセージを送信し合うことで動作する
  • このモデルではメッセージの送り先について詳しく知る必要がないため、組み合わせの自由度が高い
  • Objective-Cでは、メッセージ送信の際、セレクタと呼ばれるデータを使ってメソッドを指定し動的な起動を可能としている
  • SwiftではSelector型が提供されており、Selector型でメソッドを呼び出す仕組みは、Objective-CのNSObjectで定義されている
  • Selector型は構造体だが、#selectorという特殊なイニシャライザを使用する

Swift Package Manager

Storyboard

Storyboard

Table View

Tab View画面遷移

Tips

watchOS

watchOS を iOS プロジェクトに統合
watchOS 開発概要

Developer Document

HealthKit利用 watchOS アプリ作成
コンプリケーションを文字盤に追加する
文字盤に画像や情報を表示する
Unit Tests
WatchKitオブジェクト

HealthKit

iOS SwiftにHealthKitを組み込む
ヘルスデータを読む
ヘルスデータを書き込む

チャート

iOS Swiftにチャートを組み込む

Sqlite

iOS SwiftでSQLite使用する

ランダム

  2> Int.random(in:1 ... 9999)
$R1: Int = 8549

Sum/Average

  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

デザイン

配色
アイコン

書式

カンマつき数値
  2> import Foundation
  3> let nfmt = NumberFormatter()
  4> nfmt.numberStyle = .decimal
  5> print(nfmt.string(from: NSNumber(value:12345)))
Optional("12,345")
小数点桁揃え
print(String(format:"%.1f", (5.0 / 3.0))) 
1.7
桁揃え
print(String(format:"%05d", 99)) 
00099

日付

書式

        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter.string(from: day)

曜日判定

        let calendar = Calendar(identifier: .gregorian)
        return calendar.component(.weekday, from: date)

日付計算

  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
  • NSDateに変換
2> let d = Calendar.current.date(byAdding: .month, value:1, to: Date()) as! NSDate 
d: NSDate = 2022-07-03 15:38:43 UTC

JSON

Swift Sample

print改行させない


print(value, terminator: "")

ファイルを読む


do {
	let data = try String(contentsOfFile: filePath, encoding: .shiftJIS)
	let lines = data.components(separatedBy: .newlines)
	for line in lines {
		print(line)
	}
} catch {
	print("\(error)")
}

エンコードエラーがあってもShift-Jisファイルを読む


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: "")
	}
}

再起的にファイルパスを表示


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)
	}
}

UnitTest(XCode)


Swift unittest xcode.png Swift unittest structure xcode.png

Network


URLからデータを取得

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()

Bonjour


$ 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)
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
$ 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


digで逆引き
  • @224.0.0.251 -p 5353 はBonjourのアドレス
$ dig +short -x 192.168.0.45 @224.0.0.251 -p 5353
puli.local.

Error Domain=NSURLErrorDomain Code=-1022 の対処


  • info.plistのソース編集で以下を追加
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

UDP


bind: Operation not permitted


/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

Swift bind permission err.png