20211201のSwiftに関する記事は9件です。

Factory Method パターンについてまとめてみた【Swiftでデザインパターン攻略 #1】

はじめに 本記事では、GoFの23種類のデザインパターンの1つでもある、FactoryMethodパターンについて書いてみました。 FactoryMethodパターンとは 概要 「インスタンスの生成をサブクラスに実行させ、柔軟にインスタンス生成を行うためのパターン」のことです。 インスタンスを生成する側は、インスタンスについて直接意識しなくても、利用することができます。(Protocolを介しているため) Wikipediaより このパターンの進化前(?)のパターンで、Factoryパターン(SimpleFactory)というのがあります。 Factoryパターンは、1つのファクトリの中で、オブジェクトの種類の選択を動的に行うことが特徴です。 それに対し、FactoryMethodパターンでは、生成するオブジェクトごとに、Factoryクラスを用意することで、コードの冗長化や柔軟性を高めていることが特徴です。 SwiftにおけるFactoryMethodパターン 個人的な意見として、Swiftはenum型が強すぎて、Factoryパターンのほうが使用頻度が高い気がします。 Factoryパターンについては、下記をご覧ください。 実装してみた 今回のケースは、 従業員が職種別に存在する会社がある。 その会社の社員を職種で判断し、仕事(Task)を実行させたいと思います。 import UIKit // MARK: Product(インスタンス化される側)のプロトコル protocol Employee: AnyObject { var name: String { get set } var department: String { get } func executeTask() } // MARK: Creator(インスタンス化する側)のプロトコル protocol Factory: AnyObject { func create(name: String) -> Employee func createEmployee(name: String) -> Employee func registerEmployee(_ employee: Employee) } // MARK: Productを実装した具象クラス class Developer: Employee { var name: String var department: String { return "開発部" } init(name: String) { self.name = name } func executeTask() { print("\(department)の\(name)は、コーディングを行います。") } } // MARK: Creatorを実装した具象クラス class DeveloperFactory: Factory { var employeeList: [Employee] = [] func create(name: String) -> Employee { let employee = createEmployee(name: name) registerEmployee(employee) return employee } func createEmployee(name: String) -> Employee { return Developer(name: name) } func registerEmployee(_ employee: Employee) { employeeList.append(employee) } } // MARK: 実行 let factory = DeveloperFactory() let emp1 = factory.create(name: "山田") emp1.executeTask() // 開発部の山田は、コーディングを行います。 let emp2 = factory.create(name: "田中") emp2.executeTask() // 開発部の田中は、コーディングを行います。 冒頭でも述べたように、生成するインスタンスごとに、Factoryクラスを追加するのが特徴であるため、もし営業部を追加したいということであればこんな感じで追加します。 class SalesPerson: Employee { var name: String var department: String { return "営業部" } init(name: String) { self.name = name } func executeTask() { print("\(department)の\(name)は、得意先への訪問を行います。") } } class SalesPersonFactory: Factory { var employeeList: [Employee] = [] func create(name: String) -> Employee { let employee = createEmployee(name: name) registerEmployee(employee) return employee } func createEmployee(name: String) -> Employee { return SalesPerson(name: name) } func registerEmployee(_ employee: Employee) { employeeList.append(employee) } } // MARK: 実行 let salesFactory = SalesPersonFactory() let emp3 = salesFactory.create(name: "佐藤") emp3.executeTask() 以上!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift]async/awaitを使ってGitHubAPIを叩く(UIKit版)

概要 Swift5.5から新しく登場したasync/awaitによる非同期処理の学習でGitHub APIを叩いてリポジトリを検索する機能を実装したので記事にします。 開発環境 Swift 5.5 Xcode 13.1 サンプルプロジェクト GitHubにPushしました。気になる方はご覧ください。 https://github.com/ken-sasaki-222/AsyncAwaitTrainingInUIKit レスポンスをもとにModelを作成 { "total_count": 223653, "incomplete_results": false, "items": [ { "id": 44838949, "node_id": "MDEwOlJlcG9zaXRvcnk0NDgzODk0OQ==", "name": "swift", "full_name": "apple/swift", "private": false, "owner": { "login": "apple", "id": 10639145, "node_id": "MDEyOk9yZ2FuaXphdGlvbjEwNjM5MTQ1", "avatar_url": "https://avatars.githubusercontent.com/u/10639145?v=4", "gravatar_id": "", ... }, "html_url": "https://github.com/apple/swift", "description": "The Swift Programming Language", ... 今回はサンプルプロジェクトなのでfull_nameと、descriptionだけ取得します。 SearchResponseModel.swift import Foundation struct SearchResponseModel: Decodable { var items: [Item] } struct Item: Decodable { var full_name: String var description: String? } 実装 処理順序 依存関係逆転の原則(DIP)を利用して今回は開発しています。 DataStore SearchRepositoryDataStore.swift import Foundation class SearchRepositoryDataStore { private let baseUrl = "https://api.github.com/" private let shared = URLSession.shared private let decoder = JSONDecoder() func searchRepositories(query: String) async throws -> SearchResponseModel { let queryString = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) let urlString = baseUrl + "search/repositories?q=\(queryString ?? "")" guard let url = URL(string: urlString) else { throw NSError(domain: "error", code: -1, userInfo: nil) } let request = URLRequest(url: url) let (data, _) = try await shared.data(for: request) let response = try decoder.decode(SearchResponseModel.self, from: data) return response } } APIに通信するDataStoreです。検索結果をRepositoryに返します。 async throwsと書くことでエラーが発生する可能性のある非同期関数を意味します。エラー処理はthrowで返します。非同期処理を行う箇所でtry awaitを書くことで同期的な書き方ができます。 これまでの@escapingでの非同期処理に比べて、onFailre()などの意識しないといけない箇所が減ったので可読性だけでなく安全性も高くなっています。 Repository SearchRepositoryInterface.swift import Foundation protocol SearchRepositoryInterface { func searchGitHubRepository(searchText: String) async throws -> [Item] } SearchRepository.swift import Foundation class SearchRepository: SearchRepositoryInterface { private let searchRepositoryDataStore = SearchRepositoryDataStore() func searchGitHubRepository(searchText: String) async throws -> [Item] { do { let response = try await searchRepositoryDataStore.searchRepositories(query: searchText) let items = response.items return items } catch(let error) { throw error } } } SearchRepositoryDataStoreへ検索ワードを渡してdo catchで値を受け取ります。受け取った結果はasync throwsでViewControllerに返します。 ポイントは非同期関数を呼ぶ箇所でawaitしている点です。これまではクロージャーで受け取っていた箇所が整理されて可読性が高まっています。 ViewController SearchViewController.swift import UIKit class SearchViewController: UIViewController { @IBOutlet weak var searchBar: UISearchBar! @IBOutlet weak var tableView: UITableView! private var searchRepository: SearchRepositoryInterface private var indicator = UIActivityIndicatorView() private var items: [Item] = [] init(searchRepository: SearchRepositoryInterface) { self.searchRepository = searchRepository super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { self.searchRepository = RepositoryLocator.getSearchRepository() super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() setUpSearchBar() setUpTableView() setUpIndicator() } private func setUpSearchBar() { searchBar.delegate = self } private func setUpTableView() { tableView.delegate = self tableView.dataSource = self } private func setUpIndicator() { indicator.center = view.center indicator.style = .large indicator.color = .lightGray view.addSubview(indicator) } private func refreshTableView() { self.items = [] tableView.reloadData() } private func showSearchTextIsEmptyAlert() { let alert = UIAlertController(title: "確認", message: "リポジトリ名を入力してください", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "やり直す", style: .default, handler: nil)) present(alert, animated: true, completion: nil) } private func showSearchResponseErrorAlert() { let alert = UIAlertController(title: "エラー", message: "検索に失敗しました", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "やり直す", style: .default, handler: nil)) present(alert, animated: true, completion: nil) } @IBAction func tapRefreshButton(_ sender: UIBarButtonItem) { refreshTableView() searchBar.text = "" } } extension SearchViewController: UISearchBarDelegate { func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { searchBar.showsCancelButton = true return true } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.showsCancelButton = false view.endEditing(true) } func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { indicator.startAnimating() refreshTableView() if let searchText = searchBar.text { if searchText == "" { showSearchTextIsEmptyAlert() indicator.stopAnimating() } else { Task { do { var items = try await searchRepository.searchGitHubRepository(searchText: searchText) print("items:", items as Any) if items.count == 0 { let emptyItem = Item(full_name: "検索結果0件", description: "") items.append(emptyItem) self.items = items tableView.reloadData() indicator.stopAnimating() } else { self.items = items tableView.reloadData() indicator.stopAnimating() } } catch(let error) { print("error:", error.localizedDescription) indicator.stopAnimating() showSearchResponseErrorAlert() } } } } } } extension SearchViewController: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) let name = cell.viewWithTag(1) as? UILabel let description = cell.viewWithTag(2) as? UILabel name?.text = items[indexPath.row].full_name if description?.text != "" { description?.text = items[indexPath.row].description } else { description?.text = "not description text." } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) } } UISearchBarの検索ボタンタップでテキストが空文字でなければ検索を開始するシンプルな機能です。 ポイントは非同期処理を開始する箇所をTaskで囲んでいる点です。同期処理から非同期処理を呼ぶ場合、Taskで囲む必要があります。囲んだ後は同期的に書いてtry awaitで非同期処理を呼べばOKです。 Repositoryのasync throwsから返ってきた結果をdo catchで受け取ってそれぞれ処理します。 検索結果画面 おわりに 今回はasync/awaitを使ってGitHub APIを叩きました。 これまで@escapingで書いてたときのクロージャラッシュに比べると、安全性が向上し可読性が高くなった印象です。また同期的に書ける点も嬉しいですね。 ご覧いただきありがとうございました。 こうしたほうがいいや、ここはちょっと違うなど気になる箇所があった場合、ご教示いただけると幸いです。 おまけ データに依存するので完全なテストではないですが、DataStoreの動作確認レベルでUnitTestを書きました。 SearchRepositoryDataStoreTests.swift import XCTest class SearchRepositoryDataStoreTests: XCTestCase { override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } func test_searchRepositoryDataStore() async throws { let dataStore = SearchRepositoryDataStore() let searchText = "Swift" let response = try await dataStore.searchRepositories(query: searchText) let items = response.items print("Success search repository dataStore.") print("items:", items) XCTAssert(items.count > 0) } func testPerformanceExample() throws { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ここでもasync throwsとtry awaitが登場してますね。これまでのXCTestExpectationがなくても非同期テストを実行できます。 お知らせ iOSアプリ開発案件を募集しています!(副業) ▼おおよその稼動可能時間 - 月の稼動30-40時間 - 平日の夜19:00以降 - 土曜日終日稼働可 Twitter DMでご依頼お待ちしております!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【watchOS 8】コンプリケーションのアイコン設定方法

どういうことか おそらくあなたが抱えている悩みは2つある。 コンプリケーションの選択画面でアイコンが下記画像のように真っ白なものが表示される もしくはまったく表示されない 自分もそうだった。しかし解決した。 アイコンの設定方法 直感的にXcodeを眺めたらWatchKit ExtensionのAssetsに適切なサイズの画像を当てはめていけばいいだろうと思う。1 しかしうまく表示されないので、そもそもこの設定方法は間違ってるのでは??といろいろ調べたところwatchOS 8は以前まで設定方法が異なるらしく参考にならなかった(そもそも記事が少ない)。 答え 結論から言うと上記の画像設定方法は間違っていない。 なぜ真っ白な画像が出るのか Assetsで設定する一覧の頭に "Graphic"とついているもの以外は単色の透過された画像を設定しないと表示されない。なんと。 ちなみに背景塗りつぶしであれば円形で仕上げたほうが見た目はきれい。 フルカラーのアイコンが表示されない 上記のパターン以外(頭に "Graphic"とついているもの)はフルカラーのアイコンを設定できるが、それでも表示されない。なぜか。 実のところこれはキャッシュの問題のようで、正しく設定されていればApple Watchを再起動すると出てくる。これでめちゃくちゃハマった。 あいさつ 以上です。 スクリーンショットにあるアプリはApple Watch専用としてリリースした卓球の点数カウンターアプリです。 スタンドアロンアプリは知見も少なく正直よくわかんないことだらけですが、なんとかアップデートしたり記事書いたりしていく予定です。 おわり(´・ω・`) しかしこのコンプリケーション用の画像サイズ、よくある "icon generator" 的なやつが未対応なのでいちいち自分で画像をリサイズする必要がある。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】PHPhotoLibraryでフォトライブラリを実装する方法

実装コード iOS 14からPHPhotoLibrary.authorizationStatus()の引数に.readWriteか.addonlyの指定が必要になりました。 そこで、フォトライブラリの開き方を備忘録としてまとめました。 実装したalbumActionメソッドを使用するとフォトライブラリが開けます。 info.plistにPrivacy – Photo Library Usage Descriptionの追加も忘れないように! import Photos private func albumAction() { // 端末にアルバムがあるかを確認 if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.photoLibrary) { if #available(iOS 14, *) { // iOS14以降の設定 let authorizationStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite) self.albumCommonAction(authorizationStatus) } else { // iOS14より前の設定 let authorizationStatus = PHPhotoLibrary.authorizationStatus() self.albumCommonAction(authorizationStatus) } } } private func albumCommonAction(_ authorizationStatus: PHAuthorizationStatus) { // フォトライブラリを使う準備 let photoLibraryPicker = UIImagePickerController() photoLibraryPicker.sourceType = .photoLibrary photoLibraryPicker.delegate = self switch authorizationStatus { case .notDetermined: // 初回起動時アルバムアクセス権限確認 PHPhotoLibrary.requestAuthorization { status in switch status { case .authorized: // アクセスを許可するとカメラロールが出てくる DispatchQueue.main.async { self.present(photoLibraryPicker, animated: true) } default: break } } case .denied: // アクセス権限がないとき let alert = UIAlertController(title: "", message: "写真へのアクセスを許可してください", preferredStyle: .alert) let settingsAction = UIAlertAction(title: "設定", style: .default, handler: { (_) -> Void in guard let settingsURL = URL(string: UIApplication.openSettingsURLString ) else { return } UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil) }) let closeAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: .cancel, handler: nil) alert.addAction(settingsAction) alert.addAction(closeAction) self.present(alert, animated: true, completion: nil) case .authorized, .restricted, .limited: // アクセス権限があるとき self.present(photoLibraryPicker, animated: true) @unknown default: break } } Limited Photos Libraryの利用 iOS 14からはLimited Photos Libraryが使えるようになり、ユーザが選択した写真のみしかアプリからアクセスできない状態を作ることができます。 (「写真を選択...」の項目がiOS14から追加) ただ、ここでアクセスを許可した写真を一覧で開けるようなものがOS標準でなさそう... 現時点では、自前で実装するかライブラリを使うかして実装しないといけない MosaiqueAssetsPicker 参考 WWDC2020での許諾周りのアップデートまとめ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SwiftUI】NavigationBarのカスタマイズ

自分が設定に少し手間取ったので、メモとして残します。色々と調べた結果をまとめ、汎用化コードを設計。 これさえあれば、ナビゲーションバーのデザインは完璧。 関数の準備 func setupNavigationBar() { let appearance = UINavigationBarAppearance() appearance.configureWithOpaqueBackground() // 背景色 appearance.backgroundColor = UIColor(red: 33/255, green: 73/255, blue: 125/255, alpha: 1) // タイトル文字色 appearance.titleTextAttributes = [.foregroundColor: UIColor.white] UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().compactAppearance = appearance // 遷移後の「<戻る」ボタンの色 UINavigationBar.appearance().tintColor = .white } あとはinit()の際に読み込む init(){ setupNavigationBar() } 補足 # インライン表示(ほぼ全てのアプリがインライン表示) .navigationBarTitleDisplayMode(.inline) # ナビゲーションバーを一時的に消したいとき .navigationBarHidden(true) あとはNavigationbarItemsでメニューを追加するだけ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiftで複数ファイルを纏めて圧縮..圧縮ぅ!!

事のきっかけ GUI的には直ぐにできる操作だけども、Swift的にどうすれば良いのか気になった次第。 使用技術 Zip Foundation: Swiftネイティブで圧縮するライブラリ。 Swift: プログラミング言語 開発環境 macOS Big Sur Intel Mac Xcode 13.1 内容 フォルダ構成 Documentフォルダにworkフォルダを作成して保存 [Document] |-[work] |- test.png |- test.txt |- test.yaml 手順(上から順に) 参考サイトを参考にZipFoundationのパッケージをインポート main.swiftに下記を記載して実行。 main.swift import Foundation import ZIPFoundation func archiveFile() throws -> Void { // fileManager let fileManager = FileManager() // User Document Folder let path: URL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! let workingPath: URL = path.appendingPathComponent("work", isDirectory: true) print("====WORKING-PATH=====") print(workingPath) print("=====================") // get Working Contents guard let workingContents: [String] = try? fileManager.contentsOfDirectory(atPath: workingPath.path) else {return} // filter .DS_Store let filteredworkingContents: [String] = workingContents.filter{$0 != ".DS_Store"} let sourceURLs: [URL] = filteredworkingContents.map{workingPath.appendingPathComponent($0, isDirectory: false)} print("====SOURCE-URLS======") print(sourceURLs) print("=====================") // create tmp folder do { try fileManager.createDirectory(at: workingPath.appendingPathComponent("tmp", isDirectory: true), withIntermediateDirectories: false, attributes: nil) } catch { print("ERROR") } let archiveURL = workingPath.appendingPathComponent("tmp/test.zip", isDirectory: false) print("=====ARCHIVE-URL=====") print(archiveURL) print("=====================") guard let archive = Archive(url: archiveURL, accessMode: .create) else { throw Archive.ArchiveError.unwritableArchive } do { try sourceURLs.forEach{ try archive.addEntry(with: $0.lastPathComponent, relativeTo: $0.deletingLastPathComponent()) } } catch { print("Error") } } try archiveFile() ここに至るまで。 特定のファイルをZip化したいと思い、やり方を調べていたが、ZipFoundationの公式サイトを見ても書いておらず、日本語のサイトや英語のサイトを探索したが全くと言っていいほど情報がなかった。 しかし、公式サイトにはディレクトリを指定する事で纏めてZip化しているサンプルコードがあったため、このサンプルコードをよすがにコードをたどる事で見つけることができた。 まとめ ドキュメントだけでなく、利用するライブラリのコードを直接見ることで、事細かにカスタマイズできることを改めて認知した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift5 全予約語 (106語) の解説

ドキュメントから漏れている予約語もあるため、下記情報を統合してカウントしています。 * The Swift Programming Language (Swift 5) Lexical Structure * The Swift Programming Language (Swift 5) Summary of the Grammar * https://github.com/apple/swift/blob/main/utils/gyb_syntax_support/Token.py * https://github.com/apple/swift/tree/main/lib/Parse 宣言 (declarations) 型、変数、定数、メソッド、関数などの宣言部分で使用する予約語 actor [Swift 5.5~] Swift の型の種類の一つである Actor 型を宣言します。 actor SampleActor { var mutableValue = "mutable" let constValue = "const" func defaultFunc() {} nonisolated func nonisolatedFunc() {} nonisolated var nonisolatedComputedProperty: String { "computedProperty getter" } } Actor を使うことで、複数スレッドからの変更可能データに対する同時アクセスによって発生するデータの破損(data race)を防ぐコードをコンパイラのサポートを受けながら書けるようになります。 Actor の概要は Swift LanguageGuide > Concurrency > Actors に記載されています。実際のアプリ開発での使い所のイメージを掴んでみたい場合は WWDC21 Swift concurrency: Update a sample app が分かりやすくてオススメです。  詳細 Actor の特徴は並列処理の実行環境下でのデータの保護です。(より厳密に言うなら、actor の instance を通すことで、隔離された状態に同期的にアクセスする機能を提供するものです。) Actor の持つ変更可能なデータや method に対して同時に複数のタスクからアクセスされないように自動的に制御されます。他のタスクからアクセスされている場合はアクセス可能になるまで待機します。そのため、アクセスする場所では await でマークしておく必要があります。 await sampleActor.mutatingValue await sampleActor.defaultFunc() 変更不可なデータである定数や nonisolated が付与されている method/computed property は data race が発生しないことが保証されているため複数タスクから同時にアクセスしても問題ありません。そのため、 await を必要とせずにアクセスできます。この状態を「隔離されていない(non-isolated)」と表現します。これについては nonisolated の項目 に記載しています。 sampleActor.constValue sampleActor.nonisolatedFunc() sampleActor.computedProperty 関連: nonisolated, await async [Swift 5.5~] 1. function 宣言部の async 非同期の function を表します。 func listPhotos(inGallery name: String) async -> [String] { let result = // ... some asynchronous networking code ... return result } DB 操作やネットワーク処理などの非同期の処理を待つ、つまり、途中で関数内の処理を中断・再開するような関数を表します。非同期関数を呼ぶには await を使って結果を待つ(suspension point)ことを明示します。 let photoNames = await listPhotos(inGallery: "Summer Vacation") 2. async let 非同期関数の結果を受け取る変数を宣言します。let thumbnail = await downloadImage(...) とする場合と違い、実際に値を使う部分に到達するまで待機しません。 下記の例では thumbnail と authorImage の download が並列で実行され、値が使われる箇所(let photos = ...)で download の終了を待ちます。 async let thumbnail = downloadImage(url: bookImageUrl) // download 開始 async let authorImage = downloadImage(url: authorImageUrl) // download 開始 let photos = await [thumbnail, authorImage] // thumbnail と authorImage の download が終わるまで待機 関連: await await [Swift 5.5~] 1. await + fuction/method 非同期関数の終了を待つ部分(suspension point)を表します。 // download が終わるまで次の処理に進まない。 // (thread を一時的に解放して他の待機中のタスクが実行される) let photo = await downloadPhoto(named: name) show(photo) 2. for await-in loop AsyncSequence に対して loop を回す場合は sequence から要素が渡されるまで待機します。 for await i in Counter(howHigh: 10) { print(i, terminator: " ") } class 場所によって意味が異なります。 クラスの宣言や、メソッドやプロパティの前に指定することでクラスメソッド、クラスプロパティの宣言をします。 class Sample { class func f() {} class var classProperty: String { "classProperty" } } プロトコルの適用先をクラスだけに制限したい場合 protocol SampleProtocol : class {} class Sample : SampleProtocol {} deinit デストラクタの宣言をします。これは class でのみ有効です。 スーパークラスの deinit はサブクラスの deinit 実行後に自動的に呼ばれます。 class SuperClass { deinit { print("Super Class's deinit is called") } } class SubClass: SuperClass { deinit { print("Sub Class's deinit is called") } } var subClass: SubClass? = SubClass() subClass = nil 出力 Sub Class's deinit is called Super Class's deinit is called enum 列挙型の宣言をします。 enum SampleEnum { case a case b(Int) } extension 定義済みのクラス、構造体、プロトコル、列挙型に対して function または computed property を追加します。 extension Array { func f() {} } let intArray = [1] intArray.f() protocol の適合の追加や extension SampleClass: HogeDelegate { func f() {} } where で適応する条件を指定できます。 extension Array where Element == Int { func funcForIntArray() {} } intArray.funcForIntArray() fileprivate アクセス修飾子の一種です。 同じファイル内からのアクセスが可能です。 関連: public, private, internal, open 参考: The Swift Programming Language # Access Control func function の宣言をします。 import モジュールを読み込みます。 init コンストラクタを宣言します。 inout intout 指定された引数として渡された変数は、function 内での変更が適応されます。(表面的には参照渡しに似た動作になります) 具体的には下記の動作をします。 function の呼び出し時に、引数として与えられた値がコピーされ、そのコピーが function の中で使われます。 function の終了時に引数として与えられた変数にコピーが代入されます。 class SampleClass {} func inoutSampleForClass(sampleClass: inout SampleClass?) { sampleClass = nil } func withoutInoutSampleForClass(var sampleClass: SampleClass?) { sampleClass = nil } var sampleClass: SampleClass? = SampleClass() // inout なし withoutInoutSampleForClass(sampleClass) sampleClass // => SampleClass のインスタンス // inout あり inoutSampleForClass(&sampleClass) sampleClass // => nil func inoutSample(a: inout Int) { a = 100 } func withoutInoutSample(var a: Int) { a = 100 } var varInt = 1 // inout なし withoutInoutSample(varInt) varInt // => 1 // inout あり inoutSample(&varInt) varInt // => 100 参考: In-Out Parameters internal アクセス修飾子の一種です。 同じモジュール内からアクセスできます。 アクセス修飾子をつけなかった場合、デフォルトで internal となります。 関連: public, private, open, fileprivate 参考: The Swift Programming Language (Swift 3) # Access Control let 定数を宣言します。 関連: var open アクセス修飾子の一種です。アクセス制限の種類の中で最もアクセス制限の緩い指定です。 外のモジュールからのアクセス、継承・オーバーライドが可能です。 性質上、この制限を指定できるのは class または オーバーライド可能な class member のみです。 public との違いは、継承・オーバーライドの許可範囲 (外のモジュールからの継承・オーバーライド可能性) です。 関連: public, private, internal, fileprivate 参考: The Swift Programming Language # Access Control operator 独自の演算子を定義します。 演算子として採用できる文字については規定があります。詳細は iOS Developer Library の Language Reference > Lexical Structure > Operators をご覧ください。 関連: prefix, postfix, infix prefix operator ☁ prefix func ☁ (a: inout Int) -> Int { a *= a return a } postfix operator *** // 複数文字列も可能 postfix func *** (a: inout Int) -> Int { a *= a return a } infix operator ☁ func ☁ (left: Int, right: Int) -> Int { return left + right } var hoge = 2 ☁hoge // => 4 hoge*** // => 16 1 ☁ 2 // => 3 precedencegroup 左右に被演算子をとる演算子の優先度グループを定義します。 higherThan で、どのグループより優先度が高いか指定できます。lowerThan では、他のモジュールのどのグループより優先度が低いか指定できます。associativity と assignment も指定できます。 演算子を定義する構文 infix operator <#operator name#> : <#precedence group name#> +++が先に評価される場合 precedencegroup GroupA { associativity: none } precedencegroup GroupB { higherThan: GroupA associativity: none } infix operator ***: GroupA infix operator +++: GroupB func *** (left: Int, right: Int) -> Int { return left * right } func +++ (left: Int, right: Int) -> Int { return left + right } 1 +++ 1 *** 0 // => 0 関連: operator, associativity, assignment private アクセス修飾子の一種です (型、変数、定数、function の公開範囲の指定) 最も制限の強いアクセス修飾子で、下記の条件を満たす場合のみにアクセスできます。 同じファイル内 同じ型 class A { private var privateVar = "" } extension A { func f() { // ✅OK print(privateVar) } } class B { let a = A() func f() { // ❗️error: 'privateVar' is inaccessible due to 'private' protection level a.privateVar } } 関連: public, internal, open protocol プロトコルを宣言します。 public アクセス修飾子の一種です (クラス、変数、定数、メソッド、関数の公開範囲の指定) 同じターゲット(モジュール)外からアクセス可能になります。open と異なり、継承は禁止されます。 主に、ライブラリなどで API として公開するものに対して指定します。 関連: private, internal, open static static 変数や static function を宣言します。 protocol での宣言時 (下記参照) は class func と class var も意味に含みます。 protocol SampleProtocol { static func staticFuncInProtocol() static var staticVarInProtocol: Int { get } } class Sample { // `static func staticFuncInProtocol()` can be implemented by `static func` or `class func` class func staticFuncInProtocol() {} // ditto class var staticVarInProtocol: Int { 1 } } struct 構造体を宣言します。 subscript クラスや構造体に [] を実装します。 Objective-C の場合についてはクラスに [], {} を実装するに書いてみました。 実装例 class SubscriptSample { var hoge: Any? subscript(index: Int) -> String { get { return "Int もらいました" } set { hoge = newValue } } subscript(index: String) -> String { get { return "String もらいました" } // setter なくても良いです } subscript(index: AnyObject?) -> String { return "何かもらいました" } subscript(x: Int, y: Int) -> String { return "\(x), \(y)" } subscript() -> String { return "nothing" } } 使用例 let subscriptSample = SubscriptSample() var optionalSample: Int? = 1; subscriptSample[3] // => "Int もらいました" subscriptSample["a"] // => "String もらいました" subscriptSample[nil] // => "何かもらいました" subscriptSample[optionalSample] // => "何かもらいました" subscriptSample[1, 2] // => "1, 2" subscriptSample[] // => "nothing" typealias 型の別名を宣言(*1)、または、associated type に対して型を指定(*2)します。 型の別名を宣言(*1) typealias IntAlias = Int typealias Point = (Int, Int) 付属型に対して型を指定(*2) protocol P { associatedtype T } struct S: P { typealias T = Any } 参考: The Swift Programming Language (Language Reference -> Declaration -> Type Alias Declaration) The Swift Programming Language (Language Reference -> Declaration -> Protocol Associated Type Declaration) associatedtype associated type (付属型) の宣言をします。 protocol SampleProtocol { associatedtype AssociatedType // 付属型を宣言します func sampleFunc(param :AssociatedType) -> AssociatedType } struct SampleStruct: SampleProtocol { func sampleFunc(param: Int) -> Int { // 付属型が Int であると決定されます return param + param } } 参考: The Swift Programming Language (Language Reference -> Declaration -> Type Alias Declaration) The Swift Programming Language (Language Reference -> Declaration -> Protocol Associated Type Declaration) var 変数を宣言します。 Keywords used in statements break switch 文やループから抜けます。 for, while の前にラベルをつけることで抜けるブロックを指定できます。 詳細: document の Control Flow -> Labeled Statements var i = 0 firstLoop: while true { print("first loop: \(i)") while true { i += 1 print("second loop: \(i)") switch i { case 5: print("break firstLoop") break firstLoop default: break } } } print("finish: \(i)") first loop: 0 second loop: 1 second loop: 2 second loop: 3 second loop: 4 second loop: 5 break firstLoop finish: 5 関連: continue, fallthrough case 列挙子リストの宣言、switch 文内で条件分岐、switch 文内以外で if, for との併用により、パターンマッチングができます。 let optionalSample: Int? = 1; let optionalArraySample: [Int?] = [1, 2, nil, 3] if case let x? = optionalSample { print("optionalSample: \(x)") } for case let x? in optionalArraySample { print("optionalArraySample: \(x)") } optionalSample: 1 optionalArraySample: 1 optionalArraySample: 2 optionalArraySample: 3 参考: Swift 2: Pattern Matching with “if case”, The Swift Programming Language (Patterns -> Optional Pattern) 関連: enum continue 次のループ処理へ移動します。 break と同様にラベルをつけることで移動するループ処理を指定することができます。 var i = 0 firstLoop: while true { print("first loop: \(i)") if i != 0 { break } while true { i += 1 print("second loop: \(i)") switch i { case 5: print("continue firstLoop") continue firstLoop default: break } } } print("finish: \(i)") first loop: 0 second loop: 1 second loop: 2 second loop: 3 second loop: 4 second loop: 5 continue firstLoop first loop: 5 finish: 5 関連: break, fallthrough default switch 文内の条件分岐で、case に当てはまらなかった場合の処理の宣言をします。 関連: switch, case defer スコープを抜ける際に実行する処理を記述します。 func deferSample() { defer { print("in defer") } print("end of scope") } deferSample() end of scope in defer 参考: The Swift Programming Language (Statements -> Defer Statement) The defer keyword in Swift 2: try/finally done right do スコープを作成します。 catchを繋げることで、スコープ内で発生した例外をcatch文で処理することができます。 関連: catch, try else 条件分岐で使用します。 また、guard文では文法上必須となります。 関連: if, guard fallthrough switch 文の中で、マッチした case 文の次の case 文または default 文内の処理を実行します。 (厳密には、fallthrough の所属している case ブロックの中から抜け、所属する case 文の次の case 文または default 文内の処理を実行します) 使用例 let a = 1 switch a { case 1: print("1") fallthrough case 2: print("2") default: print("default") } C言語の場合(上記と同等の処理) switch (a) { case 1: printf("1\n"); case 2: printf("2\n"); break; default: printf("default\n"); } 1 2 補足 下記の補足です。 厳密には、fallthrough の所属している case ブロックの中から抜け、所属する case 文の次の case 文または default 文内の処理を実行します。 下記のように、fallthrough が呼ばれると、即座に case ブロックから抜けます。 そのため、その下の print("1-2") は実行されません。 switch 1 { case 1: defer { print("defer") } // ブロック(スコープ) から抜ける様子を観察 print("1-1") if true { fallthrough } print("1-2") case 2: print("2") default: print("default") } 1-1 defer 2 for 繰り返し処理を記述できます。 for-in for i in 1...10 { // 処理 } for _ in 1...10 { // 処理 } 関連: in, while guard 変数または定数が条件に一致するか評価し、一致していない場合、続く else のブロックが実行されます。 このブロックの中では必ず以下のいずれかを使用し、guard が記述されているスコープを抜けなくてはいけません。 return break continue throw fallthrough fatalError 等の Never 型を返す関数の呼び出し func guardSample1(value: Int?) -> String { guard let value = value, value > 10 else { // この中では必ずスコープを抜ける処理を書きます return "in else block" } // ここからは value がアンラップされ、また、10 より大きいことが保証されます return "\(value)" } guardSample1(nil) // => in else block guardSample1(10) // => in else block guardSample1(100) // => 100 func guardSample2(a: String, b: Int?) -> String { // 複数の変数・定数の評価もできます guard let intValue = Int(a), let b = b else { return "in else block" } // ここからは b がアンラップされます return "\(intValue + b)" } guardSample2("a", b: 1) // => in else block guardSample2("1", b: 1) // => 2 guardSample2("1", b: nil) // => in else block 関連: return, break, continue, throw if 続く条件を評価し、一致した(true)場合ブロック内を実行します。 unwrap する場合にも使用します。この構文を Optional binding と言います。詳細はこちら ([Swift] Optional 型についてのまとめ Ver2) に解説されていて分かりやすかったです。 func ifLetSample(value: Int?) { if let a = value { type(of: value) // Optional<Int>.Type type(of: a) // Int.Type } } 関連: else, guard in 文脈によって下記のように意味が変わります。 クロージャのボディの開始箇所を表します for ~ in の形で取り出す要素の配列を指定します 関連: for repeat C言語等の言語における do {...} while(...) の do の役割です。 関連: while return 返り値を指定します。 返り値は下記の 2 パターンです。 要素が一つの場合はその要素 それ以外の場合は tuple func sample() { return } var a = sample() // -> () 参考: Remove @noreturn attribute and introduce an empty Never type switch 条件分岐を行います。 switch (1, "a") { case (1, "b"): print("1, b") case (1, _): print("1, _") // ここがマッチします。 (_ はワイルドカード) case (1, "a"): print("1, a") // 上がマッチするので評価されません } 関連: case where マッチングの条件を追加します。 while 下記の2種類の繰り返し構文を記述できます。 while condition { statements } repeat { statements } while condition 関連: repeat Keywords used in expressions and types as 大きく分けて、2 種類の役割があります。 * キャスト class A {} let anyObj: AnyObject = A() let a = anyObj as! A // AnyObject から A にキャスト 型を明示すること let v = 1 as Double Any すべての型のインスタンス (関数型も含む) を表現します。 実態は空の protocol です。 参考: Type Casting for Any and AnyObject Import Objective-C id as Swift Any type catch 例外が投げられた際にブロック内が実行されます。 false 真偽値リテラルの一種で、偽を表します。 is ある型またはあるプロコトルを実装した型として振る舞えるかどうかを検査します。 1 is Int // -> true (1, 1) is AnyObject // -> false (1, 1) is (Int, Int) // -> true // プロトコルの検査 protocol SampleProtocol { } class SampleClass: SampleProtocol { } let sampleClassInstance = SampleClass() sampleClassInstance is SampleClass // true sampleClassInstance is SampleProtocol // true nil nil リテラルを表します。 Optional.None == nil // -> true rethrows 引数にとったクロージャが投げた例外を呼び出し元に対して更に投げます。 func sample(callback: () throws -> Int) rethrows { try callback() } super 親クラスを表します。 self インスタンスメソッド内などで単独で使用した場合、インスタンス自身を返します expression (式) に対して呼び出した場合、式が評価された値が返ります type (型) に対して呼び出した場合、自身の型が返ります インスタンスメソッド内などで単独で使用した場合、インスタンス自身を返します class Sample { var a: Int? func sampleMethod() -> Sample { a = 1 return self // 自身 (playground 上では Sample と見えますが、プロパティ a が変更されているので上で作成したインスタンスだと確認できます) } } expression (式) に対して呼び出した場合、式がそのまま返ります <#expression#>.self (1 + 1).self の返り値は (1 + 1) という式と同等です。 式がそのまま返るとは (1 + 1).self // (1 + 1) 余談 (1 + 1).self // 2 as Int ではない // 証明 (1 + 1).self + 1.0 // OK (1 + 1) + 1.0 // OK let exp = 1 + 1 // 2 as Int exp + 1.0 // Error (type mismatch) type (型) に対して呼び出した場合、自身の型が返ります <#type#>.self class Sample { } Sample.self // -> Sample.Type Sample.self.init() // -> Sample のインスタンス (= Sample.self は自身の型を返しています) 参考: The Swift Programming Language (Language Reference -> Expressions -> Postfix Self Expression) Self 自身の型を返します。 throw 例外を投げます。 throws メソッド、関数の宣言部に書き、例外が投げられる可能性があることを示します。 true 真偽値リテラルの一種で、真を表します。 try 例外が投げられる可能性のある関数・メソッドを実行します。 Keywords that begin with a number sign (#) #available OS 及びそのバージョンによる条件を表現します。 if #available(iOS 10.0, *) { print("iOS10 or later") } #colorLiteral 下記の構文で色を表現するリテラルを記述できます。 #colorLiteral­(­red­:­ <# expression #>­, ­green­:­ <# expression #>­,­ blue­:­ <# expression #>­,­ alpha­: <# expression #>­)­ 型を明示しない場合は、AppKit モジュールをインポートした場合は NSColor 型、UIKit モジュールをインポートした場合は UIColor 型として解釈されます。 #column #column が評価された場所の列番号 (Int) #if, #else, #elseif, #endif コンパイル時に指定のコードを含めるか否かを制御します。 条件例 #if <# Custom Flag に設定した名前 (設定せずにコンパイルすると無視される) #> print("DEBUG") #endif #if swift(>=3.0) print("Swift3.0") #endif #if arch(arm64) print("arm64") #endif #if os(OSX) print("OSX") #endif Swiftのバージョンを条件に制御する例 #if swift(>=3.0) print(">=Swift3.0") #elseif swift(>=2.0) print(">=Swift2.0") #else print("else") #endif 参考: * The Swift Programming Language > Conditional Compilation Block * CONDITIONAL COMPILATION #error 記載した行に対するエラーを compile 時に発生させます。 関連: #warning #file #file が評価された場所のパス(String)を返します。 ただし、今後の Swift のバージョンで動作の変更があるため #fileID か #filePath への置き換えが推奨されています。 Swift5.5時点では #filePath と同じ動作をしますが、今後のバージョンでは #fileID の動作へ変更されます。 // sample project の中の sampleApp.swift で使った場合 print("#file", #file) print("#filePath", #filePath) print("#fileID", #fileID) 出力 #file /Users/ezura/workspace/article/swift5/sample/sampleApp.swift #filePath /Users/ezura/workspace/article/swift5/sample/sampleApp.swift #fileID sample/sampleApp.swift 関連: #filePath, #fileID 参考: the swift programming language > Expressions > Literal Expression #fileID #fileID が評価されたファイルとそのモジュール名を module名/file名 の形式で返します(String型)。 // sample project の中の sampleApp.swift で使った場合 print("#fileID", #fileID) 出力 #fileID sample/sampleApp.swift 関連: #filePath, #file 参考: the swift programming language > Expressions > Literal Expression #filePath #filePath が評価された場所のパス(String)を返します。 // sample project 内の sampleApp.swift で使った場合 print("#filePath", #filePath) 出力 #filePath /Users/ezura/workspace/article/swift5/sample/sampleApp.swift 関連: #file, #fileID 参考: the swift programming language > Expressions > Literal Expression #fileLiteral 下記の構文でファイルリテラルを記述できます。 #fileLiteral(resourceName: "<# ファイル名 #>") 型を明示しない場合は URL 型として解釈されます。(Foundation モジュールがインポートされている場合のみ) また、ファイルが取得できなかった場合、実行時エラーとなります。 参考: * Modernizing Playground Literals * swift/test/Sema/object_literals_osx.swift #function #function が評価された場所の関数・メソッドの名前 (String) を表現します。 #imageLiteral 下記の構文で画像リソースを表現するリテラルを記述できます。 #imageLiteral(resourceName: "<# ファイル名 #>") 型を明示しない場合は、AppKit モジュールをインポートした場合は NSImage 型、UIKit モジュールをインポートした場合は UIImage 型として解釈されます。 #keypath key や keypath (string literal) を生成します。 class Object: NSObject { let value = 1 var matryoshka: Object? } #keyPath(Object.value) // "value" #keyPath(Object.matryoshka.value) // "matryoshka.value" let object = Object() object.matryoshka = Object() object.value(forKey: #keyPath(Object.value)) // 1 object.value(forKeyPath: #keyPath(Object.matryoshka.value)) // 1 関連: * #keypath の proposal * Swift3ではKVOにkeyPath()式を使っていくのが便利 #line #line が評価された場所の行番号 (Int) #selector Selector を生成します。 Swift3 から、以前の機能に加えて Objective-C で記述された class のプロパティの getter, setter メソッドの Selector を \#selector を用いて生成できるようになりました。(以前は \#selector が対応していなかったため、文字列で指定していました) @interface ViewController : UIViewController @property(assign) int num; @end #selector(getter: ViewController.num) #selector(setter: ViewController.num) 参考: * Using Swift with Cocoa and Objective-C (Swift 3) > Interacting with Objective-C APIs * Hannibal #selector #sourceLocation #line, #file, #filePath, #fileID の値を操作します。 値の変更: #sourceLocation(file: file path, line: line number) 変更をリセット: #sourceLocation() ただし、Playground 上ではリセット後の line の値が不正になるバグがあります。(Xcode13.1時点で確認) 参考:The Swift Programming Language > Line Control Statement #warning 記載した行に対する warning を compile 時に発生させます。 関連: #error #dsohandle #line, #file, #function と同様に、書かれている場所に関する情報を表すキーワードです。 自身が書かれているライブラリがロードされている場所のアドレスを表します。(アドレスなので、他のキーワードと違い、UnsafePointer型です) provides an UnsafePointer to the current dynamic shared object (.dylib or .so file) 引用: Modernizing Swift's Debugging Identifiers 特定の文脈でのみ予約語として使用 assignment 左右に値を取る演算子を宣言した際に、Optional Chaining 評価の一連の流れで演算するかを指定します。 true を指定すると、Optional Chaining 評価の一環として演算を行おうとします。false は既定の動作で、Optional Chaining の評価が終わってから、その評価結果と演算します。 オプショナルチェイニングでの畳み込み precedencegroup OperationFoldedIntoOptionalChaining { assignment: true } precedencegroup OperationOutsideOfOptionalChaining { assignment: false } infix operator ~~ : OperationFoldedIntoOptionalChaining infix operator ~^ : OperationOutsideOfOptionalChaining func ~~ (left: Int, right: Int) -> Int { print("~~") return left + right } func ~^ (left: Int?, right: Int) -> Int { print("~^") return left?.advanced(by: right) ?? -1 } let values = nil as [Int]? values?.count ~~ 5 // => nil values?.count ~^ 5 // => -1 /* ~~ 演算は、 最初に `values` の nil 判定、nil でなければ `.count ~~ 5` を評価 values.map { $0.count ~~ 5 } と同等。この例では `~~` 演算は実行されない。 ~^ 演算は、最初に `values?.count` を評価、続いて `その結果 ~^ 5` を評価 普通に `(values?.count) ~^ 5` としたのと同等。演算子の既定の動作。 */ 関連: infix, operator, precedencegroup associativity 左右に値を取る優先度グループを宣言した際に、結合方向を指定します。 指定できる値は下記の 3 種類です。 left right none: 結合方向を指定しないため、同じグループの演算子同士を並べて使用することができなくなります。 優先度グループを定義する構文 precedencegroup <#precedence group name#> { higherThan: <#lower group name#> associativity: <#left | right | none#> } 同優先度、結合方向 none precedencegroup MyAddition { associativity: none } infix operator +++ : MyAddition infix operator --- : MyAddition func +++ (left: Int, right: Int) -> Int { return left + right } func --- (left: Int, right: Int) -> Int { return left - right } 1 +++ 1 --- 1 /* error: adjacent operators are in non-associative precedence group 'MyAddition' 1 +++ 1 --- 1 ^ ~~~ */ 関連: left, right, none, operator, precedencegroup convenience init の前に記述することで、convenience initializer を宣言します。 関連: init dynamic Objective-C のランタイムを使用して値にアクセスします。 構文 dynamic <#var | let #> <# name #> 詳細 Objective-C runtime と pure Swift では、呼び出す処理を決定する方法 (method dispatch) が異なります。 Objective-C runtime の場合、実行時に Class に対して問い合わせて実行すべき処理を探していきます (message passing 方式)。 例えば、instance.methodA() を実行するとき、instance の持つ Class の情報に対して、"methodA" (呼び出したい function 名) があるか検索します。無かったらその Class の親 Class に対して問い合わせる、なかったら更にその親 Class に、といった処理を実行時に行います。 一方、Swift ではコンパイル時に「型に対してどんな method があるか」という情報を持つ table を作ります。直接呼び出す場合もありますが、継承関係がある場合など、実行時にしか呼び出す処理が決まらない場合はこの table を参照して処理を決定します。重要なのは、この table はコンパイル時に作られてから後で変更できないということです。 Objective-C の場合、Class の情報を書き換えることができます。つまり、実行時に method を増やしたり、method 名を変えたり、処理を入れ替えたりできます。(method swizzling と呼ばれる操作です) このような Objective-C の機能を使いたい場合、dynamic を指定することで、Swift の method table 上には載らず、Objective-C の機構を使って処理するようになります。 didSet Stored プロパティまたは変数の値が変更された際の処理を宣言します。 final 継承、オーバーライドを不可にします。 get 文脈によって意味が異なります。 computed property 内: プロパティにアクセスした際の処理 class A { var value: Int { get { return 1 } } } protocol 内で宣言した場合: 値が取得可能であることの宣言 protocol Sample { var value: Int { get } } class A: Sample { let value = 1 } class B: Sample { var value: Int { return 1 } } infix 左右に被演算子をとる演算子の処理を定義します。 infix operator ☁ func ☁ (left: Int, right: Int) -> Int { return left + right } 1 ☁ 2 // => 3 関連: operator indirect 列挙体を列挙子の中で再帰的に使えるようになります。つまり、Json のような入子構造を表現できます。 enum SampleEnum { case num(Int) indirect case indirectNum(SampleEnum) } または swift indirect enum SampleEnum { case num(Int) case IndirectNum(SampleEnum) } // 入子にできる SampleEnum.indirectNum( .indirectNum( .indirectNum( .num(1) ) ) ) 詳細: indirect を指定すると associated value を間接指定するようになります。 間接指定しない場合、associated value 分のメモリサイズが確定できないため、その列挙体のために確保するメモリサイズも決まりません。 間接指定する場合、associated value の場所(アドレス)を保持することになるので列挙体のサイズが確定できるようになります。 詳細: Swift Programming Language (Enumerations -> Recursive Enumerations) lazy 変数を初期化する際の値を遅延評価します。 通常 property は instance の作成時に値が評価されますが、下記のように lazy を指定すると該当の property に初めてアクセスした際に値が評価されます。 class Sample { lazy var lazyValue = Date() let defaultValue = Date() } let sample = Sample() sleep(10) // lazyValue 2021-11-18 05:14:47 +0000 print("lazyValue", sample.lazyValue) // defaultValue 2021-11-18 05:14:37 +0000 print("defaultValue", sample.defaultValue) 使い所としては、 1. 該当のpropertyに入る値の生成にコストがかかるため、実際に使うときまでその生成を遅延させたい場合 1. init 時に値が決まらないため、後で値を代入したい場合 があります。 2 番目に関しては少々トリッキーなので好みが分かれますが、下記のように IUO を避ける実装ができます。 class Sample { var iuoValue: String! // init 時に値が決まらないため IUO にしている // ↑のようなIUO を使うのを避ける実装。最初は`Never`(全ての型のサブタイプ扱い)を返すclosure を入れておき、このpropertyにアクセスする前には値を入れる前提。 lazy var lazyEval: String = { preconditionFailure() }() } left 演算子を定義した際に、左結合を指定します。 詳細: このページの associativity 項 関連: associativity, operator, right, none mutating 値型のオブジェクトにおいて、自身または自身のプロパティを書き換えるインスタンスメソッドに対して宣言する。 列挙体の場合 enum SampleEnum { case A, B, C case a, b, c mutating func upperCase() { switch self { case .a: self = .A case .b: self = .B case .c: self = .C default: break } } } 構造体の場合 struct SampleStruct { var x = 0 mutating func modifyX(x: Int) { self.x = x } mutating func reset() { self = SampleStruct() } } none 演算子を定義した際に、結合方向を指定しません。 詳細: このページの associativity 項 関連: associativity, operator, right, left nonisolated [Swift 5.5~] Actorの宣言部で使用します。 Actor の基本思想は「ミュータブルなデータ(状態)を actor 外から隔離(isolate)しデータの同期状態を守る」ことです。そのため、actor の method や mutable な property はデフォルトで隔離(isolate)対象となっています。 その対象としないことを指定する場合に nonisolated を付与します。 actor SampleActor { nonisolated func nonisolatedFunc() {} nonisolated var nonisolatedComputedProperty: String { "computedProperty getter" } } ミュータブルなデータ(状態)の保護状態を守るため、隔離対象外(nonisolated)の method/computed property は隔離対象の property や method へのアクセスが禁止されます。これに違反するとコンパイルエラーになります。 関連: actor nonmutating 値型のインスタンスメソッドが自身に変更を加えないことを宣言します。 使い所: computed property 内で定義する set はデフォルトで mutating 指定になります。iOS の API 内では下記のように setter の mutaing を無効にするために使用しています。 var value: Value { get nonmutating set } optional プロトコルで指定されたメソッドやプロパティの実装を任意とします。 override 親クラスのメソッドやプロパティをオーバーライドする際に宣言します。 postfix 独自の後置演算子を定義します。 postfix operator *** postfix func *** (a: inout Int) -> Int { a *= a return a } var hoge = 4 hoge*** // => 16 関連: prefix, infix, operator prefix 独自の前置演算子を定義します。 prefix operator *** prefix func *** (a: inout Int) -> Int { a *= a return a } var hoge = 4 ***hoge // => 16 関連: postfix, infix, operator Protocol Protocol のメタタイプを取得します。 let protocolMetatype: SampleProtocol.Protocol = SampleProtocol.self 関連: Type required サブクラスにイニシャライザのオーバーライドを強制します。 また、サブクラスでそのイニシャライザをオーバーライドする際には override ではなく、required を指定します。 right 演算子を定義した際に、右結合を指定します。 詳細: このページの associativity 項 関連: associativity, operator, left, none set 文脈によって意味が異なります。 computed property 内: プロパティにアクセスした際の処理 class A { var value: Int { get { return 1 } set {} } } protocol 内で宣言した場合: 値を受け渡し可能であることを宣言 protocol Sample { var value: Int { get set } } class A: Sample { var value = 1 } class B: Sample { var value: Int { get { return 1 } set {} } } Type クラス、構造体、列挙体のメタタイプを取得します。 class Sample { required init() {} } let metatype: Sample.Type = Sample.self let instance = metatype.init() 関連: Protocol unowned 弱参照の変数を宣言します。 weak と違い、参照している値よりも生存期間が短い、つまり、アクセスした際に参照先が解放されていない (nil となっていない) ことを前提とします。値が破棄されているとランタイムエラーになります。 // unowned = unowned(safe) unowned var safe: AnyObject // capture list 内で指定することが多いです { [unowned a] in /* ... */ } 関連: weak, unowned(safe), unowned(unsafe) unowned(safe) unowned へ修飾することで参照の動作を指定できます。 unowned(何も指定しなかった場合)は unowned(safe) 扱いとなります。 関連する指定に unowned(unsafe) があります。 // unowned = unowned(safe) unowned(safe) var safe: AnyObject // capture list 内で指定することが多いです { [unowned(safe) a] in /* ... */ } 関連: unowned(unsafe), unowned unowned(unsafe) unowned へ修飾することで参照の動作を指定できます。 unowned または unowned(safe) と違い、参照先が解放された際にアクセスしても nil と評価されません。つまり、解放されている場所を指し続けているためメモリに対して安全なアクセスをしません。 Objective-C の __unsafe_unretained と同様の動作です。 // like `__unsafe_unretained` unowned(unsafe) var unsafe: AnyObject { [unowned(unsafe) a] in /* ... */ } 関連: unowned(safe), unowned weak 弱参照の変数を宣言します。 unowned と違い、参照している値が nil になることを許容します。 willSet stored property または変数への代入が実行される前の処理を記述します。 関連: didSet その他 _ ワイルドカード switch (1, "a") { case (1, "b"): print("1, b") case (1, _): print("1, _") // ここがマッチします。 (_ はワイルドカード) } 関連: case 引数名の省略 引数が必要なinit class Sample { var a: Int init(param: Int) { a = param } } let sample = Sample(param: 1) 引数が不要なinit class Sample { var a: Int init(_ param: Int) { a = param } } let sample = Sample(1) 値を捨てる let a: Int (a, _) = (0, 1) a // -> 0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReSwiftでReduxアーキテクチャのサンプルを作る

概要 Reduxアーキテクチャでカウンターアプリを作ります。 Reduxを体験することが目的です。 動作は下のような感じです。 全体のソースコードはこちらです。 Reduxアーキテクチャの登場人物 Reduxの登場人物を紹介します。 View 画面表示・ユーザーイベントをハンドリングします。 State アプリケーションの状態を管理します。 Action Stateをどのように変更するか表現します。 Reducer StateとActionから新たなStateを生み出します。 Store StateとReducerを内包します。 Actionのディスパッチを担当します。 Reduxの流れ 主に以下の流れでアプリケーションの状態を更新します。 1. Viewでイベントが発生する 2. イベントをトリガーにStoreがActionをディスパッチする 3. ReducerがディスパッチされたActionと既存のStateから新しいStateを生成する 4. Stateを監視しているViewが更新される カウンターアプリを作る Reduxを使った実装を体験してみます。 プロジェクトを作成する Xcodeで新規プロジェクトを作ります。 その後、Swift Package MangerでReSwiftをインストールします。 Stateを実装する カウンターアプリなので数字を状態管理します。 State/CounterState.swift struct CounterState { var number: Int = 0 } Actionを実装する 数字を一つ増やすActionと数字を一つ減らすActionを準備します。 State/CounterState.swift extension CounterState { enum Action: ReSwift.Action { case increment case decrement } } Reducerを実装する Actionと既存のStateから新しいStateを生成します。 CounterState.swift extension CounterState { ... static func reducer(action: ReSwift.Action, state: CounterState?) -> CounterState { var state = state ?? CounterState() guard let action = action as? CounterState.Action else { return state } switch action { case .increment: state.number += 1 case .decrement: state.number -= 1 } return state } } Stateを束ねるAppStateを実装する Stateのルートに位置するStateを実装します。 AppStateと名づけることが多いです。 AppState.swift struct AppState { var counterState = CounterState() } AppStateと対応するReducerを実装する AppStateに対応するReducerを実装します。 発生したActionを下位のReducerに渡します。 今回の場合は appReducer -> CounterState.reducerとなります。 AppState.swift func appReducer(action: ReSwift.Action, state: AppState?) -> AppState { var state = state ?? AppState() state.counterState = CounterState.reducer(action: action, state: state.counterState) return state } Storeを実装する Storeは以下の特徴を持ちます。 1. アプリケーションに一つ 2. State, Reducerを保持する 3. Actionのディスパッチを担当する AppDelegateで生成しましょう。 AppDelegate.swift import UIKit import ReSwift let appStore = ReSwift.Store<AppState>(reducer: appReducer, state: nil) ... ボタンタップ時にActionをディスパッチする ボタンタップ時にディスパッチして、新しいStateが生成されるようにします。 また、StoreがViewのイベントを監視できるようにします。 ViewController.swift import UIKit import ReSwift class ViewController: UIViewController, StoreSubscriber { @IBOutlet weak var label: UILabel! override func viewDidLoad() { super.viewDidLoad() appStore.subscribe(self) } func newState(state: AppState) { label.text = String(state.counterState.number) } @IBAction func tapCountUpButton(_ sender: UIButton) { appStore.dispatchFunction(CounterState.Action.increment) } @IBAction func tapCountDownbutton(_ sender: UIButton) { appStore.dispatch(CounterState.Action.decrement) } } Reduxのデータフローを実現できました。 1. Viewでイベントが発生する 2. StoreがActionをディスパッチする 3. ReducerがActionを解析して、新しいStateを生成する 4. 新しいStateを元にViewが更新される ソースコードはこちらです。 参考リンク
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Blender で作成したアニメーション付きモデルを ARKit で配置して遊ぼう - 3Dデータ出力編

Blender で作成したアニメーション付きモデルを ARKit で配置する方法を解説していきます。 今回は 3Dデータを Xcode で使えるように出力していきます。 アニメーション出力 Xcode で使うためには dae ファイルにする必要があります。Blender のエクスポートにも dae がきちんと用意されています。 しかし、デフォルトのエクスポート使うと大体ズレが生じたりとおかしなことになります。(Blender 2.8時点) なので、 Better Collada というアドオンを使用しましょう。 Better Colladaの導入 まずは Better Collada の github に飛んで、Clone or download から Download ZIP を選択しダウンロードします。 ZIPファイルを解凍し、中の io_scene_dae というフォルダを圧縮してZIPにします。 Blenderを起動し、左上メニューの 編集 > プリファレンス > アドオン に行き、右上のインストールボタンを選択します。そして、先ほど作ったZIPファイルを選択してください。 すると、公式アドオンにBetter Collada Exporter というアドオンが追加されます(検索すればすぐ見つかる)ので、チェックを入れて有効化してください。 これで導入完了です。 Better Colladaでの出力 Blenderでモデルを選択しておき、左上メニューの ファイル > エクスポート > Better Collada (.dae) を選択します。 出てきた画面の右側に出力のオプションが表示されているはずなので、すでにされているチェックに加え Export Animation と、選択したオブジェクト にチェックをつけてエクスポートしましょう。詳しい設定は下記の画像を参照してください。 これで出力完了です。 この出力をするとマテリアルデータがうまく出力されず半透明のモデルとなります。 次で再び色付けするので問題ないです。 3Dデータへの色付け 先ほど出力したdaeファイルをXcodeで開いてください。 モデルは理工展のマスコットキャラクターの矢口くんになります。 すると、このように半透明なモデルが出現します。 モデルを選択し、マテリアルデータをみてみましょう。右上のメニューの球のマークを押すとマテリアルデータが確認できます。 Emision(発光), Transparent(透過)が勝手に変更されていることがあるので調節しましょう。 その後、Diffuse を開き色を適用しましょう。 これを全てのマテリアルに対して適用すれば3Dデータは完成です。お疲れ様でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む