36608


[Item+CoreDataProperties.swift]

[code]

    import Foundation
        import CoreData
        import UIKit
    
        extension Item {
    
            @nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
                return NSFetchRequest<Item>(entityName: "Item")
            }
    
            @NSManaged public var center: CGPoint
            @NSManaged public var title: String?
    
        }
    
[/code]

[モデルファイル]

[![画像の説明をここに入力](https://i.stack.imgur.com/D2m2O.png)](https://i.stack.imgur.com/D2m2O.png)

下のようにして、Itemを作ってcenterを代入しようとするとエラーとなってしまいます。

> 'NSInvalidArgumentException', reason: '-[Item setCenter:]:  
>  unrecognized selector sent to instance 0x6080002823a0'
[code]

    let newItem = NSEntityDescription.insertNewObject(forEntityName: "Item", into: coreDataStack.context) as! Item
            try! coreDataStack.saveContext()
    
            //初期値
            let center = CGPoint(x: 100, y: 100)
            newItem.center = center//エラーとなる！
    
[/code]


CC BY-SA 3.0
2017-07-23T03:31:13.263
36604
2017-07-24T23:59:20.493
2017-07-24T23:59:20.493
5519
12297
question
swift xcode swift3
CoreDataにCGPointをそのまま保存したい


いきなり表題の件について結論を書くと、表題とご質問の文面のように、直接エンティティクラスの中に`CGPoint`型のプロパティを持つのは不可能であろうと言うことです。

Core
Dataのエンティティが内部的に保持できるのは、モデルエディターのTypeポップアップに表示されるものだけです。あなたが設定されたように、Trasnformableを使用すると、クラスのプロパティには他のデータ型をもたせ、内部的にはサポートされているデータ型に変換して保持する、と言うことができるようになっているのですが、
**Transformableの機構は参照型(`class`)のみをサポートしており、値型からの変換はサポートしていない** ようです。

* * *

と言うわけで代替案として、`CGPoint`を`NSValue`に変換して設定するようにしてはどうでしょうか。

  * `NSValue`は`NSCoding`に適合しているので、`ValueTransformer`を自前で書かなくても使える
  * `CGPoint`から`NSValue`へは単純なブリッジング型の変換(`as`キャスト)が使える

と言うわけで、かなり「そのまま保存する」に近いことができると思います。

モデルエディターで`center`を選び、Data Model Inspectorの部分で、 **Custom Class**
を`NSValue`に設定します。( **Value Transformation** と、 **Module** はデフォルトのままにしておく。)

[![Transformable設定](https://i.stack.imgur.com/HjtIR.jpg)](https://i.stack.imgur.com/HjtIR.jpg)

この状態でモデルクラスを作成してやると、`@NSManaged public var center:
NSValue?`のような行ができるはずですので、こんな書き方ができるようになります。

[code]

    newItem.center = center as NSValue
    
[/code]

使うときはざっくりとその逆でこんな感じです。

[code]

    let center: CGPoint = item.center as? CGPoint ?? CGPoint.zero
    
[/code]

* * *

`as`キャストさえ面倒だと言うのなら、さらにこんな計算型プロパティを付け加えておく手もあります。

[code]

    import Foundation
    import CoreGraphics
    
    extension Item {
    
        public var centerPoint: CGPoint {
            get {
                return center as? CGPoint ?? CGPoint.zero
            }
            set {
                center = newValue as NSValue
            }
        }
    
    }
    
[/code]

* * *

なお、ちなみに計算型プロパティがありならば、`x`,
`y`を`Item`エンティティのDouble型Attributeにしてしまい、こんな計算型プロパティを定義してしまう手もあります。

[code]

    import Foundation
    import CoreData
    
    
    extension Item {
    
        @nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
            return NSFetchRequest<Item>(entityName: "Item")
        }
    
        @NSManaged public var x: Double
        @NSManaged public var y: Double
        @NSManaged public var title: String?
    
    }
    
    extension Item {
    
        public var center: CGPoint {
            get {
                return CGPoint(x: x, y: y)
            }
            set {
                x = Double(newValue.x)
                y = Double(newValue.y)
            }
        }
    
    }
    
[/code]

この方が`x`や`y`なんかのプロパティを検索条件に使えたりして便利かもしれません。

* * *

私的にはストアドプロパティさえ用意せずに`x`,
`y`そのまんまで良いんじゃないのとか思ってしまう方なのですが、うまくご自身にとってベストの方法を見つけてください。  
(なお、こちらでは **unrecognized selector**
のエラーは再現できなかったので、今エラーが出ているプロジェクトに修正を加えるだけでは、上記のコードはうまく動かないかもしれません。)


CC BY-SA 3.0
2017-07-23T06:15:00.527
36608
2017-07-23T06:59:30.187
2017-07-23T06:59:30.187
13972
13972
36604
answer