【iOS、Swift】コードスニペット（ Xcode Swift Code Snippets）

## Xcode Swift Code Snippets

Swiftに限らず、開発する際に同じ処理をなんども一から書いたり、調べることからスタートするのは面倒です。

そこでSwiftのコードスニペットをさがしたところ、
[Xcode Swift Code Snippets
](https://github.com/burczyk/XcodeSwiftSnippets)を見つけました。

>Inspiration
I was inspired by Matt Thompson's Xcode-Snippets article and GitHub repo but I haven't found anything similar for Swift.
I also used that thing in swift and SwiftSingleton as a sample implementations.

あの有名なMattさんのObjective-Cのスニペットに感化されSwift版を作成したそうです。

本記事ではどんなスニペットが用意されているかをまとめました。

導入方法などは[こちら](https://github.com/burczyk/XcodeSwiftSnippets)

### swift-documentdirectory 

ドキュメントディレクトリへのパスを取得できます。

```.swift

        if let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first {
            let documentsDirectoryURL = NSURL.fileURL(withPath: path)
        }
```

🔽iOSのデータ保存先について

```

iOS App
    ├── Documents
    ├── Library
    │   ├── Caches
    │   └── Preferences
    └── tmp

Documents/
・ユーザが作成したデータ（テキスト、写真）、再作成が不可能なデータなど重要なデータを保存する。
・ユーザに見せたくないファイル(設定ファイルなど)はここに置かない。
・iTunes、iCloud バックアップ対象。

Library/
・ユーザのデータファイル以外を保存するディレクトリ。自分でサブディレクトリを用意して保存する。
・ユーザに見せたくないファイルはここに置く。
・iTunes、iCloud バックアップ対象（Caches ディレクトリを除く）。

Library/Caches/
・一時的なデータを保存するディレクトリ。システムが自動削除する可能性がある。
・後から再度ダウンロードして復旧可能なデータを置くこと。
・iTunes、iCloud バックアップ対象外。

Library/Preferences/
・アプリの設定を保存するディレクトリ。NSUserDefaultsのデータはここに保存される。

tmp/
・一時的なデータを保存するディレクトリ。アプリが動作してないときにシステムが自動削除する可能性がある。
・iTunes、iCloud バックアップ対象外。
・使い終わったらアプリ側で削除して容量削減に務める。

```

### swift-dispatchafter

遅延実行処理。一定時間後に処理を行う。

```.swift
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#seconds#>)) {
            
        }
```

### swift-dispatchasync
       
非同期処理

```.swift
        DispatchQueue.global(qos: .default).async {
            // グローバルキューで実行される
            DispatchQueue.main.async {
                // メインキューで実行される
            }
        }
```

### swift-dispatchmain

メインキューで非同期処理を実行

```.swift
        DispatchQueue.main.async(execute: { () -> Void in
            // 処理
        })
```

### swift-nslocalizedstring

多言語対応設定

```.swift
    NSLocalizedString(<#key#>, comment: <#comment#>)
```

### swift-closuretypealias

引数、戻り値をもつ、typealiasを設定したクロージャ。

引数に複雑な型のクロージャを取る関数の定義は読みにくいです。
typealiasキーワードを用いることで　可読性が高まります。

``` .swift
    typealias <#ClosureName#> = (_ <#arguments#>: <#type#>) -> <#return value#>
```


### swift-closureoptionaltypealias

引数、戻り値をもつ、typealiasを設定したオプショナルなクロージャ。

```.swift
    typealias <#ClosureName#> = ((_ <#arguments#>: <#type#>) -> <#return value#>)?
```

### swift-weak

クロージャ内で```[weak self]```を使用する。

```.swift
        [weak self] in
        guard let strongSelf = self else { return }
```

### swift-uitableviewdelegate

UITableViewDelegate

```.swift
    //MARK: UITableViewDelegate
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        
    }
    
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        
    }
    
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        
    }
```

### swift-uitableviewdatasource

UITableViewDataSource

```.swift
    //MARK: UITableViewDataSource
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return <#numberOfSections#>
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return <#numberOfRows#>
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: <#identifier#>, for: indexPath)
        configureCell(cell: cell, forRowAt: indexPath)
        return cell
    }
    
    func configureCell(cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        
    }
```

### swift-forin

パターンマッチ。
コレクション型を任意のオブジェクトへキャストし、```for-in```文を回す。

```.swift
        for case let <#object#> in <#collection#> as [<#Type#>] {
            
        }
```

🔽使用例

```.swift
        let sampleDict = ["key1": "one", "key2": "two"]
        
        for case let (key, "one") in sampleDict as [String: String] {
            print(key)
        }

// 出力結果
// key1
```

### swift-ibo

IBOutlet

```.swift
    @IBOutlet weak var <# OutletName #>: <# OutletType #>!
```

### swift-sortarrayofstrings

ロケールに対応した配列のソート

```.swift
        <#arrayOfStrings#>.sorted { (a, b) -> Bool in
            let comparisonResult = a.compare(b, options: [], range: a.range(of: a), locale: Locale.current)
            // 右辺を変更することで、昇順・降順を設定する
            return comparisonResult == .orderedSame
        }
```

🔽使用例

```.swift
        var testArray = ["d", "b", "c", "a"]
        testArray = testArray.sorted { (a, b) -> Bool in
            let comparisonResult = a.compare(b, options: [], range: a.range(of: a), locale: Locale.current)
            return comparisonResult == .orderedAscending
        }
        print(testArray)
```

### swift-urlsession-datatask

シンプルなHTTPリクエスト処理

```.swift
let session = URLSession.shared
        let task = session.dataTask(with: <#request#>){ (data, response, error) in
            <#code#>
        }
        task.resume()
```

### swift-urlsession-delegate

デリゲートを伴うHTTPリクエスト処理

```.swift
    let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
    let task = session.dataTask(with: <#request#>){ (data, response, error) in
        <#code#>
    }
    task.resume()
```

### swift-uiviewcontrollerlifecycle

UIViewControllerのライフサイクル

```.swift
    //MARK: UIViewController lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
    }
```

### swift-uicollectionviewdelegate

UICollectionViewDelegate 処理

```.swift
    //MARK: UICollectionViewDelegate
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
    }
    
    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        
    }
```

### swift-uicollectionviewdatasource

UICollectionViewDataSource

```.swift
    //MARK: UICollectionViewDataSource
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return <#numberOfSections#>
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // TODO:- Required Method
        return <#numberOfItems#>
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: <#identifier#>, for: indexPath)
        configureCell(cell: cell, forItemAt: indexPath)
        // TODO:- Required Method
        return cell
    }
    
    func configureCell(cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let view = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: <#identifier#>, for: indexPath)
        return view
    }
```

### swift-uiremotenotification

リモート通知機能における、登録処理とハンドリングメソッド

```.swift
    //MARK: Remote Notifications
    
    // Move this line somewhere where your app starts
    //    UIApplication.shared.registerForRemoteNotifications()
    
    // Ask user for allowed notification types
    //      let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
    //      UIApplication.shared.registerUserNotificationSettings(settings)
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("Successfully egistered for Remote Notifications with token: \(deviceToken)")
    }
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Registration for Remote Notifications failed with error: \(error)")
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
        
    }
```

### swift-mark

マーク記述を追記して、コードをセクションごとに分ける

```.swift
    // MARK: - <#Section#>
```

### swift-didbecomeactive

アプリケーションがアクティブになった時のオブザーバーを設定

```.swift
NotificationCenter.default.addObserver(self, selector: #selector(<#selector#>), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
```

### swift-mail

MFMailComposeViewController
メーラーの起動・送信処理

```.swift
    //MARK: MFMailComposeViewController
    
    func presentModalMailComposerViewController(animated: Bool) {
        if MFMailComposeViewController.canSendMail() {
            let mailComposeVC = MFMailComposeViewController()
            mailComposeVC.delegate = <#delegate#>
            
            mailComposeVC.setSubject(<#subject#>)
            mailComposeVC.setMessageBody(<#body#>, isHTML: true)
            mailComposeVC.setToRecipients([<#recipients#>])
            
            present(mailComposeVC, animated: animated, completion: nil)
        } else {
            let alert = UIAlertController(title: NSLocalizedString("Error", value: "Error", comment: ""),
                                          message: NSLocalizedString("Your device doesn't support Mail messaging", value: "Your device doesn't support Mail messaging", comment: ""),
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel"), style: .cancel, handler: nil))
            present(alert, animated: true, completion: nil)
        }
    }
    
    //MARK: MFMailComposeViewControllerDelegate
    
    func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
        
        if error != nil {
            print("Error: \(error)")
        }
        
        dismiss(animated: true)
    }
```

### swift-iba

IBActionを設定

```.swift
    @IBAction func <# FuncName #>(_ sender: AnyObject) {
        <# code #>
    }
```

### swift-singleton

Singletonパターン

```.swift
    static let shared : <#SingletonClass#> = <#SingletonClass#>()
```

### swift-uiapplicationdelegate

アプリのライフサイクル

```.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        return true
    }
    
    func applicationWillResignActive(_ application: UIApplication) {
        
    }
    
    func applicationDidBecomeActive(_ application: UIApplication) {
        
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        
    }
    
    func applicationWillTerminate(_ application: UIApplication) {
        
    }
```

### swift-elegantmark

マークをつける

```.swift
    ////////////////////////////////////////////////////////////////
    //MARK:-
    //MARK:<#Section#>
    //MARK:-
    ////////////////////////////////////////////////////////////////
```