LoginSignup
5
3

More than 5 years have passed since last update.

SwiftのExtensionについてとよく使うもの(一部)

Last updated at Posted at 2019-03-11

1.はじめに

プロジェックトをまたいで使用するExtensionがいくつかあったので一部ですが、備忘録兼ねて載せます。
ちょっと前に使っていたものがあるので、もっと別の書き方は色々あると思います。参考程度に見ていただけたらと思います。

2.SwiftでのExtension(クラス拡張)とは?

まずはじめにざっと概要だけ記載します。
Extensionとは、読んで字のごとく既存のクラスやEnum、IntやDoubleなどのデータ型にプロパティやメソッドを追加できる機能です。
(今回はprotocol拡張やprotocol適合のExtensionについては説明省きます...)

3.クラス拡張するときに気をつけている点

※僕が注意している点ですが、全て基本的なことですし、プロジェクトによって異なる場合もあります。

1. クラス拡張を記述するファイル名はシンプルなものにする(拡張するクラス名+.swiftなど)
基本的にクラス拡張を定義するときはクラス名+.swiftとしてExtensionディレクトリに記述します。
少しだからとViewControllerなどのファイル内にクラス拡張を定義してしまうと後々追加する場合などに困ります。

2. 標準クラスやデータ型などに追加するものは、プロジェクトとの依存を避ける。
プロジェクトで作成したカスタムクラスなどはその限りではありませんが、基本的にプロジェクトをまたいでも使用できるようにするべきだと考えています。Extensionのみリポジトリに置いておいて、新規プロジェクトで必要なExtensionだけプルして使うようにしています。

3. 適切なクラスにクラス拡張を追加する
StringとDateや、Bool、その他必要な場合はありだと思いますが、
例えば、極端な話ですが、Stringクラスに定義した関数の戻り値をUIViewControllerにしたりするのは基本的にNGかなと思いますし、大規模な内部処理や、担いきれない責務をクラス拡張として追加するのは、あれかなと思います。


以上つらつらと書きましたが、以下よく使っているExtensionです。

4.よく使うExtentions

1.ネットワークインジケーターの表示・非表示

非同期通信でも直列だと問題ないかもしれませんが、並列の場合、普通にやろうとするとおかしなことになるので作りました。んーもっと別のいい方法はあると思います。


extension UIApplication {

    private static var networkActivityIndicatorCount: Int = 0

    static func showNetworkActivityIndicator() {
        DispatchQueue.main.async(execute: {
            UIApplication.shared.isNetworkActivityIndicatorVisible = true
            networkActivityIndicatorCount += 1
        })
    }

    static func hideNetworkActivityIndicator(_ force: Bool = false) {
        networkActivityIndicatorCount -= 1
        if networkActivityIndicatorCount <= 0 || force {
            DispatchQueue.main.async(execute: {
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
                networkActivityIndicatorCount = 0
            })
        }
    }
}

2.最前面のViewControllerの取得

UIAlertViewControllerが表示されている場合にも対応させましたが、これも汚いし、書き直さないと...

/// 最前面のViewControllerを取得する
    ///
    /// - Parameter isExceptAlertController: AlertControllerを除くかどうか
    /// - Returns: 最前面のViewController
    public func topViewController(isExceptAlertController: Bool = true) -> UIViewController? {
        /// inner func (viewControllerを解析する)
        func parseVC(vc: UIViewController, presentedVC: UIViewController?) -> (UIViewController?, UIViewController?) {
            var viewController: UIViewController? = vc

            switch presentedVC {
            case let navagationController as UINavigationController:
                viewController = navagationController.viewControllers.last
            case let tabBarController as UITabBarController:
                viewController = tabBarController.selectedViewController
            default:
                viewController = viewController?.presentedViewController
            }

            return (viewController, viewController?.presentedViewController)
        }
        /// inner func end

        var viewController = UIApplication.shared.keyWindow?.rootViewController
        guard viewController != nil else {
            return nil
        }
        var presentedViewController = viewController?.presentedViewController

        if isExceptAlertController {
            while presentedViewController != nil, !(presentedViewController is UIAlertController) {
                let vcAndPresentedVC = parseVC(vc: viewController!, presentedVC: presentedViewController)
                viewController = vcAndPresentedVC.0
                presentedViewController = vcAndPresentedVC.1
            }
        }else {
            while presentedViewController != nil {
                let vcAndPresentedVC = parseVC(vc: viewController!, presentedVC: presentedViewController)
                viewController = vcAndPresentedVC.0
                presentedViewController = vcAndPresentedVC.1
            }
        }
        return viewController
    }

3.クラス名の取得

extension NSObject {
    /// クラス名。
    static var className: String {
        get { return String(describing: self) }
    }

    var className: String {
        get { return String(describing: type(of: self)) }
    }
}

4.UIColor生成

extension UIColor {

    /// 16進数表記からUIColorを作成
    /// 失敗時は白色になる。
    ///
    /// - Parameters:
    ///   - hexString: 16進数表記のRGB (000000〜FFFFFF)
    ///   - alpha: alpha値 (0.0〜1.0)
    convenience init?(hexString: String, alpha: Double = 1.0) {
        var color: UInt32 = 0
        guard Scanner(string: hexString.replacingOccurrences(of: "#", with: "")).scanHexInt32(&color) else {
            return nil
        }
        self.init(red: CGFloat((color & 0xFF0000) >> 16) / 255.0,
                green: CGFloat((color & 0x00FF00) >> 8) / 255.0,
                blue: CGFloat(color & 0x0000FF) / 255.0,
                alpha: CGFloat(alpha))
    }

    /// 16進数表記からUIColorを作成する。
    ///
    /// - Parameters:
    ///   - hex: 16進数表記のRGB (0x000000~0xFFFFFF)
    ///   - alpha: alpha値 (0.0~1.0)
    convenience init(hex: Int, alpha: Double = 1.0) {
        self.init(red: CGFloat((hex & 0xFF0000) >> 16) / 255.0,
                green: CGFloat((hex & 0x00FF00) >> 8) / 255.0,
                blue: CGFloat(hex & 0x0000FF) / 255.0,
                alpha: CGFloat(alpha))
    }

    /// 10進数表記からUIColorを作成する。
    ///
    /// - Parameters:
    ///   - red: 赤 (0~255)
    ///   - green: 緑 (0~255)
    ///   - blue: 青 (0~255)
    ///   - alpha: alpha値 (0.0~1.0)
    @nonobjc
    convenience init(red: Int, green: Int, blue: Int, alpha: Double = 1.0) {
        self.init(red: CGFloat(red) / 255.0,
                green: CGFloat(green) / 255.0,
                blue: CGFloat(blue) / 255.0,
                alpha: CGFloat(alpha))
    }
}

5.最後に

ご静聴ありがとうございました。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3