Within a Swift class derived from an Obj-C based framework (but could just as easily be a Swift class with an #objc attribute) I declare two stored properties:
var optInt: Int?
var optString: String?
Only optString is being exposed to Obj-C via the generated -Swift.h header.
String? is presumably fine because it is exposed using an NSString object which can be nil, so the bridging has a way to represent no value.
If I remove the ? from optInt it's exposed with an NSInteger type, so I can see that for non-optional integers it avoids objects and bridges value type to value type, but does this literally mean that an Int? can't be exposed?
I can't seem to find any documentation that explicitly says this is the case. There is a whole list of incompatible Swift features here that this doesn't appear on: Using Swift from Objective-C
The use case here is the classic situation requiring the passing of a numeric ID which could legitimately be zero. In the pre-Swift world NSNumber and nil is exactly how I went about implementing this, but it just feels wrong to be trying to migrate a class to Swift but then hanging on to Obj-C types within the Swift class specifically for this reason.
I suppose I had envisaged that an Int? unlike Int would bridge as an NSNumber in the background, with its potentially nil value feeding the "has no value" element of the optional in Swift.
Is there anything I'm missing here? To reiterate, can a Swift Optional Int (Int?) be exposed to Objective-C via bridging?
The problem with exposing an Int? property as NSNumber* is that you could store a non-Int-compatible NSNumber to it from Objective-C.
You can make a computed NSNumber? property to wrap your Int? property. The getter would simply return the Int? variable. The setter would set the Int variable from -[NSNumber integerValue].
Here's a concrete answer for the solution described above:
private var _number: Int?
public var number: NSNumber? {
get {
return _number as NSNumber?
}
set(newNumber) {
_number = newNumber?.intValue
}
}
// In case you want to set the number in the init
public init(number: NSNumber?) {
_number = number?.intValue
}
Related
I'm already aware of how private(set) works. But the below code is give compile-time error,
class Person {
private(set) let name: String //Error.
private(set) let age: Int //Error.
init(name: String, age: Int){
self.name = name
self.age = age
}
}
Error:
'private(set)' modifier cannot be applied to read-only properties
Since name and age are not read-only properties, it shouldn't give such an error.
If I use let instead of var, it is working fine. Just trying to know why?
private(set) let is a contradiction in terms. Any setter requires a variable.
Please be aware that assigning a default value to a property is not setting the property in terms of initialization. For this reason the didSet property observer is not called after assigning a default value to a property.
In Swift there is only one case to use private(set): If the class contains code which modifies the variable
class Foo {
let name : String
private(set) var expiryDate : Date
init(name: String, expiryDate: Date){
self.name = name
self.expiryDate = expiryDate
}
func extendExpiryDate(to newDate : Date) {
expiryDate = newDate
}
}
If the property is only initialized during init it's a constant – as correctly mentioned in the other answers – so declare it as let constant. Unlike other programming languages Swift provides explicit constants with the benefit of more security, less memory usage and better performance.
Let are constants ... you cant change their value one you assign them. Thats why they are considered readOnly ... so error is valid ... either convert them to var if you want to set them after assign them any value or remove private(set) and make them just private
happy coding =)
They are read-only, because you’ve declared them with let, not var.
You don't need a private setter when it is a let constant.
Just initialize it in the constructor or on the same line as the declaration.
If you want to change it outside of the constructor, you need to make it a var
From the Swift.org under Getters and Setters
You can give a setter a lower access level than its corresponding
getter, to restrict the read-write scope of that variable, property,
or subscript. You assign a lower access level by writing
fileprivate(set), private(set), or internal(set) before the var or
subscript introducer.
Related post: Swift: What does this error: 'private(set)' modifier cannot be applied to read-only properties mean?
I have been trying for some time to inspect a Swift class, and determine if any of the properties are declared as dynamic. My example class is as below:
class SwiftTestClass : DBObject {
dynamic var SwiftTestString : String!
dynamic var SwiftTestNumber : NSNumber!
dynamic var lowercaseField : String!
var nonDynamicVariable : String!
func testThyself() {
SwiftTestClass.query().fetchLightweight().removeAll()
let newObject = SwiftTestClass();
newObject.SwiftTestString = "hello, world"
newObject.SwiftTestNumber = 123
newObject.lowercaseField = "lowercase"
newObject.nonDynamicVariable = "should not be persisted"
newObject.commit()
let result = SwiftTestClass.query().fetch().firstObject;
print(result)
}
}
I am basically trying to pick out the fact that the property nonDynamicVariable is not declared as dynamic as the rest of them are.
DBObject is a subclass of NSObject.
I have tried:
Looking at the type encoding of the property, they are identical (type for type)
Seeing if they have a difference in the method implementations, they do not. (e.g. class_getMethod), the dynamic properties still have getter/setter methods.
Grabbing the Ivars to see if there is any difference there
Looking at all of the property attributes, also identical.
What I do know:
If I try to class_replaceMethod for the <propertyName>/set<propertyName>, it works for a dynamic property (as you would expect, because it adds objc compatibility) but fails to work (but does replace?, well, the memory address of the method changes!) or be actioned on the non dynamic property.
Does anyone know how to differentiate the two property declarations in swift from objc?
Thanks
I have a Swift project that contains two UITableViewControllers. The second UITableViewController is linked to a MVC model called Model. According to the UITableViewCell I select in the first UITableViewController, I want to initialize some properties of Model with Ints or Strings. Therefore, I've decided to define those properties with Printable protocol type. In the same time, I want to perform Key Value Observing on one of these properties.
Right now, Model looks like this:
class Model: NSObject {
let title: String
let array: [Printable]
dynamic var selectedValue: Printable //error message
init(title: String, array: [Printable], selectedValue: Printable) {
self.title = title
self.array = array
self.selectedValue = selectedValue
}
}
The problem here is that the following error message appears on the selectedValue declaration line:
Property cannot be marked dynamic because its type cannot be
represented in Objective-C
If I go to the Xcode Issue Navigator, I can also read the following line:
Protocol 'Printable' is not '#objc'
Is there any workaround?
There is no way to do what you want. Non-#objc protocols cannot be represented in Objective-C. One reason is that Non-#objc protocols can represent non-class types (and indeed, you said that you wanted to use it for Int and String, both non-class types), and protocols in Objective-C are only for objects.
KVO is a feature designed for Objective-C, so you must think about what you expect it to see from the perspective of Objective-C. If you were doing this in Objective-C, you would not want to have a property that could either be an object like id or a non-object like int -- you can't even declare that. Instead, as you said in your comment, you probably want it to be just objects. And you want to be able to use Foundation's bridging to turn Int into NSNumber * and String into NSString *. These are regular Cocoa classes that inherit from NSObject, which implements Printable.
So it seems to me you should just use NSObject or NSObjectProtocol.
Unfortunately ObjC does not treat protocols as types, they are just a convenient way of grouping members. Under the covers they are of type Any, so regretfully you will have to make the property Any and cast to Printable.
The best I can thing of is:
dynamic var selectedValue: Any
var printableValue : Printable {
get {
return (Printable)selectedValue
}
set {
selectedValue = newValue
}
}
Xcode6 ios swift
I have created my own class and trying to make an autogetter and autosetter, but i don't really know if it's allowed.
var Birthday:NSDate {
get {return birthday}
set(newValue){birthday = newValue}
}
var BirthYear:Int32 {
get {}
set {}
}
The last part of code triggers error, missing return, so my question is that - Is there any possibility to make getter and setter without making a second variable
Stored properties in swift are backed by hidden instance variables - the property itself is its own getter and setter, unless you implement it as a computed property, in that case you have to provide your own getter and/or setter. So when you write:
var birthday: NSDate
you use it as:
let value = classInstance.birthday
to read its value, and
classInstance.birthday = someDate
to assign a new value. You don't have to do anything special to make that work.
Suggested reading: Properties
Side note: by convention variables and property should use lower camel case notation, so they should start with lowercase, and if made up of multiple words, make the first letter of each word in uppercase. For instance:
var single: Int
var multipleWordsVariable: String
I have a little strange issue which I can't seem to figure out, I have a simple entity with a custom NSManagedObject subclass:
#objc(EntityTest) class EntityTest: NSManagedObject {
#NSManaged var crDate: NSDate
#NSManaged var name: String
#NSManaged var completed: Bool
#NSManaged var completedOn: NSDate
}
This is the problem, I can create the object fine and set the all the values and store in in an array. However late on, when I try to retrieve the same object, I can set all the values EXCEPT the "completed" field. I get a run-time error saying "EXC_BAD_ACCESS", I can read the value, just can not set it.
The debugger points to:
0x32d40ae: je 0x32d4110 ; objc_msgSend + 108
0x32d40b0: movl (%eax), %edx
Maybe some issues due to it being treated as an Objective-C class and trying to send a message to set boolean which I know is a bit funny with CoreData originally representing them as NSNumbers.
Any ideas? I created the class myself, it is not generated.
EDIT:
entity.crDate = NSDate() // succeeds
entity.completed = false // fails
entity.completed.setValue(false, forKey: "completed") //succeeds
So for setting the bool, using the setValue of NSManagedObject works but not the direct setters, though for the non-bool properties, I can set it using the setters.
UPDATE:
While checking this a bit more, it seems like the first time I set the value after getting from NSEntityDescription, it uses normal Swift accessor methods. Later on when I try to access the same object (which was stored in an array) it attempts to treat it as a Objective-C style object and sends a message for method named "setCompleted". I guess it makes sense since I use the dot notation to access it and I used the #objc directive.
I tested this by creating a "setCompleted" method, however in the method I set the value using "completed = newValue" which makes a recursive call back to "setCompleted" causing it to crash... Strange, so at this moment still can't don't have a proper fix. It seems to only happen with Bools.
Only workaround is use the "setValueForKey" method of NSManagedObject. Perhaps file this as a bug report?
If you let Xcode 6 Beta 3 create the Swift files for your entities, it will create NSNumber properties for CoreDatas Boolean type.
You can however just use Bool as a Swift type instead of NSNumber, that worked for me without using the dot syntax though.
It will set the Swift Bool with a NSNumber, that maybe leads to a bug in the dot syntax.
To make it explicit you should use the type NSNumber for attributes in the entity with the Boolean type. Then create a computed property (in iBook The Swift programming language under Language Guide -> Properties -> Computed Properties) to return you a Swift Bool. So would never really store a Bool.
Like so:
#NSManaged var snack: NSNumber
var isSnack: Bool {
get {
return Bool(snack)
}
set {
snack = NSNumber(bool: newValue)
}
}
Of course it would be cool to hide the other (NSNumber attribute), but be patient and Apple will implement private attributes in the future.
Edit:
If you check the box create skalar types it will even use the type Bool in the automatically created Swift file!
So I think it is a bug.
Following on from CH Buckingham who is entirely correct. You are attempting to store a primitive type in core data where it is expecting an NSNumber.
The correct usage would be entity.completed = NSNumber.numberWithBool(false)
This is also why you cannot retrieve this completed value as a bool directly and thus you would need to write:
var: Bool? = entity.completed.boolValue()
You can downcast your property from NSNumber to Bool type like this:
var someBoolVariable = numberValue as Bool
It works for me in this way:
self.twoFactorAuthEnabledSwitch.enabled = userProfile?.twoFactorEnabled as Bool
In XCode 8 Just set :
user.isLoggedIn = true
its works like a charm
I haven't touched swift, but in Objective-C, a BOOL is not an object, and cannot be an object. It's a primitive, and it looks like you are attempting to tell an Objective-C class to treat a BOOL like an object. But I could be making that up, as I'm not familiar with what #NSManaged does under the hood.
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/FoundationTypesandCollections/FoundationTypesandCollections.html
I found that it works fine if you specify the class name and module in the data model (instead of leaving the default NSManagedObject).
Once I set that, I can use Bool, Int16, Int32, etc., without any problems.