Swift
ナビゲーションに移動
検索に移動
| Xcode | Mac | IPhone Xcode |
目次
環境
Document
Framework
Xcode
コマンドライン
- 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
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
アクセス制御
アクセス制御 | 意味 |
---|---|
open | モジュール情報をインポートすればどんなソースファイルからでもアクセス可能、サブクラスの作成や上書き定義も自由にできる |
public | モジュール情報をインポートすればどんなソースファイルからでもアクセス可能、サブクラスの作成や上書き定義はそのクラスを定義したモジュール内でのみ可能 |
internal | 定義を含むソースファイルと同じモジュール内からはどのソースファイルからもアクセス可能。アクセス制御規定値 |
fileprivate | 定義されたソースファイルの中だけで使用できる |
private | 定義単位の内部のみで利用できる |
データ型
- 型の実体はインスタンス
- 値型と参照型
- 基本データ型
- Int
- UInt
- Float
- Double
- Bool : true/false
- Character : Unicodeの1文字
- 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
文字列
- 連結は +
- リテラルは、"で囲む
- \n 改行など\でエスケープ
文字列埋め込み
- \(式) で展開される
> var i:Int = 5 i: Int = 5 > let s: String = "Number \(i * 3)" s: String = "Number 15"
配列
- 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
参照と追加
> var ia = [1,2,3] > ia[1] $R0: Int = 2 > ia.append(4) > ia $R1: [Int] = 4 values { [0] = 1 [1] = 2 [2] = 3 [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
オプショナル型
- 扱う値が存在しないことを表すため、nil が用意されている
- nil 値を持つことがある場合、Int? 型などで表す -> オプショナル型
- パラメータ付型指定では、Optional<Int>
- オプショナル型からデータを取り出 -> アンラップ
- アンラップのためには、式の後ろに、! を置く
- オプショナルをprintする場合、明示的にString(describing:)を使用する
var opi:Int? print("\(String(describing:opi))") opi = 1 var i:Int = opi! print("\(i)")
- 実行
nil 1
オプショナル束縛構文(if-let文)
- オプショナルがnilでない場合、すぐに処理で使用したい場合
- if や while の条件部にのみ記述できる
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 の場合 }
演算子
- C言語の演算子はほぼ使える
- ポインタはないため、*、&、-> は異なった意味
- インクリメント(++)、デクリメント(--) は廃止された
キャスト
式 as 型
- as :
- as? : キャストが正しく行われた場合オプショナル型、不正な場合、nil
- as! :
モジュールと名前空間
モジュールのインポート
- フレームワークや外部モジュールからクラスや関数情報を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) }
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
ラベル
- 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文
- 想定外の状況が発生した場合に、その状況から抜け出す
- 条件には、通常の条件やオプショナル束縛などを記述できる
guard 条件 else { /* break や return、throw など制御が戻らないことが自明な文 */ }
構造体
- 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
クラス
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
列挙型
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
プロパティ
構造体、列挙、クラスに定義可能
格納型プロパティ
- 定数、変数
計算型プロパティ
- 手続きで構成
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
タプル
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
プロトコル
- Swiftではインターフェースを定義するためのプロトコルをクラスだけでなく、構造体や列挙にも適用可能
- プロトコルには実装を共有できない問題があるが、拡張機能を活用することで問題を軽減できる
- 本来関係ない型どうしをプロトコルと拡張を用いて後から連携するようにできる
- Swiftでは、プロトコル指向開発を進めることが可能
クロージャ
- 実行する文を記述する前に、inキーワードが必要
- 仮引数には、inout、可変長引数も利用可能
{ ( 仮引数: 型 ) -> 型 in // 省略可 文 }
- 例(引数、戻り値なし)
var hello = { () -> () in print("hello")} hello()
- 結果
hello
SwiftUI
UIKitとのインターフェース
SwiftUIは既存のApplieプラットフォームUIフレームワークとシームレスに動作する。
MacOS
Playground
値の変化をグラフで見る
チュートリアル
Objective-Cの動作モデルとセレクタ
- Apple製品はObjective-Cの動作モデルに基づいている
- オブジェクトがプログラムを構成しオブジェクトが相互にメッセージを送信し合うことで動作する
- このモデルではメッセージの送り先について詳しく知る必要がないため、組み合わせの自由度が高い
- Objective-Cでは、メッセージ送信の際、セレクタと呼ばれるデータを使ってメソッドを指定し動的な起動を可能としている
- SwiftではSelector型が提供されており、Selector型でメソッドを呼び出す仕組みは、Objective-CのNSObjectで定義されている
- Selector型は構造体だが、#selectorという特殊なイニシャライザを使用する
Tips
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
- 概要
- Bonjour
- サンプル
- サービスタイプ
- DNS Service Discoveryの仕組み
- DNS Service Discovery
- dns-sd
- 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
© 2006 矢木浩人