Skip to content

Instantly share code, notes, and snippets.

@y-takagi
Last active July 25, 2020 03:38
Show Gist options
  • Save y-takagi/804cb844c5cc5c18c6a1d4e56ab72548 to your computer and use it in GitHub Desktop.
Save y-takagi/804cb844c5cc5c18c6a1d4e56ab72548 to your computer and use it in GitHub Desktop.
Swift Guide (v3.0)

Swift Guide (v3.0)

Comment

1行コメント

// this is a comment

複数行コメント

/*
 this is also a comment,
 but written over multiple lines
 */

ネストされた複数行コメント

/*
 this is the start of the first multiline comment
 /* this is the second, nested multiline comment */
 this is the end of the first multiline comment
 */

変数

// variables
var greetingVar = "hello, world"

// constants
let greetingCon = "hello, world"

// type annotations
let greetingType: String = "hello, world"

真偽値

Swiftは真偽値としてBool型(true、false)が用意されている。

let orangesAreOrange = true
let turnipsAreDelicious = false

また型安全なので、Bool型以外を真偽値として使うことは出来ない。

let i = 1
if i {
    // this example will not compile, and will report an error
}

if i == 1 {
    // this example will compile successfully
}

オプショナル

swiftでは基本的に変数にnilを代入することを許可していない。nilの代入を行う場合はOptional<T>型、ImplicitlyUnwrappedOptional<T>型どちらかを利用する必要がある。

Optional<T>

Optional型はenumで定義されている。

public enum Optional<Wrapped> : NilLiteralConvertible {

  /// The absence of a value.
  ///
  /// In code, the absence of a value is typically written using the `nil`
  /// literal rather than the explicit `.none` enumeration case.
  case none

  /// The presence of a value, stored as `Wrapped`.
  case some(Wrapped)

  /// Creates an instance that stores the given value.
  public init(_ some: Wrapped)

  // 以下省略
}

例えば、nilを許容するString型の変数を扱いたい場合は以下のように宣言する。

// T?はOptional<T>のシンタックスシュガー
var food: String?

swiftではOptional<T>型の変数のことをwrapされていると言う。またOptional<T>から実際の値(T型の値)を取り出すことをunwrapすると言い、unwrapして変数にアクセスするのに以下の方法がある。

  • Optional chaining
    • 変数にアクセスするときに`?`をつけると、unwrapした値に対してメソッドを実行して、その結果をwrap(Optional型に)して返却する。unwrapした値がnilの場合は、それ以降の処理を中断してnilを返却する。
      var food: String?
      food = "pizza"
      food?.uppercased() // => Optional("PIZZA")
      
      food = nil
      food?.uppercased() // => nil
      food?.uppercased().lowercased() // => nil
              
  • Forced unwrapping
    • 変数にアクセスするときに`!`をつけてunrwapする方法
      var food: String?
      food = "pizza"
      food!.uppercased() // => String("PIZZA")
      
      food = nil
      food!.uppercased() // => ランタイムエラー
              
  • Optional binding
    • ifやwhileなどの条件式で変数への代入をするとunwrapされる
      var food: String?
      food = "pizza"
      if let unwrappedFood = food {
        unwrappedFood // => String("pizza")
        unwrappedFood.uppercased() // => String("PIZZA")
      }
      
      food = nil
      if let unwrappedFood = food {
        // 呼ばれない
      }
              
  • 比較演算子
    • 比較演算子を使うと、Optional型の変数が自動的にunwrapされる
      var optionalInt: Int? = 10
      
      print(optionalInt == 10) // => true
      print(optionalInt != 10) // => false
      print(optionalInt >= 10) // => true
      print(optionalInt > 10)  // => false
      
      optionalInt = nil
      // nilでもエラーにはならない
      print(optionalInt == 10) // => false
              
    • 三項演算子
      var optionalValue: String?
      // (a ?? b) は (a != nil ? a! : b) のシンタックスシュガー
      print(optionalValue ?? "hello")
              

ImplicitlyUnwrappedOptional<T>

Optional型ではあるけど、アクセスするときに強制的にunwrapされる型。

// T!はImplicitlyUnwrappedOptional<T>のシンタックスシュガー
var food: String!

food = "pizza"
food.uppercased() // => String("PIZZA")

food = nil
food.uppercased() // => ランタイムエラー

Swiftでは、オブジェクトのinit時に値が確定できない場合はOptional型にする必要があります。しかし、アクセスするときに必ず値があることが保証されているにも関わらず、毎回unwrapするのは効率が悪い。ImplicitlyUnwrappedOptional型はこれを解決するために使う。

例として、IBOutletが挙げられる。IBOutletで接続された変数は、オブジェクト(ViewControllerなど)のinit時には値が確定されていないが、viewDidLoad後であれば値は確実に入っている。こういう場合はImplicitlyUnwrappedOptional型で宣言しておくと、毎回unwrapする必要がなくなる。

@IBOutlet weak var button: UIButton!

関数

関数内で関数の宣言も可能。

シンタックス)

// 宣言
func methodName([label] param: type [= defaultValue]) -> returnType {
  return value
}

// 呼び出し
methodName([label:|param:] value)

サンプル)

func greet(_ name: String) -> String {
  func increment(input: Int) -> Int { return input + 1 }
  return "Hello, \(name)!"
}

greet("John") // => Hello, John!

また関数は仮引数も含めてユニークであればよいので、下記の3つはそれぞれユニークである。

// greet(to: "John")
func greet(to name: String) -> String {
    return "Hello, \(name)!"
}

// greet("John")
func greet(_ name: String) -> String {
    return "Hello, \(name)!"
}

// greet(name: "John")
func greet(name: String) -> String {
    return "Hello, \(name)!"
}

クロージャ

シンタックス)

{ (`parameters`) -> `return type` in `statements` }

サンプル)

names = ["John", "Bob", "Alice"]

names.sorted({ (s1: String, s2: String) -> Bool in
    return s1 > s2
})

// 型は省略できる。またクロージャがメソッドの最後の引数なら、括弧を省略できる。
names.sorted { s1, s2 in return s1 > s2 }

// 式が1つしかないなら、returnを省略できる
names.sorted { s1, s2 in s1 > s2 }

// クロージャでは引数は$0、$1、...で扱うこともできる
names.sorted { $0 > $1 }

クラス、構造体、列挙型

クラス

参照型。

class SomeClass: SuperClass {
  var v1 = "init"
  var v2: String?
  
  // init()で初期化しないとエラーになる
  var v3: String

  let v4 = "const"
  
  // init()で初期化しないとエラーになる
  let v5: String

  init() {
    self.v3 = "init"
    self.v5 = "const"
  }

  deinit {
    // print("Do something when deallocated")
  }

  override func superMethod() -> Void {}
  func someMethod() -> Void {}
}

構造体

値型。基本はクラスと同じだが、変数の変更は不可。

struct Resolution {
  var width = 0
  var height = 0
  func size() -> Int {
    return width * height
  }
}
r = Resolution(width: 800, height: 600)
r.width
r.height
r.size()
r.width = 1000 // これはエラーになる

列挙型

値型。メソッドを実装できるが、値を保持することは出来ない。

enum CardType {
  case Spades, Hearts, Diamonds, Clubs
  func color() -> String {
    switch self {
    case .Spades, .Clubs:
      return "black"
    case .Hearts, .Diamonds:
      return "red"
    }
  }
}
card = CardType.Spades
card.color()

プロパティ

プロパティはクラスや構造体、列挙型に値を紐付ける役割がある。

Stored Properties (保持型プロパティ)

class SomeClass {
  var greeting = "hello"
}

Computed Properties (算出プロパティ)

setter/getterを記述できる。setterは引数にnewValueを取る。

class SomeClass {
  var name: String
  private var _greeting: String = "hello"
  var greeting: String {
    get { "\(_greeting), \(name)" }
    set { _greeting = newValue }
  }

  init(name: String) {
    self.name = name
  }
}

Property Observers (監視プロパティ)

プロパティの更新に応じて、処理を記述できる。willSet、didSetはそれぞれnewValue、oldValueを引数に取るが、引数名の変更も可能。

class SomeClass {
  var greeting: String {
    willSet {
      print("New value is \(newValue)")
    }
    didSet {
      print("Value is changed from \(oldValue)")
    }
  }
}

Type Properties (型プロパティ)

型プロパティ(型メソッド)を定義するのに使う修飾子には、staticとclassの2つがある。

class SomeClass {
  static var staticProperty: String = "value"
  static func staticMethod() {}

  class func klassMethod() {
    print(staticProperty)
    staticMethod()
  }
}

staticとclassはそれぞれ以下の違いがある。

staticclass
override出来ない出来る
struct / enum使える使えない
Stored Property使える使えない

プロトコル

クラス、構造体、列挙型はそれぞれプロトコルを実装できる。

protocol DiceGame {
  var dice: Int { get }
  func play()
}

protocol DiceGameDelegate {
  func gameDidStart(game: DiceGame)
  func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
  func gameDidEnd(game: DiceGame)
}

struct SomeStructure: DiceGame, DiceGameDelegate {}
enum SomeEnumeration: DiceGame {}

class SomeClass: SomeSuperClass, DiceGame {
  var delegate: DiceGameDelegate?
}

エラーハンドリング

シンタックス)

do {
  try `expression`
} catch `pattern1` {
  `statements`
} catch `pattern2` where `condition` {
  `statements`
}

例外を投げる関数throwの引数は、Error Protocolを実装しているオブジェクトを渡す。

enum ErrorResponse: Error {
  case BadRequest, NotFound, URITooLong(uri: String)
}

func errorMethod(code: Int) throws -> String {
  switch code {
  case 400:
    throw ErrorResponse.BadRequest
  case 404:
    throw ErrorResponse.NotFound
  case 414:
    throw ErrorResponse.URITooLong(uri: "http://www.google.com")
  }
  return "success"
}

do {
  try errorMethod(code: 414)
} catch ErrorResponse.URITooLong(let uri) {
  print(uri) // => http://www.google.com
}

// エラー起きても特に処理しない場合は下記のように書ける。成功した場合はOptional型にラップされて返却される。
let r = try? errorMethod(code: 414)
print(r) // => nil

// ランタイム時にエラーが起きないことが保証されてる場合は下記のように書ける(もちろん例外起きたら、ランタイムエラーになる)
let r = try! errorMethod(code: 200)
print(r) // => success

その他

Literal Expression

LiteralTypeValue
#fileStringファイル名
#lineInt行番号
#columnInt先頭の列番号
#functionString定義されている場所

Wildcard Expression

// x is 10, and 20 is ignored
let (x, _) = (10, 20)

Availability Condition

シンタックス)

if #available(`platform name` `version`, `...`, *) {
  `statements to execute if the APIs are available`
} else {
  `fallback statements to execute if the APIs are unavailable`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment