How to find value difference of two struct instances in Swift - ios

I have two instances from the same struct in Swift. I need to find out key-values that have the same keys but different values.
For example:
struct StructDemo {
let shopId: Int
let template: String?
}
let a = StructDemo(shopId: 3, template: "a")
let a = StructDemo(shopId: 3, template: "different a")
// My expectation is to return the change pairs
let result = [template: "different a"]
My approach is as show below but comes errors.
static func difference(left: StructDemo, right: StructDemo) -> [String: Any]{
var result:[String: Any] = [:]
for leftItem in Mirror(reflecting: left).children {
guard let key = leftItem.label else { continue }
let value = leftItem.value
if value != right[key] { // This is the problem. Errror message: Protocol 'Any' as a type cannot conform to 'RawRepresentable'
result[key] = right[key]
}
}
}
Appreciate for any suggestion and solutions.
Thank you

The problem that you are seeing is that you referred to
right[key]
but right is a StructDemo and is not subscriptable. You can't look up fields given a runtime name. Well, you can with Mirror which you correctly used for left, but you did not mirror right.
Using Mirror will lead to other issues, as you will have expressions with static type Any where Equatable will be required in order to compare values.
IMHO, your best bet is to avoid a generic, reflective approach, and just embrace static typing and write a custom difference functions that iterates all the known fields of your type. Hard coding is not so bad here, if there is only one struct type that you are trying to diff.
If you have a handful of struct types each needing diffs then that might be a different story, but I don't know a good way to get around the need for Equatable. But if you have a ton of diffable types, maybe you want dictionaries to begin with?

Related

Advantages/Disadvantages of parsing data using initializer vs computed properties

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.

RxSwift, chain dependent downloads returning same Observable type

I'm downloading a list of books from an API, and for each book I want to download its cover image.
I'm stuck with what I think it's a very simple and common task in Rx. I've done a research but I cannot find the solution because all the related questions are about how to return another Observable; for example get Github repositories and then get the issues for each one, but returning Observable<Issue> not a modified Observable<Repository>, that is my case. In my case I want to modify the previous observable result (Observable<[Book]>) with the result of another request returning different observable (Observable<Data>).
For now I have the first part, download books:
func search(query: String) -> Observable<[Book]> {
return networkSession.rx
.data(request: URLRequest(url: url))
.map({ try JSONDecoder().decode([Book].self, from: $0) })
}
Book is a simple struct:
struct Book {
let title: String
let authors: [String]
let coverImageURL: URL
var coverImage: Data?
}
After that I'm able to download each image but I don't know how to assign it to each object in order to return the same Observable<[Book]> type without messing with nested observables and a zillion of compile time errors. I'm pretty sure this is a common scenario to use flatMap but it is still kind of obscure to me.
Thank you so much!
Perhaps one should split the Book struct into two structs. The first one is called BookInfo here and an array of them is downloaded with calling the function search.
struct BookInfo {
let title: String
let authors: [String]
let coverImageURL: URL
}
Composing a BookInfo instance together with the coverImage data could result in Book struct like this:
struct Book {
let bookInfo: BookInfo
let coverImage: Data
}
The RxSwift chain would look like this:
self.search(query: "<someQuery>")
.flatMap { (bookInfos: [BookInfo]) -> Observable<BookInfo> in
Observable.from(bookInfos)
}
.flatMap { (bookInfo: BookInfo) -> Observable<Book> in
//use the coverImageURL from the closure parameter and fetch the coverImage
//return an Observable<Book>
}
.observeOn(MainScheduler.instance)
.do(onNext: { (book: Book) in
//e.g. add a book in main thread to the UI once the cover image is available
})
.subscribe()
.disposed(by: disposeBag)
Instead of using type inference I added explicit type specifications, so it is more obvious what kind of elements are passed through the chain.
UPDATE
If you want to use just one Book struct, you could use this:
self.search(query: "<someQuery>")
.flatMap { (books: [Book]) -> Observable<Book> in
Observable.from(books)
}
.flatMap { (book: Book) -> Observable<Book> in
//use the book.coverImageURL from the closure parameter and fetch the coverImage
//since the closure parameter 'book' is a let constant you have to construct a new Book instance
//return an Observable<Book>
}
...
Please note that the closure parameters are let constants. This means that you cannot change the coverImage property even though it is defined as var in the structure. Instead, you must create a new instance of the Book structure, fill it with the values from the Closure parameter, and add the coverImage data. Accordingly, if you want to go this route, you could also optionally change coverImage to be a let constant.
I guess I personally would a have a slight preference for the first approach with the two structs.

Swift: Mirror(reflecting: self) too slow?

I am trying to make a dictionary with the properties of a class of mine.
class SomeClass() {
var someString = "Hello, stackoverflow"
var someInt = 42 // The answer to life, the universe and everything
var someBool = true
func objectToDict() -> [String: String] {
var dict = [String: String]()
let reflection = Mirror(reflecting: self)
for child in reflection.children {
if let key = child.label {
dict[key] = child.value as? AnyObject
}
return dict
}
}
but objectToDict() is very slow. Is there a way to speed this up, or may be another approach to add the property values to a Dictionary?
I do not agree with most other users. Using reflection results less code, which means less time to develop, maintain, and test your product. With a well written library like EVReflection you don't need to worry about things behind the scene too much.
However, if performance is going to be a concern, do NOT use reflection based approaches at all. I'd say it's never really a problem in front-end development for me, especially in iOS, but it cannot be ignored in back-end development.
To see how slow it can be, I ran some test in Xcode. I'll write a blog about it, but generally speaking, getting Mirror is not the worst part (plus it may be possible to catch property list in memory), so replacing it with objc runtime wouldn't change the situation too much. In the other hand, setValue(_, forKey) is surprisingly slow. Considering that in real life you also need to perform tasks like checking dynamicType and so on, using the dynamic approach surely will make it 100+ times slower, which won't be acceptable for server development.
- Looping 1,000,000 times for a class with 1 `Int` property
- Getting mirror: 1.52
- Looping throw mirror and set `child.value`: 3.3
- Looping throw mirror and set `42`: 3.27
- Setting value `42`: 0.05
Again, for iOS I'll keep using it to save my time. Hopefully end customers won't care about whether it's 0.005 seconds or 0.0005 seconds.
Not only is that slow, it's also not a good idea: mirroring is for debug introspection only. You should instead construct the dictionary yourself. This ensures that you have the flexibility to store all the data in exactly the right way, and also decouples your Swift property names from the keys of the dictionary you're generating.
class SomeClass {
var someString = "Hello, stackoverflow"
var someInt = 42 // The answer to life, the universe and everything
var someBool = true
func objectToDict() -> [String: AnyObject] {
return ["someString": someString, "someInt": someInt, "someBool": someBool]
}
}

Linking 2 strings (or arrays)

I'm trying to make a connection between multiple arrays. Example; (Mario bros)
var names = ["Mario", "Luigi"]
var colors = ["Red", "Green"]
Instead of making if-statements such as if names == "Mario" && colors == "Red" I would like to make an easier connection - just like buttons has tags I would like to make a String-tag ("Mario".tag = 1 and "Red".tag = 1)
Note that I have 10 different arrays such as the above.
Rather than having 10 parallel arrays, I suggest you create a struct with 10 properties, and make an array of those structs.
Structs are a package of data that abstracts away the details of the contents, and lets you deal with the data as a whole.
I would recommend you check out the Swift Programming guide (in its entirity). It's very well written. In particular, here is the page on Classes and Structs.
In addition, if you want to compare one struct to, say, Mario (as you do in your example), you could implement a method == and make your struct conform to the Equatable protocol, which will allow you do something like:
if someCharacter == Mario {... //automatically compares all properties.
See The Swift Programming Language (Swift 2.2) - Protocols.
First of all you need a ModelValue
struct Element {
let name: String
let color: String
}
Now given
let names = ["Mario", "Luigi"]
let colors = ["Red", "Green"]
you can create a list of Element(s).
let elements = zip(names, colors).map { Element(name: $0.0, color: $0.1) }
Finally your can use it
elements[0].name
elements[0].color

error: Generic parameter 'R.Generator.Element' cannot be bound to non-#objc protocol type 'AnyObject'

I am querying HealthKit and saving it to CoreData. I fetch the data in a separate class. In TableViewController I append the data to an array:
if NSUserDefaults.standardUserDefaults().boolForKey("weightSwitch") == true {
xAxisDatesArray.append(cdFetchWeight.queryCoreDataDate())
yAxisValuesArray.append(cdFetchWeight.queryCoreDataData())
and pass it at tableView.dequeueReusableCellWithIdentifier
myCell.xAxisDates = xAxisDatesArray[indexPath.row]
myCell.yAxisValues = yAxisValuesArray[indexPath.row]
In UITableViewCell I initialise the variables (yAxisValues, xAxisDates) and pass them into a charting library which takes the x and y values and plot a chart.
class TableViewCell: UITableViewCell, TKChartDelegate {
var xAxisDates = []
var yAxisValues = []
plot....
I need to get the min and max values of yAxisValues so that I can set the appropriate y-axis range to the data.
I have tried to get the min and max with the following code:
func rangeMinAndMax(){
let minYvalue = minElement(yAxisValues)
let maxYvalue = maxElement(yAxisValues)
}
But this generates the error: Generic parameter 'R.Generator.Element' cannot be bound to non-#objc protocol type 'AnyObject'
- Question: Why and how can I fix it?
Any help would be much appreciated !
The thing to do in a situation like this is to throw away all the misleading dross and boil it down to the simplest possible case:
let arr : [AnyObject] = [1,2,3]
let min = minElement(arr) // same error: "Generic parameter blah-de-blah..."
So, you see, minElement doesn't work on an array of AnyObject, and that's what the error is telling you. And the reason is obvious: an AnyObject is not a Comparable. There is no "minimum" for a bunch of AnyObject things; the entire concept of one AnyObject being "less than" another AnyObject is undefined. You need to cast your array down to an array of something that minElement can work on, namely a Comparable of some kind.
For example, in that code, I can fix the problem like this:
let arr : [AnyObject] = [1,2,3]
let min = minElement(arr as [Int])
That is the sort of thing you need to be doing. Of course, what you cast down to depends upon what these elements actually are. It looks to me as if will probably be Double and NSDate respectively, but that's just a guess; I don't know what's in your arrays. You do (presumably). Note that an NSDate is not a Comparable so you will have a bit more work to do with that one.

Resources