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

MyMemoWiki

Swift

提供: MyMemoWiki
2021年2月26日 (金) 16:19時点におけるPiroto (トーク | 投稿記録)による版 (→‎Network)
ナビゲーションに移動 検索に移動

| 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

値の変化をグラフで見る

Playground view result.png

チュートリアル

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


$ 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