propertynameItemClass in swift

231 views
Skip to first unread message

Julian Paas

unread,
Sep 15, 2014, 4:43:44 PM9/15/14
to mobile-c...@googlegroups.com
I have a CBLModel with an array of objects that implement CBLJSONEncoding, but every time I try to access the array I get

"fatal error: NSArray element failed to match the Swift Array Element type"

I have tried to implement a class method to return the class for this array as follows, but it never gets called. Have I implemented the method incorrectly, or is there something else I am missing?

  @NSManaged var gamePlayerStats: [PlayerStats]

  class func gamePlayerStatsItemClass() -> PlayerStats.Type {
    return PlayerStats.self
  }



Jens Alfke

unread,
Sep 15, 2014, 6:59:33 PM9/15/14
to mobile-c...@googlegroups.com

> On Sep 15, 2014, at 1:43 PM, Julian Paas <julia...@osaaru.com> wrote:
>
> @NSManaged var gamePlayerStats: [PlayerStats]

I haven't gotten into Swift programming yet — is the "@NSManaged" declaration equivalent to "@dynamic" in Objective-C? (It needs to be, otherwise CBLModel won't recognize gamePlayerStats as a database-backed property.)

> class func gamePlayerStatsItemClass() -> PlayerStats.Type {
> return PlayerStats.self
> }

That looks OK as far as I know, although I don't know exactly what PlayerStats.Type is. The return type needs to be a Swift equivalent of 'Class'. It's possible that the compiler decided that PlayerStats.Type isn't compatible with anything in Objective-C and decided not to make that method visible from Obj-C, which would explain why it didn't get called…

—Jens

Julian Paas

unread,
Sep 15, 2014, 9:23:29 PM9/15/14
to mobile-c...@googlegroups.com
Yes @NSManaged is the same as @dynamic and is working nicely for all my other simple, non-array properties.

PlayerStats is a custom class that implements the CBLJSONEncoding protocol so that it can be stored as part of a Couchbase document.

class PlayerStats : NSObject, CBLJSONEncoding {
  
  required init(JSON jsonObject: AnyObject!) {
   ...
  }
  
  func encodeAsJSON() -> AnyObject! {
  ....
  }
}

It took me quite a while to figure out how to return a class in Swift. Since I was only about 80% sure that gamePlayerStatsItemClass method is correct, I just tested it by creating an objective-C class with a method that would call gamePlayerStatsItemClass so I could see what the response looks like on the Objective-C side. As far as I can tell, it works. It returns a class with a module namespace prefix. i.e. the class is named "mymodule.PlayerStats". So the method signature does appear to return an objective-C Class.

Jens Alfke

unread,
Sep 16, 2014, 1:28:24 AM9/16/14
to mobile-c...@googlegroups.com
Hm. Well, you can see the code that calls that method here.  So you could try adapting that in your Obj-C class and step through it and see what happens…

—Jens

Julian Paas

unread,
Sep 16, 2014, 10:04:15 AM9/16/14
to mobile-c...@googlegroups.com
I adapted that code into my test class and it does find the selector. I had trouble with the objc_msgSend because the new LLVM in Xcode has introduced strict type checking and I haven't figured out yet how to invoke it properly.

Next step I suppose would be to try including the library as source rather than as a cocoapod so that could step through the actual API code. Not sure I have time for that right at the moment.

Have there been any thoughts/discussions about a native Swift API?

Jens Alfke

unread,
Sep 16, 2014, 11:46:49 AM9/16/14
to mobile-c...@googlegroups.com

On Sep 16, 2014, at 7:04 AM, Julian Paas <julia...@osaaru.com> wrote:

Have there been any thoughts/discussions about a native Swift API?

No, not yet. All I've done so far is tweaked the .h files to make them more Swift-friendly, by adding things like NS_ENUM.

Starting to play with an actual Swift project now. Is there a way to see an entire Swift-ified interface for a CBL class? If I Cmd-click on a UIKit or Foundation class name it shows me the full interface in Swift syntax, but if I do that on a CBL class it just opens the regular .h file.

When I start thinking about a native Swift API, I find myself thinking about JSON types. The natural Swift encoding of a JSON value would be an enum; has anyone built a library around that yet?

—Jens

Julian Paas

unread,
Sep 16, 2014, 11:22:54 PM9/16/14
to mobile-c...@googlegroups.com
I'm new enough to Swift I don't know the answers to those questions either. Its still early days and hard to find answers to a lot of questions.

Karel-Jan Van Haute

unread,
Oct 15, 2014, 5:55:57 AM10/15/14
to mobile-c...@googlegroups.com
Hey Julian 

you say that you have working swift code to use CBLModel and link the properties right with @NSManaged?
If so, can you share a class? As an example? 

Thank you

Julian Paas

unread,
Oct 15, 2014, 9:06:38 AM10/15/14
to mobile-c...@googlegroups.com
Here's the simplest CBLModel class I have at the moment

class Team: CBLModel {


  @NSManaged var name: String
  @NSManaged var type: String


  class func teamsByName() -> CBLQuery {
    let query = databaseMain.viewNamed(kCblViewTeamsByName).createQuery()

    return query
  }


  // Because class variables are not supported yet.

  class func typeName() -> String {
   
return "Team"
  }

 
override
func willSave(changedPropertyNames: NSSet!) {
    self.setValue(Team.typeName(), ofProperty: "type")
  }
}


Julian Paas

unread,
Nov 3, 2014, 10:04:40 PM11/3/14
to mobile-c...@googlegroups.com
I still haven't figured out how to make this work in Swift. Here's some more complete examples that show how I've tried to do it








import Foundation




class ArrayItem : NSObject, CBLJSONEncoding {


 


  let holding: Int


 


  init(holding: Int) {


    self.holding = holding


  }


 


  required init(JSON jsonObject: AnyObject!) {


    self.holding = 0


  }


 


  func encodeAsJSON() -> AnyObject! {


    return ["holding": self.holding]


  }


}




class TestModel : CBLModel {


 


  @NSManaged var itemArray: [ArrayItem]


 


  @NSManaged var simpleArray: [Int]


 


  class func all() -> CBLQuery {


    let query = databaseMain.viewNamed("allTestModels").createQuery()


    return query


  }


 


  class func itemArrayItemClass() -> AnyClass {


    return ArrayItem.self


  }


 


  convenience init() {


    self.init(newDocumentInDatabase: databaseMain)


    itemArray = [ArrayItem]()


    simpleArray = [Int]()


    type = "TestModel"


  }


}


That declares the model object. Here is an example that will look up the objects and try to access the properties. When it tries to access the property named itemArray, it causes a fatal error.









  func runModelTests() {


   


    var error: NSError?


    let queryEnumerator = TestModel.all().run(&error)


    assert(error == nil, "Unable to query all TestModels")


    while let row = queryEnumerator.nextRow() {


      let testModel = TestModel(forDocument: row.document) as TestModel


      println("properties: \(testModel.document.properties)")


      println("simpleArray: \(testModel.simpleArray)")


     


      // Here's where we access the array of objects and get...


      // fatal error: NSArray element failed to match the Swift Array Element type


      println("itemArray: \(testModel.itemArray)")


      testModel.deleteDocument(&error)


      assert(error == nil, "Unable to delete TestModel")


    }


   


    let testModel = TestModel()


    testModel.simpleArray.append(0)


    testModel.simpleArray.append(1)


    testModel.itemArray.append(ArrayItem(holding: 10))


    testModel.itemArray.append(ArrayItem(holding: 11))


    testModel.save(&error)


    assert(error == nil, "Unable to create test model")


    println("Created TestModel: \(testModel.document.properties)")


  }

Jens Alfke

unread,
Nov 4, 2014, 12:48:43 AM11/4/14
to mobile-c...@googlegroups.com
Thanks for the test case, Julian. You may be in terra incognita here — I haven't had a chance to get too far into Swift coding yet and haven't looked into how it interacts with CBLModel. I think my colleague Pasin is starting to investigate (he's the one who added the Swift code samples to the documentation recently.)

If you're building CBL from source and can step through its code, the next step is probably to set a breakpoint in +[CBLModel impForGetterOfProperty:ofClass:]. This will get called once for every dynamic property, so when it's called for itemArray you can step through it and see what goes wrong.

—Jens

adam wilson

unread,
Sep 15, 2015, 6:30:32 PM9/15/15
to Couchbase Mobile
Has there been any progress on this? I'm in the process of building an iOS CBLite app with Swift and hit this issue with CBLModel and @dynamic properties...
Or do the model classes still need to be Objective-C?

Julian Paas

unread,
Sep 15, 2015, 7:42:58 PM9/15/15
to Couchbase Mobile
I've been using Swift with Couchbase Lite for a year now and I've recently upgraded my app to Xcode 7 and Swift 2.0. Its working fine, although the Couchbase Swift docs have not been updated to 2.0 yet.

Jens Alfke

unread,
Sep 16, 2015, 12:41:57 AM9/16/15
to mobile-c...@googlegroups.com

On Sep 15, 2015, at 3:30 PM, adam wilson <adam.el...@gmail.com> wrote:

Has there been any progress on this? I'm in the process of building an iOS CBLite app with Swift and hit this issue with CBLModel and @dynamic properties... 

Yes, I think this was resolved; I did some work on CBLModel and its base class MYDynamicObject a few months ago to make them work better with Swift. (See issue #648.) It looks as though this got merged into the 1.1.1 branch, which should be showing up as a release in the next few days.

—Jens

adam wilson

unread,
Sep 16, 2015, 6:26:28 AM9/16/15
to Couchbase Mobile
Thanks, I found the latest release 1.1.1-18 and its working fine now. Also heped to look at this project.
Reply all
Reply to author
Forward
0 new messages