元柔道整復師エンジニアBlog

- 元柔道整復師エンジニアBlog -

『 エンジニアをリングする。』

CoreDataのSubclass/Propertiesクラスを生成したらMultiple commands produce...によりビルトが通らなくなった

オフライン時のデータ保持のために、CoreDataを使いう機会がありそのときにつまずいたことを書いていく。

もともとCoreDataモデルをいくつか作成していて、
これまで通りに今回もCoreDataモデル(User)を新たに追加するために

  • CoreDataModel.xcdatamodelを作成する
  • CoreDataModelにEntityを追加する
  • 管理対象のオブジェクトサブクラス+CoreDataClassおよびプロパティファイル+CoreDataPropertiesを作成する

の手順で生成したあとにビルドを通してみると

:-1: Multiple commands produce '/Users/ユーザ名/Library/Developer/Xcode/DerivedData/***/Build/Intermediates.noindex/***.build/Debug-iphoneos/***.build/Objects-normal/arm64/User+CoreDataProperties.o':
1) Target 'Scheme名' (project 'App名') has compile command for Swift source files
2) Target 'Scheme名' (project 'App名') has compile command for Swift source files

:-1: Multiple commands produce '/Users/ユーザ名/Library/Developer/Xcode/DerivedData/***/Build/Intermediates.noindex/***.build/Debug-iphoneos/***.build/Objects-normal/arm64/User+CoreDataClass.o':
1) Target 'Scheme名' (project 'App名') has compile command for Swift source files
2) Target 'Scheme名' (project 'App名') has compile command for Swift source files

というエラーが発生した。

どちらもNSManagedObjectサブクラスを生成したときのUser+CoreDataPropertiesUser+CoreDataClassが影響してるみたい...

しかし、これまで通り作業手順は変えてもいないのに急になぜ!?

....と思いつつ調べてみた

※以降からは、 CoreDataから生成した管理対象のオブジェクトサブクラス〇〇+CoreDataClassとプロパティファイル〇〇+CoreDataPropertiesをまとめてNSManagedObjectと呼んでいます。

原因

どうやらXcode8以降、CoreDataからNSManagedObjectを生成する際のコード生成オプション(Codegen)設定が追加されたみたいで

これまでのようなCoreDataのEntity作成後に
手動でEditor > Create NSManagedObject Subclass...をしなくても、デフォルト状態でNSManagedObjectを自動生成してくれる仕組みになったらしい。

そこでCodegenの設定を見てみると、 f:id:CHU-BURA:20190518141834p:plain

Codegenプロパティ値は、デフォルトであるクラス定義Class Definitionとなっていた。

このClass Definitionは公式によると

Choose Class Definition If You Won't Need to Edit the Generated Logic or Properties
Choose this option when you don’t need to edit the properties or functionality of the managed object subclass and properties files that Core Data generates for you.
The generated source code doesn’t appear in your project’s source list. Xcode produces the class and properties files as part of the build process and places them in your project’s build directory.
These files regenerate whenever the related entity changes in the data model.


生成されたロジックやプロパティを編集する必要がない場合は、クラス定義(Class Definition)を選択してください。
Core Dataが生成する管理対象オブジェクトサブクラスおよびプロパティファイルのプロパティや機能を編集する必要がない場合は、このオプションを選択してください。
生成されたソースコードは、プロジェクトのソースリストには表示されません。 Xcodeは、ビルドプロセスの一部としてクラスファイルとプロパティファイルを作成し、それらをプロジェクトのビルドディレクトリに配置します。
これらのファイルは、関連するエンティティがデータモデル内で変更されるたびに再生成されます。
Generating Code | Apple Developer Documentation

要するにプロジェクト内のソースとして見えていないだけで、Class Definitionを設定した時点でNSManagedObjectが内部的に生成されていた。

そしてNSManagedObjectが生成されているにもかかわらず、
再度、手動でEditor > Create NSManagedObject Subclass...したことにより同一名のNSManagedObjectが重複してしまいコンパイルエラーが発生した。

これまで生成したCoreDataのCodegenプロパティを見てみると
Class Definitionではなく、Manula/Noneの設定がされていて、なんらかの影響でデフォルトの設定になってしまってたようだ...

解決

現在のClass Definitionを設定した状態で、そのあとに手動追加していたEditor > Create NSManagedObject Subclass...をしなければ簡単に解決はする....

しかし今回の場合は、生成したNSManagedObjectにメンバ変数やメソッドを追加したい。
→ Core Dataが生成する管理対象オブジェクトサブクラスおよびプロパティファイルのプロパティや機能を編集する必要がある。

そうなると現在の設定Class DefinitionでNSManagedObjectを生成した場合は、生成されたNSManagedObjectのロジックやプロパティを編集することができない。

そこでCodegenプロパティをManula/Noneにすることで、

  • NSManagedObjectの自動生成を無効にして
    → 同一名のNSManagedObjectによる重複エラーの回避
  • 手動Editor > Create NSManagedObject Subclass...でNSManagedObjectを生成できる。
    → NSManagedObjectのロジックやプロパティの編集が可能

f:id:CHU-BURA:20190518184359p:plain

Choose Manual/None to Edit Properties and Logic in the Managed Object Subclass
Choose this option to edit the properties in your managed object subclass, for example, to alter access modifiers, and to add additional convenience methods or business logic.
With Manual/None, Core Data doesn’t generate files to support your class. You create and maintain your class, including its properties, manually. Core Data then locates these files using the values you supply in the class name and module fields.
To generate the class and properties files initially:

  1. Choose Editor > Create NSManagedObject Subclass from the menu at the top of the screen.
  2. Select your data model, then the appropriate entity, and choose where to save the files. Xcode places both a class and a properties file into your project.

You can now see and edit both the class and properties files in your project source list.


管理対象サブクラスのプロパティとロジックを編集するには、Manula/None を選択します。
このオプションを選択すると、たとえば、アクセス修飾子を変更したり、便利なメソッドやビジネスロジックを追加したりするために、管理オブジェクトサブクラスのプロパティを編集できます。
Manual / Noneの場合、Core Dataはクラスをサポートするためのファイルを生成しません。手動でクラスを作成し、そのプロパティも含めて管理します。その後、Core Dataは、クラス名とモジュールフィールドに指定した値を使用してこれらのファイルを見つけます。
クラスファイルとプロパティファイルを最初に生成するには

  1. 画面上部のメニューから「エディタ」>「NSManagedObjectサブクラスの作成」を選択します。
  2. データモデルを選択してから適切なエンティティを選択し、ファイルの保存場所を選択します。Xcodeはクラスとプロパティファイルの両方をプロジェクトに配置します。

これで、プロジェクトソースリスト内のクラスファイルとプロパティファイルの両方を表示して編集できます。
Generating Code | Apple Developer Documentation

参考

【Swift】@IBActionのsender(Any型)からUIButtonの重複タップを防止する。

目的

  • StoryBoard上のあるUIButtonに接続された@IBActionsender(Any型)から重複タップを防止したい。
  • @IBActionのみ接続されたUIButtonの重複タップを防止したい。

前提条件

  1. StroryBoard側には、対象のUIButtonが配置されている。
  2. コード側では、1.のUIButtonが@IBActiononClickButton(sender: Any)にのみ接続されている。
  3. StoryBoard上への変更は加えない。
class ViewController: UIViewController {
    
    // UIButton押下時の処理
    @IBAction func onClickBtn(_ sender: Any) {
        
        // ここで重複タップを防止させたい
    }

}

方法

  • @IBActionのsenderからUIButton属性を取得する。
    →この場合、senderはAny型のためUIBUttonへキャストする。

  • UIButton属性を取得後、Enabledステータスをfalseに変更する。
    →UIButtonを非活性にする。

  • UIButton押下時に、Enabledステータスをtrueに戻す。
    →UIButtonを活性にもどす。

class ViewController: UIViewController {
    
    // UIButton押下時の処理
    @IBAction func onClickBtn(_ sender: Any) {
        
        // UIButtonを非活性にする
        let targetButton = sender as! UIButton
        targetButton.isEnabled = false
        
        // 何らかの処理(アニメーションなど)
        ・
        ・
        ・
        
        // メイン処理後に、UIButtonを活性に戻す
        targetButton.isEnabled = true
    }

}

【Xcode】iOS実機確認時における「This iPhone 6 is running iOS 11.4.1 (15G77), which may not...」の対処方法

環境

事象

Route_xcodeproj-3.png

Xcodeにおいて、実機接続後のビルド実行時に 「This iPhone 6 is running iOS 11.4.1 (15G77), which may not be supported by this version of Xcode.」 が発生する。
→ 実機iPhoneのOSに対応したデバイスサポートファイルが存在しない言われる。

原因

  • 自身の実機iPhoneのOSがXcode側でサポート外である場合
  • 以前使用可能であった実機iPhoneのOSをアップデートしたことにより、Xcode側でサポート外となった場合

対処

Xcode側に実機OSに対応したデバイスサポートファイルを配置し、サポート対象であること明示する。

1. 対応したデバイスサポートファイルの確認

実機OSを確認する。

  1. 実機iPhoneのOSを確認する。
    設定 > 情報 > バージョン
    ⇒ 例)11.4.1(15G77)

  2. 自身のXcodeバイスサポート内容を確認する。 /Users/<USER_NAME>/Library/Developer/Xcode/iOS DeviceSupport/配下のディレクトリに、実機OSと同等ではないディレクトリ名が存在するかを確認する。
    ⇒ 例)ディレクトリ名 : 11.3.1 (15E302)
    iosDeviceSupport_15E302.png

実機OSに対応したデバイスサポートファイルを確認する。

Ghibli / iOS-DeviceSupportより、実機OSに対応したデバイスサポートファイルを確認する。

2. デバイスサポートファイルのDL

先程のGithubリポジトリ iOS-DeviceSupport から対象のzipファイルをDLする。

→ 今回は、実機OSが11.4.1(15G77)なので、11.4(15F79).zipファイルをDLしました。

※ はじめに 11.4(15F5061c).zip をDLしましたが、上手く動作しなかったので()内のバージョンも意味があるみたいなので、実機OSに近しいものを選んだほうがいいかと思います。

3. デバイスサポートファイルの配置

DLしたzipファイル解凍後、 /Users/<USER_NAME>/Library/Developer/Xcode/iOS DeviceSupport/配下に解凍されたディレクトリを配置する。
※ もともと~/iOS DeviceSupport/配下にある既存ディレクトリの削除は不要です。 11_4_1__15G77__と_ダウンロード.png

4. 再ビルド

バイスサポートファイル配置後、再度ビルドし直す。
※ うまく動作しない場合は、Xcodeを再起動することおすすめします。

【Xcode】初心者でも便利なショートカット

最近、iOSアプリ開発に携わって個人的に便利だと感じたXcodeショートカットたち。

起動 / 停止

Cmd + b

ビルド

Cmd + r

ビルド+Run

Cmd + q

シュミレーター終了

Cmd + .

Run停止

Cmd + Shift + k

クリーン

検索

Cmd + Shift + f

プロジェクト内検索

Cmd + Shift + o

 プロジェクト内のファイル・フィールド・メソッド検索

Cmd + f

編集中ファイルのファイル内検索

Ctrl + 6

 編集中ファイル内のフィールド・メソッド・

移動

Cmd + Ctrl + j

定義元へジャンブ

Cmd + Ctrl + ← または →

ファイル間の進む、戻る

Cmd + j

展開ファイルのカーソル移動、画面分割時のカーソル移動

表示切替

Cmd + Shift + j

プロジェクトナビゲーター(右エリア)で、現在の編集中ファイルを選択

Cmd + 0

ナビゲータエリア(左エリア)の表示非表示切り替え

Cmd + 1 ~ 9

ナビゲータエリア(左エリア)の表示、切り替え

Cmd + Opt + 0

ユーティリティエリア(右エリア)の表示非表示切り替え

Cmd + Opt + 0 ~7

ユーティリティエリア(右エリア)の表示、切り替え

Cmd + Shift + y

デバッグエリアの表示非表示切り替え

【Swift】16進数⇔RGBの相互変換 / UIColor生成

環境

16進数 ⇒ RGB

RGB値を配列で返却する

/**
 16進数からRGB値へ変換する.
 - parameter String: 16進数
 - returns [Int]: RGB
 */
func hexToRGB(hex color: String) -> [Int]{
    var rgb:[Int] = []
    let scanner = Scanner(string: color as String)
    var color:UInt32 = 0
    if(scanner.scanHexInt32(&color)) {
        let r = (color & 0xFF0000) >> 16
        let g = (color & 0x00FF00) >> 8
        let b = (color & 0x0000FF)
        rgb.append(Int(r))
        rgb.append(Int(g))
        rgb.append(Int(b))
    }
    return rgb
}
print(hexToRGB(hex: "FFFFFF")) // [255, 255, 255]

16進数 ⇒ UIColor

UIColorクラスを拡張する

extension UIColor {
    
    /**
     16進数からUIColorへ変換する.
     - parameter String: 16進数
     - returns UIColor: RGB
     */
    class func hexToRgb(color: String, alpha : CGFloat = 1) -> UIColor {
        let code = color
        let scanner = Scanner(string: code as String)
        var color:UInt32 = 0
        if scanner.scanHexInt32(&color) {
            let r = (color & 0xFF0000) >> 16
            let g = (color & 0x00FF00) >> 8
            let b = (color & 0x0000FF)
            return UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha:alpha)
        } else {
            print("invalid hex string")
            return UIColor.clear;
        }
    }
}
print(UIColor.hexToRgb(color: "ffffff")) // UIExtendedSRGBColorSpace 255 255 255 1
print(UIColor.hexToRgb(color: "FF0000", alpha: 0.8)) // UIExtendedSRGBColorSpace 255 0 0 0.8

RGB ⇒ 16進数

/**
 RGB値から16進数へ変換する.
 - parameter Int: RGB
 - returns String: 16進数
 */
func rgbToHex(red r: Int, green g: Int, blue b: Int) -> String {
    return String(NSString(format: "%02X%02X%02X", r, g, b))
}
print("#\(rgbToHex(red: 255, green: 255, blue: 255))") // #FFFFFF

今週のお題「2019年の抱負」

RGB ⇒ UIColor

/**
 RGB値からUIColorへ変換する.
 - parameter Int: RGB
 - parameter CGFloat: alpha
 - returns UIColor
 */
func rgbToColor(red r: Int, green g: Int, blue b: Int, alpha: CGFloat = 1) -> UIColor {
    return UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: alpha)
}

参考