Can you run KVC functions in Xcode Playgrounds? - swift

I'm following a tutorial on KVC and KVO when I attempted to enter the code into a playground however it wouldn't run. I received the error "terminating with uncaught exception of type NSException". I even tried to create a single app application and entered the information into a viewController to see what happens and it still wouldn't build which provided the error that the object wasn't key coding compliant. I'd really like to see this work, what am I doing incorrectly?
import UIKit
import Foundation
//this is a reference object which means when it is copied, it will copy a reference to the same instance and not a brand new value like a value type does
class Student: NSObject {
var name: String = ""
var gradeLevel: Int = 0
}
let seat1 = Student()
seat1.setValue("Kelly", forKey: "name")

Your issue is not the playground. Your issue is that to use the Objective-C KVC mechanism, you need to mark the property with #objc.
class Student: NSObject {
#objc var name: String = ""
var gradeLevel: Int = 0
}
Adding that will fix the crash.

Related

'Cannot sort on key path, property does not exist' - RealmSwift

So I am getting this fatal error:
Terminating app due to uncaught exception 'RLMException', reason: 'Cannot sort on key path 'title': property 'Item.title' does not exist.'
I have a Class Category which contains a list of my Class Item, and the two have a Linking Objects relationship. I am using Realm, and I get the error upon clicking a Category cell in my tableview, which is supposed to bring me to a tableview of all the Items in the selectedCategory. Here is the function (the two variables are at the beginning of my file, I put them here so you can see):
var toDoItems : Results<Item>?
var selectedCategory : Category? {
didSet {
loadItems()
}
}
func loadItems() {
toDoItems = selectedCategory?.items.sorted(byKeyPath: "title", ascending: true)
tableView.reloadData()
}
Just to be clear, the selectedCategory gets set in my CategoryViewController through a tableView.indexPathForSelectedRow property, which is how it obtains its value in my ToDoListViewController, where the loadItems() function is. And here are my classes of Category and Item, (in their own files of course):
import Foundation
import RealmSwift
class Category: Object {
dynamic var name: String = ""
let items = List<Item>()
}
import Foundation
import RealmSwift
class Item: Object {
dynamic var title: String = ""
dynamic var done: Bool = false
dynamic var dateCreated: Double = 0.0
var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
}
Why am I getting the error that the Item.title property does not exist? It clearly does. I have erased and rewritten the variable, restarted Xcode, etc, and the error is still there. Thanks in advance for any help!
Your Realm object is not constructed correctly. You either need to mark the individual properties as #objc
class Item: Object {
#objc dynamic var title: String = ""
or mark the whole object as #objmembers
#objcMembers class Item: Object {
dynamic var title: String = ""
I prerfer to specify each property as it gives more more control as some I don't always want all vars managed.
To answer the part below about why the app is still crashing, all I had to do was go to my simulator, and delete the app on the simulator, then restart it. Fixed!

Strange issue with setter

I have this code and whenever i am running app it gives me EXC_BAD_ACCESS.
I printed values for self.state and newValue.rawValue by po in terminal of XCode and they have values but i dont understand why i am getting "EXC_BAD_ACCESS" error and "CoreData: warning: Unable to load class named for entity Class not found, using default NSManagedObject instead". I am getting this error when I do following
d.change = .n //d is just instance of class from where state comes
What can be a reason?
var change: A {
get { return (A(rawValue: self.state) ?? .none)! }
set { self.state = newValue.rawValue }
}
enum A: Int16 {
case a = 1
case b = 2
case c = 3
case n = 90
}
state is a property of class which inherits from NSManagedObject
#NSManaged var state: Int16
The Unable to load class named for entity Class error means that you have created the entity in the Core Data model editor but that you didn't tell it what class name to use. Core Data doesn't know that the class Class is used with the entity Class, because the names don't have to be the same so Core Data doesn't assume they are. So it uses a plain old NSManagedObject, but warns you about it.
You can't use properties of Class because you don't have one, so your app crashes.
You fix this by going to the Core Data model editor and making sure that the class name for the entity is correct.

Swift Realm getting stuck on re-adding an Object in a write block

I'm using Realm, the project is on version 1.0.0.
When I create a list of Realm Objects (with data obtained from a web API), then try to save them to the Realm using this utility function in a struct:
static func saveRealmObjects(objects: [Object]) {
defer {
// Never entered
}
for object in objects {
let realm = try! Realm()
do {
try realm.write {
print("TEST: 1: object: \(object)")
realm.add(object)
print("TEST: 2")
}
} catch {
// Never entered
}
}
}
(Please don't judge me on the exact structure, I've been toying around seeing if anything will work).
I can tell from liberal use of print statements (mostly removed above) that the function gets to TEST: 1 okay, but fails to make it to TEST: 2, for the very first Object in the list I pass to the function.
I should note this function does work the first time I use it with the data (say after wiping the simulator and launching the app afresh), but then if I recreate the Objects and try to save them again it gets stuck.
I assumed Realm would use the private key on the Objects and overwrite any if necessary. But it seems to just get stuck.
-
Then - after it's stuck - if I try and get another set of results from Realm (using a different Realm object) I get the following error:
libc++abi.dylib: terminating with uncaught exception of type realm::InvalidTransactionException: Cannot create asynchronous query while in a write transaction
FYI I'm creating a different Realm object using try! Realm()
-
For reference, here is the Object I'm trying to save:
import Foundation
import RealmSwift
class MyObject: Object {
// MARK: Realm Primary Key
dynamic var id: String = ""
override static func primaryKey() -> String? {
return "id"
}
// MARK: Stored Properties
dynamic var date: NSDate? = nil
dynamic var numA = 0
dynamic var numB = 0
dynamic var numC = 0
dynamic var numD = 0
dynamic var numE = 0
dynamic var numF = 0
dynamic var numG = 0
dynamic var numH = 0
// MARK: Computed Properties
var computedNumI: Int {
return numD + numE
}
var computedNumJ: Int {
return numF + numG
}
}
(The variable names have been changed.)
-
Hopefully I'm doing something obviously wrong - this is my first time using Realm after all.
If you have any ideas why it's sticking (perhaps it's a threading issue?), or want more info, please answer or comment. Thank you.
Being the clever clogs I am, I've literally just found the answer by reading the documentation:
https://realm.io/docs/swift/latest/#creating-and-updating-objects-with-primary-keys
The add to Realm line needed to look like this:
realm.add(object, update: true)
Where the update flag will update Objects already saved with that primary key.
-
Although it would have been nice if it either gave some sort of obvious warning or crash upon trying to add the same object, or didn't cause other queries and writes to Realm to crash.

JavaScriptCore and Swift "this class is not key value coding-compliant for the key test"

I can't for the life of me pass a dictionary (in this case containing a SKLabelNode) or any other object to JavaScriptCore in Swift. I've taken this short sample straight out of a "command line tool" template project I just created in Xcode.
import Foundation
import JavaScriptCore
import SpriteKit
let context = JSContext()
let label = SKLabelNode(text:"Test")
context.setValuesForKeysWithDictionary(["test": label])
This throws me an exception:
2014-11-16 18:07:47.104 JSCoreTest[10139:193484] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<JSContext 0x1008268d0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key test.'
I am using Xcode 6.1 on Yosemite. Can anyone reproduce my issue and solve it?
UPDATE:
I thought that perhaps it didn't work because I didn't "JSExport" the object, so I tried with this example, but I still get the same error:
protocol MyObjExport: JSExport {
var foo: String! { get }
}
class MyObj: NSObject, MyObjExport {
var foo: String! { return "bar" }
}
let context = JSContext(virtualMachine: JSVirtualMachine())
context.setValuesForKeysWithDictionary(["test": MyObj()])
You're using the wrong method. You want to use setObject:forKeyedSubscript: from JSContext and not setValuesForKeysWithDictionary: from the NSKeyValueCoding protocol (which JSContext does not conform to) i.e. :
let context = JSContext()
let label = SKLabelNode(text:"Test")
context.setObject(label, forKeyedSubscript: "test")

Private var is accessible from outside the class

This was done in Playground, just to simplify.
class MyPrivateVar
{
private var priv: String?
}
var myInstance = MyPrivateVar()
myInstance.priv = "Something"
No compiler warning. In fact auto-complete is showing priv without a problem.
My understanding is that outside the boundaries of {} of the class, I'm not supposed to be able to see a private anything, func nor var.
Am I missing something?
Access modifiers in Swift are implemented differently than other languages. There are three levels:
private: accessible only within that particular file
internal: accessible only within the module (project)
public: accessible from anywhere
Unless marked otherwise, everything you write is internal by default.
The Swift blog had a post about access control when the features were introduced in beta 4, and Apple's documentation has a chapter as well.
Note: this answer is for Swift 2
The Swift Programming Language states:
Swift provides three different access levels for entities within your
code. These access levels are relative to the source file in which an
entity is defined, and also relative to the module that source file
belongs to.
If you wan't to test private access level with Swift, the following step by step may help you.
1/ Create a new Xcode project.
2/ Create a file, MyPrivateVar.swift, and add the following code in it:
class MyPrivateVar {
private var priv: String? = nil
}
3/ Create a second file, MySecondClass.swift, and add the following code in it:
class MySecondClass {
init() {
var myPrivateVar = MyPrivateVar()
myPrivateVar.priv = "some string"
}
}
Xcode will immediatly give you a Swift compiler error message:
'MyPrivateVar' does not have a member named 'priv'
4/ Now, remove the two previous files from your project and create a single file TwoClassesInAFile.swift with the following code in it:
class MyPrivateVar {
private var priv : String? = nil
}
class MySecondClass {
init() {
var myPrivateVar = MyPrivateVar()
myPrivateVar.priv = "some string"
}
}
This time, you will get no Swift compiler error message and you will be able to access MyPrivateVar's priv private property from MySecondClass because priv and MySecondClass are in the same file (your TwoClassesInAFile.swift file).
Furthermore, access levels also work for global variables. For example, Xcode won't give any compiler error if the following code is part of the same ViewController.swift file:
import UIKit
private var globalPrivate : String? = nil
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
globalPrivate = "some string"
println(globalPrivate)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
However, if you define globalPrivate outside of ViewController.swift, Xcode will generate an error message:
Use of unresolved identifier 'globalPrivate'