Swift
| Xcode | Mac | IPhone Xcode | SwiftUI | Swift Sample | リファクタリング |
目次
- 1 環境
- 2 Xcode
- 3 コマンドライン
- 4 コメント
- 5 アクセス制御
- 6 データ型
- 7 コレクション
- 8 演算子
- 9 モジュールと名前空間
- 10 制御構文
- 11 構造体
- 12 クラス
- 13 列挙型
- 14 関数
- 15 プロパティ
- 16 プロトコル
- 17 クロージャ
- 18 並行処理
- 19 正規表現
- 20 SwiftUI
- 21 MacOS
- 22 Playground
- 23 Objective-Cの動作モデルとセレクタ
- 24 Swift Package Manager
- 25 Tips
環境
Document
Swift言語ガイド
Hacking With Swift
ライブラリ
標準ライブラリ
- 言語の一部として基本機能を提供
- インポートせずに常に利用可能
コアライブラリ
- モジュールとしてインポートすることで利用可能となる
Foundation
- 多くのアプリケーションに必要になる機能を提供
- URL Loading
libdispatch
- マルチコアハードウェア並列処理を抽象化
XCTest
- ユニットテスト用
Ubuntuへインストール
- ツールチェイン依存ライブラリのインストール
- $ sudo apt-get update
- $ sudo apt-get install clang libicu-dev
- ツールチェインのダウンロードと解凍
- https://www.swift.org/download/
- $ 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}"
Xcode
ツールチェイン
- ツールチェインとは、コンパイラやデバッガなど開発に必要なツールを一通りまとめたもの
- SwiftのツールチェインはXcode に含まれる
開発ツール
Swift Package Manager
- パッケージとはソースコードとマニフェストをまとめたもの
- マニフェストファイルは、依存関係などをまとめたもの
- Swift Package Manager はこれらをもとにビルドする
- マニフェストに記述された外部パッケージはビルド時にダウンロードされる
LLDB
- オープンソースデバッガ
コマンドライン
- 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:
アクセス制御
アクセス制御 | 意味 |
---|---|
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
変数の定義
- 変数
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 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?で宣言できる
- Optional<T> を T! で宣言すると、使用時に自動的にアンラップされる
- 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が必要
コレクション
- 以下にはコレクションとしての共通機能がある
- 配列 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
- }
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"
- }
- }
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
演算子
- C言語の演算子はほぼ使える
- ポインタはないため、*、&、-> は異なった意味
- インクリメント(++)、デクリメント(--) は廃止された
高度な演算
- Foundation をインポートすることで、sin、logなど利用可能になる
型の判定
- is
- 11> let a:Any = "123"
- a: String = "123"
- 12> a is String
- $R1: Bool = true
キャスト
- 式 as 型
アップキャスト
- as
- コンパイル可能なアップキャストは常に成功
- 暗黙も可
- 1> let a: Any = "abc" as Any
- a: String = "abc"
- 2> let b: Any = "abc"
- b: String = "abc"
ダウンキャスト
- 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
- ^
モジュールと名前空間
モジュールのインポート
- フレームワークや外部モジュールからクラスや関数情報を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
エラー処理
エラーを投げる
- プロトコル Errorに適合している必要がある
- throw 式
- エラーを投げる関数は直後に、throws宣言が必要
エラーハンドリング
- エラーを投げる関数を呼び出すには、関数名の前にtryを付与して呼び出す必要がある
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
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文を記述した場合は逆順で実行される。
パターンマッチ
- 値の持つ構造や性質を表現するパターンのマッチングによって制御を行うことができる
式パターン
- ~= により評価
- ~= は型別に定義されていて、範囲型では、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
構造体
- 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 というキーワードを記述し、初期化のための手続きをコードブロックに記述する
- 構造体のプロパティに自由にアクセス可能
- 初期値のある定数の初期化はできないが、初期値がなければ一度だけ可能
- イニシャライザで構造体のメソッドを利用できるのは、全てのプロパティの初期化が完了してから(メソッドを利用してプロパティの初期値を設定することはできない)
複数のイニシャライザ
- 関数のオーバーロードのように、必要に応じて複数のイニシャライザを定義できる
- 引数のないイニシャライザ、全項目のイニシャライザは便宜的にコードをかかなくても既定イニシャライザとして、利用できるが、特定のイニシャライザを作成した時点で利用できなくなるので、利用するには別途記述する必要が生じる
クラス
宣言
- 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 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]
関数
定義
- 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 プロパティ名:型 {
- 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
タイププロパティ/静的プロパティ
- 型に関連
- static を付与
プロパティオブザーバー
- プロパティの変更をトリガーに手続きを起動
- 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
プロトコル
- Swiftではインターフェースを定義するためのプロトコルをクラスだけでなく、構造体や列挙にも適用可能
- プロトコルには実装を共有できない問題があるが、拡張機能を活用することで問題を軽減できる
- 本来関係ない型どうしをプロトコルと拡張を用いて後から連携するようにできる
- Swiftでは、プロトコル指向開発を進めることが可能
- プロトコルの機能を実装している場合、プロトコルに、適合(conform)、準拠しているという
定義
- public protocol Hogeable {
- public var foo: String { get } // foo という名前でStringを返すプロパティ
- }
採用
- プロトコルを使用して、メソッドなどを実装することを、プロトコルを採用(adopt)するという
- 構造体で採用
- struct 型名 : プロトコル名, プロトコル名, ・・・ {
- }
同値、比較
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"
Collectionプロトコル
- isEmptyプロパティ
- countプロパティ
- firstプロパティ
- lastプロパティ
クロージャ
- 基本
- { 引数 in 戻り値を返す式 }
- 実行する文を記述する前に、inキーワードが必要
- 仮引数には、inout、可変長引数も利用可能
- {
- ( 仮引数: 型 ) -> 型 in // 省略可
- 文
- }
- 例(引数、戻り値なし)
- var hello = { () -> () in print("hello")}
- hello()
- 結果
- hello
キャプチャリスト
- クロージャはローカル変数を共有する
- 共有の必要がない場合、クロージャのインスタンス生成時にローカル変数の値をコピーする
- {
- [キャプチャリスト]
- ( 仮引数: 型 ) -> 型 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!
接尾(トレイリング)クロージャ
- クロージャを関数の引数として使用する場合、末尾であれば特別な書き方をすることができる
- クロージャを()の外に出し、可読性を高めることができる
- // クロージャ(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!
並行処理
ディスパッチキュー
- Button(action: {
- let rootDir = EncConverterService.chooseDir()
- let queue = DispatchQueue.global(qos: .userInitiated)
- queue.async {
- EncConverterService.loadFile(directoryPath: rootDir, filepaths: self.filePaths)
- }
- }) {
- Text("Choose dir")
- }
正規表現
- 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/
- 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
値の変化をグラフで見る
チュートリアル
Objective-Cの動作モデルとセレクタ
- Apple製品はObjective-Cの動作モデルに基づいている
- オブジェクトがプログラムを構成しオブジェクトが相互にメッセージを送信し合うことで動作する
- このモデルではメッセージの送り先について詳しく知る必要がないため、組み合わせの自由度が高い
- Objective-Cでは、メッセージ送信の際、セレクタと呼ばれるデータを使ってメソッドを指定し動的な起動を可能としている
- SwiftではSelector型が提供されており、Selector型でメソッドを呼び出す仕組みは、Objective-CのNSObjectで定義されている
- Selector型は構造体だが、#selectorという特殊なイニシャライザを使用する
Swift Package Manager
Tips
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)
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
- https://developer.apple.com/bonjour/
- 概要
- TCP/IP環境に革新もたらすZeroConfネットワーク「Bonjour」
- Bonjour
- サンプル
- サービスタイプ
- DNS Service Discoveryの仕組み
- DNS Service Discovery
- dns-sd
- macOSのdns-sdコマンドではDNSの逆引きに 相当することが出来ない
- How to get MAC address from OS X with Swift
- $ 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
© 2006 矢木浩人