Deep copy of a structure - how to? - ios

I have the following structure, which I want to make a deep copy of, so I can treat those differently.
class NearbyStopsViewModel {
var stopViewModels: [StopViewModel]
}
class StopViewModel {
var stop: Stop
var name: String
var departures: [DepartureViewModel]?
}
class DepartureViewModel: NSObject {
var departure: Departure
var name: String
}
I having a hard time wrapping my head around making a deep copy of this structure, any ideas?

To create a 'deep' copy of a class instance you need to conform to NSCopying for those relevant classes, Structs are passed by value. I added my own implementation for the missing structures.
import Foundation
struct Departure {
var to: Stop
var from: Stop
init(from: Stop, to: Stop) {
self.from = from
self.to = to
}
}
struct Stop {
var name: String = ""
init(named: String) {
self.name = named
}
}
class NearbyStopsViewModel: NSCopying {
var stopViewModels: [StopViewModel]
init(stops: [StopViewModel] = []) {
self.stopViewModels = stops
}
func copy(with zone: NSZone? = nil) -> Any {
var stops: [StopViewModel] = []
self.stopViewModels.forEach { (stop) in
stops.append(stop.copy() as! StopViewModel)
}
let nearbysvm = NearbyStopsViewModel.init(stops: stops)
return nearbysvm
}
}
class StopViewModel: NSCopying {
var stop: Stop
var name: String = ""
var departures: [DepartureViewModel]
init(stop: Stop, named: String, with departures: [DepartureViewModel] = [] ) {
self.stop = stop
self.name = named
self.departures = departures
}
func copy(with zone: NSZone? = nil) -> Any {
var departuresCopy: [DepartureViewModel] = []
self.departures.forEach { (departure) in
departuresCopy.append(departure.copy() as! DepartureViewModel)
}
let stopvm = StopViewModel.init(stop: self.stop, named: self.name, with: departuresCopy)
return stopvm
}
}
class DepartureViewModel: NSObject, NSCopying {
var departure: Departure
var name: String = ""
init(name: String, departure: Departure) {
self.name = name
self.departure = departure
}
func copy(with zone: NSZone? = nil) -> Any {
let departure = DepartureViewModel.init(name: self.name, departure: self.departure)
return departure
}
}
// Structs are passed by Value, making a 'minimal' copy of themselves as they move.
var pennStation = Stop(named: "Pennsylvania Station")
print(pennStation)
let copyByValue = pennStation
print(copyByValue) // "Pennsylvania Station"
pennStation.name = "Penn. Station"
print(copyByValue) // "Pennsylvania Station"
// Classes are passed by Reference
let clarkLake = Stop(named: "Clark and Lake")
let stateLake = Stop(named: "State and Lake")
let clarkToState = Departure(from: clarkLake, to: stateLake)
// DepartureViewModel is your lowest level class that conforms to NSCopying
let departure = DepartureViewModel(name: "clark to state", departure: clarkToState)
print(departure) // This Memory Address should never change.
let referenceDeparture = departure
departure.name = "Unexpected delay"
print(referenceDeparture.name)
print(referenceDeparture) // Same Address as departure.
let deepCopyOfDeparture = departure.copy() as! DepartureViewModel // Copy() and mutableCopy() will return a passed-by-value copy.
print(deepCopyOfDeparture) // Different Memory Address as departure
let stopvm = StopViewModel(stop: pennStation, named: "Penn. Station", with: [departure])
print("Stop View Model's Child Departure Instance(s): \(stopvm.departures)")
let copyOfSVM = stopvm.copy() as! StopViewModel
print("Copy of SVM Child Departure Instance(s): \(copyOfSVM.departures)")
Output:
Stop(name: "Pennsylvania Station")
Stop(name: "Pennsylvania Station")
Stop(name: "Pennsylvania Station")
<StackOverflow.DepartureViewModel: 0x149dc4de0>
Unexpected delay
<StackOverflow.DepartureViewModel: 0x149dc4de0>
<StackOverflow.DepartureViewModel: 0x149da89a0>
<Stop View Model's Child Departure Instance(s): <StackOverflow.DepartureViewModel: 0x149dc4de0>
<Copy of SVM Child Departure Instance(s): <StackOverflow.DepartureViewModel: 0x149dc7630>

You can take advantage of value types, which are deeply copied by default, the caveat here is that all members are also value types (structs, enums, tuples), and those members contain only value types, and so on:
struct NearbyStopsViewModel {
var stopViewModels: [StopViewModel]
}
struct StopViewModel {
var stop: Stop
var name: String
var departures: [DepartureViewModel]?
}
struct DepartureViewModel: NSObject {
var departure: Departure
var name: String
}
struct Departure {
// fields needed for the struct
}
With a hierarchy like this, every assign to any of the above types will result in a deep copy of all underlying members. You let the compiler do all the work for you.
Beware, though, there might be some performance issues if you work with a large amount of these values (for a fair enough amount the performance thing is unnoticeable).

Related

iOS Swift - Reference between Objects

I have a shared handler class in which I 'manage' objects.
In this shared class, there is a 'main object (mainObject)' and a 'single object (singleData)'.
If I now assign the singleData with the reference to mainObject.data[index] in viewA and then change mainObject.data[index] in viewB, then the singleData object also changes. How can I avoid this strong-reference here?
==> In short: I want to change the mainObject without changing the singleObject. <==
struct kOBJECT {
let name: String
let data: [Int]
}
class HandlerClass {
let shared = HandlerClass()
var mainObject = kOBJECT(name: "AnyName", data: [1,2,3,4,5])
var singleData: Int?
}
class viewA: UIViewController {
.....
func didSelectRow(at indexPath: IndexPath) {
HandlerClass.shared.singleData = HandlerClass.shared.mainObject.data[indexPath.row] // Create Reference
viewB.indexPath = indexPath
pushToViewController(viewB)
}
}
class viewB: UIViewController {
.....
public var indexPath: IndexPath!
func someFunction() {
HandlerClass.shared.mainObject.data[indexPath.row] = 10000 // <- Does this also change the `singleData` Reference? In my case it does......
}
}
I tried the following in 'didSelectRow'
let tempValue = HandlerClass.shared.mainObject.data[indexPath.row]
HandlerClass.shared.singleObject = tempValue
You misunderstand reference meaning. Reference works only of instances - that's a part of memory reserved for specific objects. Structs are also objects, but it works as Type (similar to Int, String, Double, Float, etc), so when you modify a struct as a result you will have a new object, but for class instance, you will still modify the same object as you copied only reference to that object.
Here example below:
class Apple {
init(name: String) {
self.name = name
}
var name: String
}
struct Pear {
var name: String
}
var apple = Apple(name: "Apple 1")
var pear = Pear(name: "Pear 1")
var apple2 = apple // here we copy only reference
apple2.name = "Apple 2"
print(apple.name) // 1st object
print(apple2.name) // the same object
var pear2 = pear // here we create new object
pear2.name = "Pear 2"
print(pear.name) // 1st object
print(pear2.name) // 2nd object
Also a result (run on Playground)

Issue with adding Data to an AnyObject Var so that I could make native ads work

for postdata in postdata {
if index < tableViewItems.count {
tableViewItems.insert(postdata, at: index)
index += adInterval
} else {
break
}
}
I'll need to add both PostData ads and Native Ads on the same AnyObject Var for me to get it to work and I can't find a way to add the Post Data because it says an error appears saying "Argument type 'PostData' expected to be an instance of a class or class-constrained type." Assistance would be very much appreciated, thank you.
edit 1
class Ad {
var postimage: String!
var publisher: String!
var timestring: String!
var timestamp = Date().timeIntervalSince1970
}
class PostDataAd: Ad {
// Declare here your custom properties
struct PostData1
{
var postimage: String
var publisher: String
var timestring : String
var timestamp = Date().timeIntervalSince1970
}
}
class NativeAd: Ad {
// Declare here your custom properties
struct NativeAd
{
var nativeAds: [GADUnifiedNativeAd] = [GADUnifiedNativeAd]()
}
}
My model class to merge both Data into one AnyObject Var
and then trying to append the Data from Firebase by doing this
var ads: [Ad] = [PostDataAd(), NativeAd()]
let postList = PostDataAd.PostData1(postimage: postimage, publisher:
postpublisher, timestring: postid, timestamp: timestamp)
self.ads.insert(postList, at:0)
an error occurs saying Cannot convert value of type 'PostDataAd.PostData1' to expected argument type 'Ad'
I hope I got what you want correctly. So basically you have two objects which you want to store in one array, under AnyObject. If that is correct, I recommend you to go in a bit of different direction. It is a nice example where you can use subclassing. You can declare a class called Ad, where you define the common properties which will be true for both PostDataAds and NativeAds.
class Ad {
// Add here the common elements you want to retrieve from both classes
var name: String = ""
}
After you define your PostDataAds and NativeAds inheriting from Ad:
class PostDataAd: Ad {
// Declare here your custom properties
}
class NativeAd: Ad {
// Declare here your custom properties
}
And if you want to define an array with two types of objects you can go:
let ads: [Ad] = [PostDataAd(), NativeAd()]
When retrieving you can check their type:
if let item = ads.first as? PostDataAd {
// The Ad is a PostDataAd
} else if let item = ad as? NativeAd {
// The Ad is a NativeAd
}
Or at some cases you don't even how to know the exact type, as you can access the properties defined in Ad without checking.
Update:
First of all your PostData1 and Ad objects are the same, you don't need to duplicate them. If you really want to have two classes you can inherit PostData1 from Ad.
class Ad {
var postimage: String
var publisher: String
var timestring: String
var timestamp = Date().timeIntervalSince1970
// You can add timestamp here also if you wish
init(postimage: String, publisher: String, timestring: String) {
self.postimage = postimage
self.publisher = publisher
self.timestring = timestring
}
}
class PostDataAd: Ad {
// Define some custom properties
}
And if you want to append PostData to the [Ad] array, you would do the following:
var ads: [Ad] = []
// Replace with your values
let postList = PostDataAd(postimage: "", publisher: "", timestring: "")
ads.insert(postList, at: 0)
// Appending NativeAd works also
let nativeAdd = NativeAd(postimage: "", publisher: "", timestring: "")
ads.append(nativeAdd)

Changing one property in a variable triggers subcriptions to other properties. RxSwift

I have the following struct of properties for Chat
struct Chat {
var id = String()
var gender = String()
var date = Date()
init() {}
}
In a view controller, i declare an instance of Chat called observablechat, and then i used the flatmap operator to attempt and observe only changes in the date property of observablechat. However, if i change the gender property (as shown), the subscription gets triggered. I wonder why that is and how can i fix this code such that the subscription only looks at what happens to the date property and nothing else?
class ViewController: UIViewController {
var observablechat = Variable(Chat())
observablechat.asObservable()
.flatMap { (Chat) -> Observable<Date> in
return Observable.of(Chat.matchDate)
}.subscribe(onNext: { (r) in
print(r)
}).addDisposableTo(disposeBag)
self.observablechat.value.gender = "male"
//triggers subscription above.
}
}
First of all, why flatMap?
You need only distinctUntilChanged and map. DebugPrint will be triggered only twice: initial setup + date changed once. Check out the code below and feel free to ask questions.
import PlaygroundSupport
import RxSwift
struct Chat {
var id: String
var gender: String
var date: Date
init(id: String = "", gender: String = "", date: Date = Date(timeIntervalSince1970: 0)) {
self.id = id
self.gender = gender
self.date = date
}
}
final class YourClass {
lazy var variableChat: Variable<Chat> = Variable<Chat>(Chat())
var observableDate: Observable<Date> {
return variableChat
.asObservable()
.distinctUntilChanged({ (chatOld, chatNew) -> Bool in
return chatOld.date == chatNew.date
})
.map({ (chat) -> Date in
return chat.date
})
.shareReplay(1)
}
}
let value = YourClass()
value.observableDate
.subscribe(onNext: { (date) in
debugPrint(date.timeIntervalSince1970)
})
value.variableChat.value.gender = "male"
value.variableChat.value.date = Date()
value.variableChat.value.gender = "female"
value.variableChat.value.gender = "male"
value.variableChat.value.gender = "female"
P.S. the way to run RxSwift in playground: readme

What is the best way to connect Realm and SwiftBond

I love Realm and I love Bond. Both of them makes app creation a joy. So I was wondering what is the best way to connect Realm and Bond? In Realm we can store basic types such as Int, String, e.g. But in Bond we work with Dynamics and Bonds. The only way that I found to connect Realm and Bond is following:
class TestObject: RLMObject {
dynamic var rlmTitle: String = ""
dynamic var rlmSubtitle: String = ""
var title: Dynamic<String>
var subtitle: Dynamic<String>
private let titleBond: Bond<String>!
private let subtitleBond: Bond<String>!
init(title: String, subtitle: String) {
self.title = Dynamic<String>(title)
self.subtitle = Dynamic<String>(subtitle)
super.init()
self.titleBond = Bond<String>() { [unowned self] title in self.rlmTitle = title }
self.subtitleBond = Bond<String>() { [unowned self] subtitle in self.rlmSubtitle = subtitle }
self.title ->> titleBond
self.subtitle ->> subtitleBond
}
}
But it surely lacks simplicity and elegance and produces a lot of boiler code. Is there any way to do this better?
With Realm supporting KVO and Bond 4, you can extend Realm objects to provide Observable variants. There is some boilerplate to it, but it's clean and without hacks.
class Dog: Object {
dynamic var name = ""
dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
}
extension Dog {
class ObservableDog {
let name: Observable<String>
let birthdate: Observable<NSDate>
init(dog: Dog) {
name = Observable(object: dog, keyPath: "name")
birthdate = Observable(object: dog, keyPath: "birthdate")
}
}
func observableVariant() -> Dog.ObservableDog {
return ObservableDog(dog: self)
}
}
Than you'll be able to do:
let myDog = Dog().observableVariant()
myDog.name.observe { newName in
print(newName)
}
myDog.name.bindTo(nameLabel.bnd_text)
realm.write {
myDog.name.value = "Jim"
}
You could likely simplify the pattern you're using somewhat if you used
default property values:
class TestObject: RLMObject {
dynamic var rlmTitle = ""
dynamic var rlmSubtitle = ""
var title: Dynamic<String>
var subtitle: Dynamic<String>
private let titleBond = Bond<String>() { [unowned self] title in self.rlmTitle = title }
private let subtitleBond = Bond<String>() { [unowned self] subtitle in self.rlmSubtitle = subtitle }
init(title: String, subtitle: String) {
self.title = Dynamic<String>(title)
self.subtitle = Dynamic<String>(subtitle)
self.title ->> titleBond
self.subtitle ->> subtitleBond
super.init()
}
}
You could remove another two lines of code if Bond's ->> operator returned the
left value so you could do self.title = Dynamic<String>(title) ->> titleBond.
But ultimately, until Swift has native language support for KVO or an equivalent observation mechanism, you're sadly going to have to write some amount of boilerplate.
I've been thinking about this for three days and came up with nearly perfect solution, which does not employ any boilerplate code. First of all I have created a super class for a realm model's wrapper:
class BondRealmBaseClass {
private var realmModel: RLMObject!
private let realm = RLMRealm.defaultRealm()
private var bonds = NSMutableArray()
init(){
realmModel = createRealmModel()
realm.beginWriteTransaction()
realm.addObject(realmModel)
realm.commitWriteTransaction()
createBonds()
}
init(realmModel: RLMObject){
self.realmModel = realmModel
createBonds()
}
func createBondFrom<T>(from: Dynamic<T>, toModelKeyPath keyPath: String){
from.value = realmModel.valueForKeyPath(keyPath) as T
let bond = Bond<T>() { [unowned self] value in
self.realm.beginWriteTransaction()
self.realmModel.setValue(value as NSObject, forKey: keyPath)
self.realm.commitWriteTransaction()
}
from ->| bond
bonds.addObject(bond)
}
//MARK: - Should be overriden by super classes
func createBonds(){ fatalError("should be implemented in supreclass") }
func createRealmModel() -> RLMObject{ fatalError("should be implemented in supreclass") }
}
After that for each realm model I create two classes, first is the actual realm model, which stores all properties:
class RealmTodoModel: RLMObject {
dynamic var title = ""
dynamic var date = NSDate()
}
and a second one is the wrapper around realm model:
class TodoModel : BondRealmBaseClass{
let title = Dynamic("")
let date = Dynamic(NSDate())
override func createBonds(){
createBondFrom(title, toModelKeyPath: "title")
createBondFrom(date, toModelKeyPath: "date")
}
override func createRealmModel() -> RLMObject { return RealmTodoModel() }
}
And this two classes is actually all is needed to link Realm and Bond: creating new TodoModel will actually add to Realm new RealmTodoModel and all changes made with TodoModel's title and date will be automatically saved to corresponding Realm model!
EDIT
I added some functionality and posted this as a framework on GitHub. Here is the link.

Construct typed dictionary using swift

I would like to create a typed map (Dictionary) class to meet the following requirements:
func testMap() {
var map = ActivitiesMap()
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
logger.debug("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
In short, I want this class to both be a dictionary such that it can be used in the for loop, however I want to ensure the keys are strings and the values are Activity objects.
I tried many variations of inheriting from Dictionary or typing the class, but so far it's resulted in multiple errors.
EDIT:
I don't think a simple generic dictionary will work, such as String:Activity. I want to have extra methods in the ActivityMap class, such as getAllActivitiesBetweenDates().
I need an actual class definition, not a generic dictionary expression.
You can make it looks like dictionary by implement subscript operator
And conform to Sequence protocol to support for-in loop
struct ActivitiesMap : Sequence {
var map = [String:Activity]()
subscript(key: String) -> Activity? {
get {
return map[key]
}
set(newValue) {
map[key] = newValue
}
}
func generate() -> GeneratorOf<(String, Activity)> {
var gen = map.generate()
return GeneratorOf() {
return gen.next()
}
}
// I can't find out type of map.generator() now, if you know it, you can do
//func generate() -> /*type of map.generator()*/ {
// return map.generate();
//}
}
This works for me. Not sure what is in your ActivitiesMap class, but just typed a Dictionary
class Activity{
var title:String = "";
var uuid: String = "";
}
func testMap() {
//var map = ActivitiesMap()
var map: Dictionary< String, Activity> = Dictionary< String, Activity>();
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
println("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
testMap();
This is my output:
ACTIVITY MAP: asdf1234=C11lldb_expr_08Activity (has 2 children)
class Activity {
var title=""
var id=""
init(id:String, title:String) { self.id=id; self.title = title }
}
var activities = [String:Activity]()
let a1 = Activity(id:"a1", title:"title1")
let a2 = Activity(id:"a2", title:"title2")
let a3 = Activity(id:"a3", title:"title3")
activities[a1.id] = a1
activities[a2.id] = a2
activities[a3.id] = a3
for (id,activity) in activities {
println("id: \(id) - \(activity.title)")
}
should print
id: a2 - title2
id: a3 - title3
id: a1 - title1
(key order not guaranteed to be the same)
You can use typealias keyword to define nice name of any type.
Here is how it can be used for your code:
class Activity { /* your code */ }
typealias ActivityMap = Dictionary<String, Activity>
var activityDict = ActivityMap()
And to support custom functions you can write an extension, example bellow:
extension Dictionary {
func getAllActivitiesBetweenDates(fromDate:NSDate, toDate:NSDate) -> Array<Activity>
// your code
return []
}
}
Usage:
let matchedActivities = activityDict.getAllActivitiesBetweenDates(/*date*/, /*date*/)

Resources