How to assign value to NSManaged object in Swift? - objective-c

I need to write test case and in the test case, I need to mock an object of the following class and put them into an array. e.g. moviesArray = [MovieCoreData]. I am wondering how I can instantiate this?
#objc(MovieCoreData)
class MovieCoreData {
static var genre: Genre = "Comedy"
#NSManaged var actor: String
#NSManaged var released: Bool
#NSManaged var name: String?
public static func mapping() -> [FieldName: String] {
return [
"actor": "actor",
"released": "released",
"name": "name"
]
}
}
For example, I tried to write
moviesArray = [MovieCoreData(actor: "Johnny Depp", released: True, name: "Pirate Captain"]
and
moviesArray.first.actor = "Johnny Depp"
moviesArray.first.released = "true"
moviesArray.first.name = "Pirate Captain"
none of them worked, I admit I don't know much about Objective-C and NSManaged, can someone tell me how to create an array of MovieCoreData with fake data?

The #NSManaged attribute belongs to Core Data.
The class must be a subclass of NSManagedObject and instances must be created with regard to the NSManagedObjectContext to take advantage of its functionality

Related

Kotlin get all property value of data class

Is there a syntactic sugar in Kotlin to iterate on each field/property value of a data class?
Sample:
data class User(
var firstName: String = DEFAULT_VALUE_STRING,
var middleName: String = DEFAULT_VALUE_STRING,
var lastName: String = DEFAULT_VALUE_STRING
)
val user = User()
Then check if any of the property's value is empty, considering all of it is String data type with something like this
if (user.properties.any{ it.isBlank() }) {
// TODO ...
}
Probably the closest you'll get is checking all the values of all the generated componentX() functions (since they're only created for the constructor parameter properties, the "data" in a data class) but yeah that involves reflection.
If I were you, I'd create an interface with a properties property and make all your data classes implement that - something like this:
import kotlin.reflect.KProperty0
interface HasStringProperties {
val properties: List<KProperty0<String>>
}
data class User(
var firstName: String = "",
var middleName: String = "",
var lastName: String = ""
) : HasStringProperties {
override val properties = listOf(::firstName, ::middleName, ::lastName)
}
fun main() {
val user = User("Funny", "", "Name")
println(user.properties.any {it.get().isBlank()})
}
So no, it's not automatic - but specifying which properties you want to include is simple, and required if you're going to access it on a particular class, so there's an element of safety there.
Also, because you're explicitly specifying String properties, there's type safety included as well. Your example code is implicitly assuming all properties on your data classes will be Strings (or at least, they're a type with an isBlank() function) which isn't necessarily going to be true. You'd have to write type-checking into your reflection code - if you say "I don't need to, the classes will only have String parameters" then maybe that's true, until it isn't. And then the reflection code has to be written just because you want to add a single age field or whatever.
You don't actually have to use property references in Kotlin either, you could just grab the current values:
interface HasStringProperties {
val properties: List<String>
}
data class User(
var firstName: String = "",
var middleName: String = "",
var lastName: String = ""
) : HasStringProperties {
// getter function creating a new list of current values every time it's accessed
override val properties get() = listOf(firstName, middleName, lastName)
}
fun main() {
val user = User("Funny", "", "Name")
println(user.properties.any {it.isBlank()})
}
It depends whether you want to be able to reference the actual properties on the class itself, or delegate to a getter to fetch the current values.
And of course you could use generics if you want, list all the properties and use filterIsInstance<String> to pull all the strings. And you could put a function in the interface to handle a generic isEmpty check for different types. Put all the "check these properties aren't 'empty'" code in one place, so callers don't need to concern themselves with working that out and what it means for each property

Unacceptable type of value for ordered to-many relationship when writing swift case

I am working on a project that requires me to add a new test case on an existing file structure. Right now I am doing a test case that needs to do api call to get maintenance information.
The maintenance information I am getting back is structured as follows:
#objc(MaintenanceData)
class MaintenanceData: WebServiceEntity, Persistable {
static var contentId: ContentId = "maintenance"
#NSManaged var version: String
#NSManaged var maintenance: Bool
#NSManaged var maintenanceDescription: String?
#NSManaged var features: [FeatureMaintenanceData]?
public static func fieldMapping() -> [FieldName: String] {
return [
"version": "version",
"maintenance": "maintenance",
"maintenanceDescription": "maintenanceDescription",
"features": "features"
]
}
}
** For security reasons, I changed some code
Right now I have to mock a fake MaintenanceData to test the features property, which is an array of [FeatureMaintenanceData]
The FeatureMaintenanceData is written as follows:
#objc(FeatureMaintenanceData)
class FeatureMaintenanceData: WebServiceEntity, Persistable {
static var contentId: ContentId = "featureMaintenance"
#NSManaged var identifier: String
#NSManaged var maintenance: Bool
#NSManaged var maintenanceDescription: String?
public static func fieldMapping() -> [FieldName: String] {
return [
"identifier": "identifier",
"maintenance": "maintenance",
"maintenanceDescription": "maintenanceDescription"
]
}
}
Right now I need to mock the data, there is a function in the MockDataFactory which returns a MaintenanceData object, I need to make use of it and pass it to the function for testing. The function is written as follows:
#discardableResult func createAndInsertMaintenance(
version: String = "",
maintenance: Bool = false,
maintenanceDescription: String? = nil,
features: [FeatureMaintenanceData]? = nil,
) -> MaintenanceData {
let maintenance = NSEntityDescription.insertNewObject(
forEntityName: String(describing: MaintenanceData.self),
into: managedObjectContext
) as! MaintenanceData
maintenance.version = version
maintenance.softUpdateMinimalVersion = softUpdateMinimalVersion
maintenance.maintenance = maintenance
maintenance.maintenanceDescription = maintenanceDescription
maintenance.features = features <-----This is where error occurs
return updateMaintenance
}
I created and pass the fake FeatureMaintenanceData to the function
let featureMaintenance = NSEntityDescription.insertNewObject(
forEntityName: "FeatureMaintenanceData",
into: managedObjectContext
) as! FeatureMaintenanceData
featureMaintenance.identifier = "Microwave"
featureMaintenance.maintenance = true
featureMaintenance.maintenanceDescription = "test"
It however returns an error Unacceptable type of value for ordered to-many relationship: property = "features"; desired type = NSOrderedSet;
Since I don't know Obj-c, could someone explain to me what is the problem and how to solve it? Much thanks!

How can I set the JsName for a property's backing field in Kotlin?

I played about with Kotlin's unsupported JavaScript backend in 1.0.x and am now trying to migrate my toy project to 1.1.x. It's the barest bones of a single-page web app interfacing with PouchDB. To add data to PouchDB you need JavaScript objects with specific properties _id and _rev. They also need to not have any other properties beginning with _ because they're reserved by PouchDB.
Now, if I create a class like this, I can send instances to PouchDB.
class PouchDoc(
var _id: String
) {
var _rev: String? = null
}
However, if I do anything to make the properties virtual -- have them override an interface, or make the class open and create a subclass which overrides them -- the _id field name becomes mangled to something like _id_mmz446$_0 and so PouchDB rejects the object. If I apply #JsName("_id") to the property, that only affects the generated getter and setter -- it still leaves the backing field with a mangled name.
Also, for any virtual properties whose names don't begin with _, PouchDB will accept the object but it only stores the backing fields with their mangled names, not the nicely-named properties.
For now I can work around things by making them not virtual, I think. But I was thinking of sharing interfaces between PouchDoc and non-PouchDoc classes in Kotlin, and it seems I can't do that.
Any idea how I could make this work, or does it need a Kotlin language change?
I think your problem should be covered by https://youtrack.jetbrains.com/issue/KT-8127
Also, I've created some other related issues:
https://youtrack.jetbrains.com/issue/KT-17682
https://youtrack.jetbrains.com/issue/KT-17683
And right now You can use one of next solutions, IMO third is most lightweight.
interface PouchDoc1 {
var id: String
var _id: String
get() = id
set(v) { id = v}
var rev: String?
var _rev: String?
get() = rev
set(v) { rev = v}
}
class Impl1 : PouchDoc1 {
override var id = "id0"
override var rev: String? = "rev0"
}
interface PouchDoc2 {
var id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
}
class Impl2 : PouchDoc2 {
init {
id = "id1"
rev = "rev1"
}
}
external interface PouchDoc3 { // marker interface
}
var PouchDoc3.id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var PouchDoc3.rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
class Impl3 : PouchDoc3 {
init {
id = "id1"
rev = "rev1"
}
}
fun keys(a: Any) = js("Object").getOwnPropertyNames(a)
fun printKeys(a: Any) {
println(a::class.simpleName)
println(" instance keys: " + keys(a).toString())
println("__proto__ keys: " + keys(a.asDynamic().__proto__).toString())
println()
}
fun main(args: Array<String>) {
printKeys(Impl1())
printKeys(Impl2())
printKeys(Impl3())
}
I got a good answer from one of the JetBrains guys, Alexey Andreev, over on the JetBrains forum at https://discuss.kotlinlang.org/t/controlling-the-jsname-of-fields-for-pouchdb-interop/2531/. Before I describe that, I'll mention a further failed attempt at refining #bashor's answer.
Property delegates
I thought that #bashor's answer was crying out to use property delegates but I couldn't get that to work without infinite recursion.
class JSMapDelegate<T>(
val jsobject: dynamic
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return jsobject[property.name]
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
jsobject[property.name] = value
}
}
external interface PouchDoc4 {
var _id: String
var _rev: String
}
class Impl4() : PouchDoc4 {
override var _id: String by JSMapDelegate<String>(this)
override var _rev: String by JSMapDelegate<String>(this)
constructor(_id: String) : this() {
this._id = _id
}
}
The call within the delegate to jsobject[property.name] = value calls the set function for the property, which calls the delegate again ...
(Also, it turns out you can't put a delegate on a property in an interface, even though you can define a getter/setter pair which work just like a delegate, as #bashor's PouchDoc2 example shows.)
Using an external class
Alexey's answer on the Kotlin forums basically says, "You're mixing the business (with behaviour) and persistence (data only) layers: the right answer would be to explicitly serialise to/from JS but we don't provide that yet; as a workaround, use an external class." The point, I think, is that external classes don't turn into JavaScript which defines property getters/setters, because Kotlin doesn't let you define behaviour for external classes. Given that steer, I got the following to work, which does what I want.
external interface PouchDoc5 {
var _id: String
var _rev: String
}
external class Impl5 : PouchDoc5 {
override var _id: String
override var _rev: String
}
fun <T> create(): T = js("{ return {}; }")
fun Impl5(_id: String): Impl5 {
return create<Impl5>().apply {
this._id = _id
}
}
The output of keys for this is
null
instance keys: _id
__proto__ keys: toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,__proto__,constructor
Creating external classes
Three notes about creating instances of external classes. First, Alexey said to write
fun <T> create(): T = js("{}")
but for me (with Kotlin 1.1) that turns into
function jsobject() {
}
whose return value is undefined. I think this might be a bug, because the official doc recommends the shorter form, too.
Second, you can't do this
fun Impl5(_id: String): Impl5 {
return (js("{}") as Impl5).apply {
this._id = _id
}
}
because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least). The generic function approach skips the type-check. I'm guessing that's not a bug, since Alexey recommended it, but it seems odd, so I'll ask him.
Lastly, you can mark create as inline, though you'll need to suppress a warning :-)

Obj-C to Swift Coredata conversion

Ok, just starting to look at Swift and trying to use a Coredata model and SQLfile from an existing Obj-C program.
Created new swift Coredata project
replaced the coredata model with the existing model
pointed program to sql data file
Created Swift NSMangagedObject Subclass
Run program, get an error that class types don't match.
Remove Swift NSMangedObject Subclass and created Objective-C version
Run program, no issues, fetch executes nicely.
I want to eventually rewrite the program in swift, but I'll have to deal with old SQL data files. Is there a way to get this to work without having to blend the two languages?
I have not tried this, but is this something lightweight migration would do?
-[EDIT]---
Error message is in console:
"fatal error: NSAarry element failed to match the Swift Array Element type"
fetch Code:
let request = NSFetchRequest(entityName: "Account")
var error:NSError?=nil
var fetchResults = managedObjectContext?executeFetchRequest(request, error: &error)! as [Account]
for aAccount in fetchResults {
println(aAccount.name)
}
This code does works - IF the NSManagedObject Subclass (Account) IS Obj-C
This code does NOT work - IF the NSManagedObject subclass (Account) IS Swift - This is when I get the error.
The NSManagedObject Subclass is generated through "Create NSMangedObject Subclass" in XCode.
The CoreData Model and the SQL data file are from a previous, functioning, program when 100% Obj-C.
-[EDIT 2]-------
Unless I did something wrong, it is type cased...
var fetchResults = ...(request, error: &error)! as [Account]
To test, I changed the above to:
var fetchResults = ...(request, error: &error)! as [NSManagedObject]
with this change the following works - as you would expect:
for aAccount in fetchResults {
println(aAccount.valueForKey("name")!)
}
The following DOES NOT work (added it inside the for-in loop):
if let account = aAccount as? Account {
println(account.name)
}
So, it will NOT let me cast to the Swift Account Class. I used Xcode's "Create ManagedObject Subclass" to create the Swift subclass.
-[EDIT 3]-----
XCode Version: 6.1
Account.swift file:
import Foundation
import Coredata
class Account: NSManagedObject {
#NSManaged var name: String
#NSManaged var number: String
#NSManaged var type: String
#NSManaged var status: String
}
If I create an Obj-C version of Account (Account.h and Account.m), add them to the Bridging-Header, remove the .swift version, everything works.
You need to cast the NSManagedObject in fetchedResults to your Swift class in order to access the properties of the Swift class directly.
for aAccount in fetchResults {
println(aAccount.name) // NOT GOING TO WORK
}
aAccount will be a NSManagedObject so you must cast this to your Swift class in order to access it's property via the Swift class (aAccount.name). Otherwise you have to access the property like this
aAccount.getValueForKey("Name")
Alternately try this
for managedObject in fetchResults {
if let account = managedObject as? Account {
println(account.name)
}
}
Here is one of my Swift model classes, try adding the #objc()
import Foundation
import CoreData
#objc(AssetX)
class AssetX: BaseNode {
#NSManaged var childCostTotal: NSNumber
#NSManaged var dateOfManufacture: NSDate
#NSManaged var dateOfPurchase: NSDate
#NSManaged var dateOfValuation: NSDate
#NSManaged var invoice: NSAttributedString
#NSManaged var make: String
#NSManaged var model: String
#NSManaged var photo: NSAttributedString
#NSManaged var purchaseCost: NSNumber
#NSManaged var serialNo: String
#NSManaged var valuationCost: NSNumber
#NSManaged var category: AssetCategory
}
The base class is:
import Foundation
import CoreData
#objc(BaseNode)
class BaseNode: NSManagedObject {
#NSManaged var canCollapse: NSNumber
#NSManaged var canExpand: NSNumber
#NSManaged var details: NSAttributedString
#NSManaged var displayName: String
#NSManaged var isExpanded: NSNumber
#NSManaged var isLeaf: NSNumber
#NSManaged var isSelectable: NSNumber
#NSManaged var isSpecialGroup: NSNumber
#NSManaged var reminderDate: NSDate
#NSManaged var reminderPeriod: String
#NSManaged var sortIndex: NSNumber
#NSManaged var status: NSNumber
#NSManaged var children: NSSet
#NSManaged var linkedTo: People
#NSManaged var parent: BaseNode
var found:Bool = false
}

Gson equivalent in Objective-C

Is there any equivalent to gson in Objective-C?
Thanks.
DCKeyValueObjectMapping https://github.com/dchohfi/KeyValueObjectMapping is not a JSON parser but an object-json mapper to inject NSObject properties from NSDictionary/NSArray.
I recently used Mantle which works great and is very similar to GSON (which is use for android projects)
https://github.com/Mantle/Mantle
In Objective-C the functionality of GSON is sort of built in. Say I have a class defined like so:
#interface MyModel : NSObject
#property(nonatomic,strong) NSString *name;
#property(nonatomic,strong) NSString *address;
#end
And lets say that I have a JSON object defined like so
{
"name":"marc",
"address":"1234 Some Street"
}
Then I can use AFNetowrking to get an NSDictionary of the JSON object which is pretty easy. Finally you can just do a loop like so where dict is the dictionary returned by AFNetworking parsing the JSON and self is an instance of MyModel.
for (NSString *key in dict) {
[self setObject:dict[key] forKey:key];
}
In Java GSON uses reflection to achieve the same effect as the above loop. Its just a lot easier in objective-c so no need for a library to do it. If you have nested objects maybe AFNetworking with DCKeyValueObjectMapping.
OCMapper is very similar to Gson and easy to use
https://github.com/aryaxt/OCMapper
Json
{
"firstName" : "FirstName",
"lastName" : "LastName",
"age" : 26,
"dateOfBirth" : "01/01/2013",
"address" : {
"city" : "San Diego",
"country" : "US"
},
"posts" : [
{
"title" : "Post 1 title",
"datePosted : "04/15/2013",
},
{
"title" : "Post 2 title",
"datePosted : "04/12/2013",
}
]
}
Model
#objc public class User: NSObject {
var firstName: String?
var lastName: String?
var age: NSNumber?
var dateOfBirth: NSDate?
var address: Address?
var posts: [Post]?
}
Usage Swift
let user = ObjectMapper.sharedInstance().objectFromSource(dict, toInstanceOfClass:User.self) as User
or
let User = User.objectFromDictionary(dictionary)
Usage Objective C
User *user = [[ObjectMapper sharedInstance] objectFromSource:dictionary toInstanceOfClass:User.class];
or
User *user = [User objectFromDictionary:dictionary];
At WWDC 2017, Apple has introduced the new feature in Swift to parse JSON without any pain using Swift Codable protocol
struct YourStructure: Codable {
let name: String?
let avatarUrl: URL?
private enum CodingKeys: String, CodingKey {
case name
case avatarUrl = "avatar_url"
}
}
decoder:
let decoder = JSONDecoder()
parsedData = decoder.decode(YourStructure.self, from: YourJsonData)
encode:
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(data)
more info: Encoding and Decoding Custom Types
OCMapper is the best i know and the easiest library and it have reverse mapping as well and map complex objects without the need of configuration , and work with realmObjects as well
Yes - see http://psionides.jogger.pl/2010/03/04/cocoa-json-parsing-libraries/
I think I have found few libraries which can server this purpose but most important one seems to be RestKit