// 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型は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
- 変数にアクセスするときに`?`をつけると、unwrapした値に対してメソッドを実行して、その結果をwrap(Optional型に)して返却する。unwrapした値がnilの場合は、それ以降の処理を中断してnilを返却する。
- Forced unwrapping
- 変数にアクセスするときに`!`をつけてunrwapする方法
var food: String? food = "pizza" food!.uppercased() // => String("PIZZA") food = nil food!.uppercased() // => ランタイムエラー
- 変数にアクセスするときに`!`をつけてunrwapする方法
- 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 { // 呼ばれない }
- ifやwhileなどの条件式で変数への代入をするとunwrapされる
- 比較演算子
- 比較演算子を使うと、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")
- 比較演算子を使うと、Optional型の変数が自動的にunwrapされる
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()
プロパティはクラスや構造体、列挙型に値を紐付ける役割がある。
class SomeClass {
var greeting = "hello"
}
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
}
}
プロパティの更新に応じて、処理を記述できる。willSet、didSetはそれぞれnewValue、oldValueを引数に取るが、引数名の変更も可能。
class SomeClass {
var greeting: String {
willSet {
print("New value is \(newValue)")
}
didSet {
print("Value is changed from \(oldValue)")
}
}
}
型プロパティ(型メソッド)を定義するのに使う修飾子には、staticとclassの2つがある。
class SomeClass {
static var staticProperty: String = "value"
static func staticMethod() {}
class func klassMethod() {
print(staticProperty)
staticMethod()
}
}
staticとclassはそれぞれ以下の違いがある。
static | class | |
---|---|---|
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 | Type | Value |
---|---|---|
#file | String | ファイル名 |
#line | Int | 行番号 |
#column | Int | 先頭の列番号 |
#function | String | 定義されている場所 |
// x is 10, and 20 is ignored
let (x, _) = (10, 20)
シンタックス)
if #available(`platform name` `version`, `...`, *) { `statements to execute if the APIs are available` } else { `fallback statements to execute if the APIs are unavailable` }