Swift
#17
TIOBE#25
PYPL#11
GitHub#22
RedMonk#11
IEEESpectrum#21
JetBrains#16
プログラミング言語
Swift
概要
SwiftはAppleが開発したiOS/macOSアプリ開発のための現代的なプログラミング言語です。
詳細
Swiftは2014年にAppleによって発表された、iOS、macOS、watchOS、tvOSアプリケーション開発のための現代的なプログラミング言語です。Objective-Cの後継として設計され、安全性、性能、表現力を重視した言語です。型安全性、自動メモリ管理、nil安全性などの機能により、より安全で保守しやすいコードの記述が可能です。2015年にオープンソース化され、Apple以外のプラットフォームでも使用できるようになりました。SwiftUIの導入により、宣言的なUI開発が可能となり、iOS開発の効率が大幅に向上しています。現在では、App Storeの多くのアプリがSwiftで開発されており、iOS/macOSアプリ開発の標準言語として確立されています。
書き方の例
Hello World
// 基本的な出力
print("Hello, World!")
// 複数の値を出力
print("Hello,", "Swift!")
// 変数を使った出力
let message = "こんにちは、Swift!"
print(message)
// 文字列補間
let name = "太郎"
let age = 25
print("私の名前は\(name)で、\(age)歳です。")
// 複数行の文字列
let multiline = """
これは複数行の
文字列です。
Swift らしい書き方ですね。
"""
print(multiline)
// デバッグ出力
debugPrint("これはデバッグ出力です")
// ファイル名と行番号を含む出力
print("エラーが発生しました", #file, #line)
// PlaygroundとiOSアプリでの基本構造
import Foundation
// iOSアプリの場合(UIKit)
/*
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Hello from iOS app!")
}
}
*/
// SwiftUIアプリの場合
/*
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
}
}
*/
変数とデータ型
// 変数と定数
var mutableValue = 42 // 変更可能な変数
let immutableValue = 100 // 変更不可能な定数
// 型注釈(型を明示的に指定)
var explicitInt: Int = 50
var explicitString: String = "Hello"
// 基本データ型
let integer: Int = 42
let floatingPoint: Double = 3.14159
let singlePrecision: Float = 2.71828
let boolean: Bool = true
let character: Character = "A"
let string: String = "Swift Programming"
// 型推論
let inferredInt = 123 // Int型として推論
let inferredDouble = 3.14 // Double型として推論
let inferredString = "Hello" // String型として推論
// 数値の表現
let binary = 0b1010 // 二進数
let octal = 0o755 // 八進数
let hexadecimal = 0xFF // 十六進数
let scientific = 1.25e2 // 指数表記
// Optional型(nil許容型)
var optionalString: String? = "値があります"
var nilValue: String? = nil
// Optional binding(安全な値取り出し)
if let unwrappedString = optionalString {
print("値: \(unwrappedString)")
} else {
print("値はありません")
}
// Guard文によるOptional handling
func processOptional(_ value: String?) {
guard let unwrappedValue = value else {
print("値がありません")
return
}
print("処理中: \(unwrappedValue)")
}
// Nil coalescing operator
let defaultValue = optionalString ?? "デフォルト値"
print("値: \(defaultValue)")
// 配列
var fruits = ["りんご", "バナナ", "オレンジ"]
let numbers: [Int] = [1, 2, 3, 4, 5]
var emptyArray: [String] = []
// 配列の操作
fruits.append("いちご")
fruits.insert("ぶどう", at: 0)
fruits.remove(at: 1)
// 辞書(Dictionary)
var person = [
"name": "田中太郎",
"age": "30",
"city": "東京"
]
// 型を明示した辞書
let scores: [String: Int] = [
"数学": 85,
"英語": 92,
"理科": 78
]
// Set(重複なしコレクション)
var uniqueNumbers: Set<Int> = [1, 2, 3, 4, 5]
uniqueNumbers.insert(3) // 重複は追加されない
// タプル
let coordinates = (x: 10.0, y: 20.0)
let httpStatus = (statusCode: 200, description: "OK")
// タプルの要素にアクセス
print("X座標: \(coordinates.x)")
print("ステータス: \(httpStatus.statusCode)")
// 型エイリアス
typealias Point = (x: Double, y: Double)
typealias UserInfo = [String: String]
let point: Point = (x: 5.0, y: 10.0)
let user: UserInfo = ["name": "山田", "email": "[email protected]"]
// 型の確認
print(type(of: integer)) // Int
print(type(of: string)) // String
print(type(of: fruits)) // Array<String>
// 型変換
let stringNumber = "123"
let convertedNumber = Int(stringNumber) ?? 0
let numberAsString = String(integer)
print("文字列から数値: \(convertedNumber)")
print("数値から文字列: \(numberAsString)")
print("配列: \(fruits)")
print("辞書: \(person)")
print("セット: \(uniqueNumbers)")
print("タプル: \(coordinates)")
関数とクロージャ
// 基本的な関数
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// 単一式の関数(returnを省略可能)
func multiply(_ a: Int, _ b: Int) -> Int {
a * b
}
// 引数ラベル付きの関数
func greet(person name: String, from hometown: String) -> String {
return "Hello, \(name) from \(hometown)!"
}
// デフォルト引数
func createUser(name: String, age: Int = 0, email: String = "") -> [String: Any] {
return [
"name": name,
"age": age,
"email": email
]
}
// 可変長引数
func sum(_ numbers: Int...) -> Int {
return numbers.reduce(0, +)
}
// inout引数(参照渡し)
func swapValues(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
// 関数を返す関数
func makeIncrementer(incrementAmount: Int) -> () -> Int {
var total = 0
return {
total += incrementAmount
return total
}
}
// 関数を引数に取る関数
func processArray(_ array: [Int], using operation: (Int) -> Int) -> [Int] {
return array.map(operation)
}
// 関数の使用例
print(add(5, 3))
print(multiply(4, 6))
print(greet(person: "田中", from: "東京"))
let user1 = createUser(name: "山田")
let user2 = createUser(name: "佐藤", age: 25, email: "[email protected]")
print("合計: \(sum(1, 2, 3, 4, 5))")
var x = 10
var y = 20
swapValues(&x, &y)
print("交換後: x=\(x), y=\(y)")
// クロージャ
let incrementByTwo = makeIncrementer(incrementAmount: 2)
print("インクリメント: \(incrementByTwo())")
print("インクリメント: \(incrementByTwo())")
// クロージャの様々な書き方
let numbers = [1, 2, 3, 4, 5]
// 完全な形
let doubled1 = numbers.map({ (number: Int) -> Int in
return number * 2
})
// 型推論を使用
let doubled2 = numbers.map({ number in
return number * 2
})
// 単一式のクロージャ
let doubled3 = numbers.map({ number in number * 2 })
// 引数名の省略
let doubled4 = numbers.map({ $0 * 2 })
// トレイリングクロージャ
let doubled5 = numbers.map { $0 * 2 }
// 高階関数の例
let evens = numbers.filter { $0 % 2 == 0 }
let total = numbers.reduce(0) { $0 + $1 }
let strings = numbers.map { "数値: \($0)" }
print("倍数: \(doubled5)")
print("偶数: \(evens)")
print("合計: \(total)")
print("文字列: \(strings)")
// エスケープクロージャ
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.global().async {
// 非同期処理をシミュレート
Thread.sleep(forTimeInterval: 1)
DispatchQueue.main.async {
completion("データを取得しました")
}
}
}
// 使用例(実際のアプリでは非同期で実行される)
// fetchData { result in
// print(result)
// }
// オートクロージャ
func logIfDebug(_ message: @autoclosure () -> String, isDebug: Bool = true) {
if isDebug {
print("DEBUG: \(message())")
}
}
logIfDebug("重要な情報: \(Date())")
構造体とクラス
// 構造体の定義
struct Person {
var name: String
var age: Int
let id: String = UUID().uuidString
// 計算プロパティ
var description: String {
return "\(name) (\(age)歳) - ID: \(id)"
}
// プロパティ監視
var email: String = "" {
willSet {
print("メールアドレスを \(newValue) に変更予定")
}
didSet {
print("メールアドレスを \(oldValue) から \(email) に変更しました")
}
}
// イニシャライザ
init(name: String, age: Int) {
self.name = name
self.age = age
}
// メソッド
func introduce() -> String {
return "私の名前は\(name)で、\(age)歳です。"
}
// mutating メソッド(構造体のプロパティを変更)
mutating func haveBirthday() {
age += 1
print("\(name)さん、\(age)歳のお誕生日おめでとう!")
}
// 静的メソッド
static func createUnknownPerson() -> Person {
return Person(name: "Unknown", age: 0)
}
}
// クラスの定義
class Vehicle {
var brand: String
var model: String
var year: Int
// 指定イニシャライザ
init(brand: String, model: String, year: Int) {
self.brand = brand
self.model = model
self.year = year
}
// 便利イニシャライザ
convenience init(brand: String, model: String) {
self.init(brand: brand, model: model, year: 2024)
}
func description() -> String {
return "\(year) \(brand) \(model)"
}
// デイニシャライザ
deinit {
print("\(description()) が解放されました")
}
}
// 継承
class Car: Vehicle {
var numberOfDoors: Int
init(brand: String, model: String, year: Int, numberOfDoors: Int) {
self.numberOfDoors = numberOfDoors
super.init(brand: brand, model: model, year: year)
}
override func description() -> String {
return "\(super.description()) - \(numberOfDoors)ドア"
}
func startEngine() {
print("\(brand) \(model) のエンジンを始動しました")
}
}
// プロトコルの定義
protocol Drawable {
func draw()
var area: Double { get }
}
protocol Colorable {
var color: String { get set }
}
// プロトコルの実装
struct Circle: Drawable, Colorable {
var radius: Double
var color: String
var area: Double {
return Double.pi * radius * radius
}
func draw() {
print("半径\(radius)の\(color)い円を描画します")
}
}
// 列挙型
enum Direction {
case north, south, east, west
func description() -> String {
switch self {
case .north: return "北"
case .south: return "南"
case .east: return "東"
case .west: return "西"
}
}
}
// 関連値を持つ列挙型
enum Result<T> {
case success(T)
case failure(Error)
func getValue() -> T? {
switch self {
case .success(let value):
return value
case .failure(_):
return nil
}
}
}
// 使用例
print("=== 構造体とクラスの使用例 ===")
// 構造体の使用
var person1 = Person(name: "田中太郎", age: 25)
print(person1.introduce())
print(person1.description)
person1.email = "[email protected]"
person1.haveBirthday()
let unknownPerson = Person.createUnknownPerson()
print(unknownPerson.introduce())
// クラスの使用
let vehicle = Vehicle(brand: "Toyota", model: "Prius")
print(vehicle.description())
let car = Car(brand: "Honda", model: "Civic", year: 2023, numberOfDoors: 4)
print(car.description())
car.startEngine()
// プロトコルの使用
let circle = Circle(radius: 5.0, color: "赤")
circle.draw()
print("面積: \(circle.area)")
// 列挙型の使用
let direction = Direction.north
print("方向: \(direction.description())")
let successResult: Result<String> = .success("データを正常に取得しました")
let failureResult: Result<String> = .failure(NSError(domain: "NetworkError", code: 404, userInfo: nil))
if let value = successResult.getValue() {
print("成功: \(value)")
}
// 値型と参照型の違い
var person2 = person1 // 構造体は値型なのでコピーされる
person2.name = "山田花子"
print("person1: \(person1.name)") // "田中太郎"
print("person2: \(person2.name)") // "山田花子"
let car1 = car // クラスは参照型なので同じインスタンスを参照
car1.brand = "Nissan"
print("car.brand: \(car.brand)") // "Nissan"
print("car1.brand: \(car1.brand)") // "Nissan"
エラーハンドリング
// エラーの定義
enum ValidationError: Error {
case emptyName
case invalidAge(Int)
case invalidEmail(String)
case networkError(code: Int, message: String)
}
// エラーの詳細情報を提供
extension ValidationError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyName:
return "名前が空です"
case .invalidAge(let age):
return "無効な年齢です: \(age)"
case .invalidEmail(let email):
return "無効なメールアドレスです: \(email)"
case .networkError(let code, let message):
return "ネットワークエラー (\(code)): \(message)"
}
}
}
// throwsキーワードを使った関数
func validateUser(name: String, age: Int, email: String) throws -> [String: Any] {
// 名前の検証
guard !name.isEmpty else {
throw ValidationError.emptyName
}
// 年齢の検証
guard age >= 0 && age <= 150 else {
throw ValidationError.invalidAge(age)
}
// メールアドレスの検証
let emailRegex = #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"#
guard email.range(of: emailRegex, options: .regularExpression) != nil else {
throw ValidationError.invalidEmail(email)
}
return [
"name": name,
"age": age,
"email": email,
"id": UUID().uuidString
]
}
// ネットワーク処理をシミュレートする関数
func fetchDataFromServer() throws -> String {
let shouldSucceed = Bool.random()
if shouldSucceed {
return "サーバーからデータを取得しました"
} else {
throw ValidationError.networkError(code: 500, message: "Internal Server Error")
}
}
// リトライ機能付きの関数
func fetchDataWithRetry(maxAttempts: Int = 3) throws -> String {
var attempts = 0
while attempts < maxAttempts {
attempts += 1
do {
let data = try fetchDataFromServer()
print("試行 \(attempts) で成功")
return data
} catch {
print("試行 \(attempts) 失敗: \(error)")
if attempts == maxAttempts {
throw error
}
// 少し待ってからリトライ
Thread.sleep(forTimeInterval: 0.1)
}
}
// この行には到達しないが、コンパイラエラーを避けるため
throw ValidationError.networkError(code: 999, message: "Unexpected error")
}
// do-catch文による例外処理
print("=== エラーハンドリング ===")
// 正常なケース
do {
let user = try validateUser(name: "田中太郎", age: 30, email: "[email protected]")
print("ユーザー作成成功: \(user)")
} catch ValidationError.emptyName {
print("エラー: 名前が空です")
} catch ValidationError.invalidAge(let age) {
print("エラー: 無効な年齢 - \(age)")
} catch ValidationError.invalidEmail(let email) {
print("エラー: 無効なメール - \(email)")
} catch {
print("予期しないエラー: \(error)")
}
// 各種エラーケースのテスト
let testCases = [
("", 25, "[email protected]"), // 空の名前
("山田花子", -5, "[email protected]"), // 無効な年齢
("佐藤次郎", 30, "invalid-email"), // 無効なメール
("高橋一郎", 25, "[email protected]") // 正常なケース
]
for (name, age, email) in testCases {
do {
let user = try validateUser(name: name, age: age, email: email)
print("✅ ユーザー作成成功: \(user["name"] ?? "Unknown")")
} catch let error as ValidationError {
print("❌ 検証エラー: \(error.localizedDescription)")
} catch {
print("❌ その他のエラー: \(error)")
}
}
// try?とtry!の使用
print("\n=== try? と try! ===")
// try? - 失敗した場合にnilを返す
let optionalUser1 = try? validateUser(name: "テストユーザー1", age: 25, email: "[email protected]")
if let user = optionalUser1 {
print("✅ オプショナルユーザー作成成功: \(user)")
} else {
print("❌ ユーザー作成に失敗しました")
}
let optionalUser2 = try? validateUser(name: "", age: 25, email: "[email protected]")
print("空の名前での結果: \(optionalUser2 == nil ? "nil" : "値あり")")
// try! - 失敗した場合にクラッシュ(使用時は注意が必要)
// let forcedUser = try! validateUser(name: "強制ユーザー", age: 25, email: "[email protected]")
// print("強制的な結果: \(forcedUser)")
// defer文 - スコープ終了時に必ず実行される
func processFile() {
print("ファイル処理を開始")
defer {
print("ファイル処理のクリーンアップ")
}
defer {
print("ファイルをクローズ")
}
do {
let data = try fetchDataFromServer()
print("データ処理: \(data)")
} catch {
print("ファイル処理中にエラー: \(error)")
}
print("ファイル処理のメイン処理完了")
}
processFile()
// Result型を使ったエラーハンドリング
func validateUserAsResult(name: String, age: Int, email: String) -> Result<[String: Any], ValidationError> {
do {
let user = try validateUser(name: name, age: age, email: email)
return .success(user)
} catch let error as ValidationError {
return .failure(error)
} catch {
return .failure(.networkError(code: 999, message: "Unknown error"))
}
}
let result = validateUserAsResult(name: "結果ユーザー", age: 28, email: "[email protected]")
switch result {
case .success(let user):
print("✅ Result成功: \(user["name"] ?? "Unknown")")
case .failure(let error):
print("❌ Result失敗: \(error.localizedDescription)")
}
// リトライ処理の例
print("\n=== リトライ処理 ===")
do {
let data = try fetchDataWithRetry(maxAttempts: 3)
print("✅ リトライ成功: \(data)")
} catch {
print("❌ リトライ最終失敗: \(error)")
}
SwiftUIの基本
// SwiftUIは宣言的UIフレームワーク
// 以下は基本的なSwiftUIの例
import SwiftUI
// 基本的なView
struct ContentView: View {
@State private var name: String = ""
@State private var age: String = ""
@State private var showAlert = false
@State private var message = ""
var body: some View {
NavigationView {
VStack(spacing: 20) {
// ヘッダー
Text("ユーザー登録")
.font(.largeTitle)
.fontWeight(.bold)
// 入力フィールド
VStack(spacing: 15) {
TextField("名前を入力", text: $name)
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("年齢を入力", text: $age)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
}
.padding(.horizontal)
// ボタン
Button(action: registerUser) {
Text("登録")
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(10)
}
.padding(.horizontal)
.disabled(name.isEmpty || age.isEmpty)
Spacer()
}
.navigationTitle("アプリ")
.alert("結果", isPresented: $showAlert) {
Button("OK") { }
} message: {
Text(message)
}
}
}
private func registerUser() {
guard let ageInt = Int(age) else {
message = "有効な年齢を入力してください"
showAlert = true
return
}
// バリデーション関数を使用(上記で定義したもの)
do {
let user = try validateUser(name: name, age: ageInt, email: "\(name.lowercased())@example.com")
message = "ユーザー '\(user["name"] ?? "Unknown")' を登録しました"
showAlert = true
// フィールドをクリア
name = ""
age = ""
} catch let error as ValidationError {
message = "登録エラー: \(error.localizedDescription)"
showAlert = true
} catch {
message = "予期しないエラーが発生しました"
showAlert = true
}
}
}
// リスト表示のView
struct UserListView: View {
@State private var users: [[String: Any]] = []
var body: some View {
NavigationView {
List {
ForEach(users.indices, id: \.self) { index in
UserRowView(user: users[index])
}
.onDelete(perform: deleteUsers)
}
.navigationTitle("ユーザー一覧")
.onAppear(perform: loadUsers)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("追加") {
addSampleUser()
}
}
}
}
}
private func loadUsers() {
// サンプルユーザーの読み込み
users = [
["name": "田中太郎", "age": 30, "email": "[email protected]"],
["name": "山田花子", "age": 25, "email": "[email protected]"],
["name": "佐藤次郎", "age": 35, "email": "[email protected]"]
]
}
private func addSampleUser() {
let newUser = [
"name": "新規ユーザー\(users.count + 1)",
"age": Int.random(in: 20...60),
"email": "user\(users.count + 1)@example.com"
] as [String : Any]
users.append(newUser)
}
private func deleteUsers(offsets: IndexSet) {
users.remove(atOffsets: offsets)
}
}
// ユーザー行のView
struct UserRowView: View {
let user: [String: Any]
var body: some View {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(user["name"] as? String ?? "Unknown")
.font(.headline)
Text("\(user["age"] as? Int ?? 0)歳")
.font(.subheadline)
.foregroundColor(.secondary)
Text(user["email"] as? String ?? "")
.font(.caption)
.foregroundColor(.blue)
}
Spacer()
Image(systemName: "person.fill")
.foregroundColor(.gray)
}
.padding(.vertical, 4)
}
}
// アプリのエントリーポイント
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Image(systemName: "person.badge.plus")
Text("登録")
}
UserListView()
.tabItem {
Image(systemName: "list.bullet")
Text("一覧")
}
}
}
}
}
// Preview(Xcodeでのプレビュー用)
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// カスタム修飾子(Modifier)の例
struct PrimaryButtonStyle: ViewModifier {
func body(content: Content) -> some View {
content
.foregroundColor(.white)
.padding()
.background(Color.blue)
.cornerRadius(10)
.shadow(radius: 5)
}
}
extension View {
func primaryButtonStyle() -> some View {
modifier(PrimaryButtonStyle())
}
}
// 使用例
// Button("テストボタン") { }
// .primaryButtonStyle()
print("SwiftUIのコード例を定義しました。実際のXcodeプロジェクトで動作します。")
バージョン
バージョン | リリース日 | 主な新機能 |
---|---|---|
Swift 5.9 | 2023-09 | Macros, if-else expressions, Parameter packs |
Swift 5.8 | 2023-03 | Function back deployment, Collection downcasts |
Swift 5.7 | 2022-09 | Regex literals, Generic protocols |
Swift 5.6 | 2022-03 | Type placeholders, ConcurrentValue protocol |
Swift 5.5 | 2021-09 | Async/await, Actors, Structured concurrency |
Swift 5.4 | 2021-04 | Multiple variadic parameters, Result builders |
Swift 5.3 | 2020-09 | Multi-pattern catch clauses, @main attribute |
参考ページ
公式ドキュメント
- Swift公式サイト - Swift公式サイト
- Swift Documentation - 公式ドキュメント
- Apple Developer Swift - Apple開発者向けSwift情報
学習リソース
- Swift Programming Language - 公式Swift言語ガイド
- Swift by Sundell - Swift学習ブログ
- Hacking with Swift - Swift総合学習サイト
開発ツール・フレームワーク
- Xcode - Swift開発IDE
- SwiftUI - 宣言的UIフレームワーク
- Swift Package Manager - パッケージ管理ツール
- Vapor - サーバーサイドSwiftフレームワーク