I know I can get the list of class properties using Mirror(reflecting:) but I can only print them. But what if I want to set properties to them and return the mirrored object.
Somwhat like this -
let mirroredObj = Mirror(reflecting: User())
for (index, property) in mirroredObj.children.enumerate() {
property.value = <SOME_VALUE>
}
return mirroredObj
Or maybe some totally different approach to do this?
You're trying to modify a class during runtime, which is impossible in Swift.
You are able to add a dictionary [String: Any] as a property though. It can be modified during runtime.
Related
Let's say I have a json data object I would like to parse into an object of my own. I've came across two ways of doing this. The first is to use an initializer like so:
class DataModelOne {
let firstProperty: String?
let secondProperty: String?
init(json: [Sting: AnyObject]) {
self.firstProperty = json["firstProperty"] as? String
self.secondProperty = json["secondProperty"] as? String
}
}
This would be called like so:
let object = DataModelOne(json: data)
where data is your JSON that you are trying to parse.
The second method that I have come across is by using computed properties:
class DataModelTwo {
let json: [String: AnyObject]
init(json: [String: AnyObject]) {
self.json = json
}
var firstProperty: String? {
return json["firstProperty"] as? String
}
var secondProperty: String? {
return json["secongProperty"] as? String
}
}
This would be initialized in the same way as above:
let object = DataModelTwo(json: data)
Aside from the fact that you couldn't set the properties once the data has been cast using DataObjectTwo, as these are computed properties and so get-only, what are the advantages/disadvantages of parsing using these two methods?
I don't see any real advantage to the computed properties. I prefer the first option, because:
This decouples your data model from the JSON. With the first option you can instantiate a DataModelOne without any JSON, and assign properties manually.
It's clearer because the parsing happens in one place.
You don't have to keep the dictionary around like you do with the computed properties. So if the dictionary contains a lot of other data that can be discarded, it can free up some memory.
The only advantage of the computed properties I can think of is that it delays accessing the Dictionary to the last moment. If you never access a property, it will never have to reach into the Dictionary in the first place. But the increase in performance will be negligible.
Lastly, I would rename the initialiser to something like init(values:). You're not initialising it with JSON, but with a plain Swift Dictionary.
I am trying to implement Urban Airship Analytics in my app. I want to track each and every event in my app, for that I have made a different class and passed tracking data as a dictionary.
Following https://docs.urbanairship.com/platform/ios/?swift#ios-screen-tracking link for the same.
I am passing parameters as:
UAirship.shared().analytics.trackScreen("MainScreen")
let event = UACustomEvent()
event.properties = createParamDictionary(paramDict1,paramDict2)
event.track()
As event properties is readonly, I can not assign/add data to it.
And the only option I can see is adding data one by one according to its defined type.
ie.
event.setStringProperty("abcd", forKey: "abcd")
event.setNumberProperty(123, forKey: "xyz")
Which is very tedious in my case.
So My questions are:
Am I doing it correctly?
If Yes, then is there any other variable or some way from which I can directly add parameters?
I also want to add User_id for tracking particular user. Any provision for this?
Thanks.
I think that you can create a custom method to UACustomEvent class which takes a dictionary and sets values using UA defined method, something like this,
extension UACustomEvent {
func setEventProperties<T: Any>(_ values: [String: T]) {
for keyValuePair in values {
if let value = keyValuePair.value as? String {
setStringProperty(value: value, forKey: keyValuePair.key)
} else if let value = keyValuePair.value as? Int {
setNumberProperty(value: value, forKey: keyValuePair.key)
}
}
}
}
That way, you dont have to use setNumberProperty or setStringProperty each time, you want to set events. You can simply do it like this,
event.setEventProperties(["abcd": "abcd", "xyz": 123])
I have a NSManagedObject class with two relationships: courseAand courseB.
These relationships should be represented in a dynamic variable. How is it possible to change this variable from outside the class?
#objc(Universtity)
public class Universtity: NSManagedObject {
dynamic var name: String {
get {
let name = self.courseA?.name
return name!
}
}
}
For example from within a ViewController like University.name = University.courseB.name ?
I was thinking about a Notifikation, but this seems maybe a little more complicated as it could be.
And if there is no other way, how should I implement the observer inside the University class?
Thank you for every idea.
Looking at your code, you have declared a "computed" or "ready-only" variable. This is a variable whose value comes from another variable or combination of variables.
I can't see your data model, so it's not clear if you have defined a name parameter in the Core Data model. Regardless, if you have the logic is somewhat confused, because the getter you have defined means any value it may hold would be ignored anyway. You would need to define a setter to set self.courseA.name if you want to ensure the value can be written to. You don't need to worry about key-value coding notifications, because they will be triggered by the Core Data Managed Object.
public class Universtity: NSManagedObject {
dynamic var name: String {
get {
let name = self.courseA?.name
return name!
}
set(newValue) {
courseA!.name = newValue
}
}
}
Also the pattern you have used to force unwrap a non-optional value in your getter isn't optimal. I haven't edited this because that is another discussion, but I would suggest asking yourself the question am I sure why I am doing this? for every "?" and "!" you use.
I think this is impossible but I'm hoping someone knows a work around to use a unique int or string as a uibutton tag
Ideally I want something like this:
let uuid = NSUUID().UUIDString
myBTN.tag = uuid.toInt()!
It's frustrating swift only allows int as tags
If you'd like to assign a unique integer to the tag of the view as you've described you can do something like this:
myButton.tag = Int(arc4random())
That will give you a very random tag which is unlikely to collide.
This is almost certainly a poor solution to the broader context though. If adding a UUID is actually correct, consider subclassing UIButton and adding a unique identifier property instead of using the tag.
Consider, as #dimpiax suggests, storing references to your buttons as properties, most likely an array of buttons, rather than using identifiers and going back to find the related button later.
You can do a trick with a helper class and then, do it like this;
let uuid:Int = NSUUIDI.UUIDInt; // NSUUIDI() Not required here
myBTN.tag = uuid;
//Reverse (String value from tag)
println(NSUUIDI.UUIDStringFromInt(myBTN.tag));
Helper Class here;
//NSUUIDI.swift
class NSUUIDI:NSObject {
private static var _uUIDs:[String] = [String]();
class var UUIDInt:Int {
get {
let uUIDInt:Int = NSUUIDI._uUIDs.count;
NSUUIDI._uUIDs.append(NSUUID().UUIDString);
return uUIDInt;
}
}
class func UUIDStringFromInt(uUID:Int) -> String {
return (uUID < NSUUIDI._uUIDs.count) ? NSUUIDI._uUIDs[uUID]:"\(uUID)";
}
}
I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash