Search code examples
iosswiftclassreflection

Copy some property values from one class to another class instance in Swift5


I am writing an app in swift the connects to and stores a reference to a Bluetooth device. This Bluetooth device physically connects to a sensor (e.g., UV sensor, temperature sensor, etc.) and transmits the sensor’s readings to the app.

I have a class called Sensor that has several subclasses for each type of sensor that can be plugged into the Bluetooth device. Since the user can unplug and plug in a different sensor to this Bluetooth device, I create a new instance of my subclass depending on the sensor type and I replace the old sensor class instance with this new one. However, I haven't found a good method for copying over some of the pertinent properties that will still apply to the new sensor (I don't want all the properties copied over since some of them get set specifically depending on class whereas others will carry over regardless of which sensor is plugged into the bluetooth device).

public class Sensor {
    // Properties I don’t want copied over to new class instance
    public let peripheral: CBPeripheral    
    var sensorID: Int
    var sensorImage: UIImage 
    var modelName: String 
    var sensorType: String 

    // Properties I do want copied over to new class instance
    var bluetoothDeviceSerialNumber: String 
    var hardwareVersion: String 
    var firmwareVersion: String 
    var softwareVersion: String 
    var alias: String 
    var lastConnectedDate: Date
    var exampleProperty1: Int
    var exampleProperty2: Int
    var exampleProperty3: Int
    var exampleProperty4: Bool 
    var exampleProperty5: Bool

    required public init(withPeripheral peripheral: CBPeripheral) {
        self.peripheral = peripheral
    }
}

class UV_Sensor: Sensor {
    required public init(withPeripheral peripheral: CBPeripheral){
        super.init(withPeripheral: peripheral)
        
        var sensorID = 1
        var sensorImage = UIImage(named: “UV_Sensor”)
        var modelName = “UVSensorModelName”
        var sensorType = “UV”
    }
}

Class TemperatureSensor: Sensor {
    required public init(withPeripheral peripheral: CBPeripheral){
        super.init(withPeripheral: peripheral)
        
        var sensorID = 2
        var sensorImage = UIImage(named: “Temp_Sensor”)
        var modelName = “TemperatureSensorModelName”
        var sensorType = “Temp”
    }
}

My current solution has been to have a method that manually copies over exactly the variables that I want.

func copyPropertyValues(from oldSensor: Sensor) {
    self.bluetoothDeviceSerialNumber = oldSensor.bluetoothDeviceSerialNumber
    self.hardwareVersion = oldSensor.hardwareVersion
    self.firmwareVersion = oldSensor.firmwareVersion 
    self.softwareVersion = oldSensor.softwareVersion
    self.alias = oldSensor.alias
    self.lastConnectedDate = oldSensor.lastConnectedDate
    self.exampleProperty1 = oldSensor.exampleProperty1
    self.exampleProperty2 = oldSensor.exampleProperty2
    self.exampleProperty3 = oldSensor.exampleProperty3
    self.exampleProperty4 = oldSensor.exampleProperty4 
    self.exampleProperty5 = oldSensor.exampleProperty5
}

I'm not a fan of this idea because it doesn't scale very well in my opinion and if I or some future developer adds a new property to this sensor class, they also have to remember to add it to this method. I've tried also looking at reflection as that has seemed like the most promising method but have not had much success getting anything close to achieving what I want.


Solution

  • You've already made two groupings of properties here, with your comments and use of white space:

    public class Sensor {
        // Properties I don’t want copied over to new class instance
        public let peripheral: CBPeripheral    
        // ... group 1
    
        // Properties I do want copied over to new class instance
        var bluetoothDeviceSerialNumber: String 
        // ... group 2
    

    Groupings like this are structs screaming to get out. You might try something like this:

    public class Sensor {
        struct SensorDetails {
            var bluetoothDeviceSerialNumber: String 
            var hardwareVersion: String 
            var firmwareVersion: String 
            var softwareVersion: String 
            var alias: String 
            var lastConnectedDate: Date
            var exampleProperty1: Int
            var exampleProperty2: Int
            var exampleProperty3: Int
            var exampleProperty4: Bool 
            var exampleProperty5: Bool
        }
    
        // Properties I don’t want copied over to new class instance
        public let peripheral: CBPeripheral    
        var sensorID: Int
        var sensorImage: UIImage 
        var modelName: String 
        var sensorType: String 
    
        // Properties I do want copied over to new class instance
        var sensorDeatils: SensorDetails
    
        required public init(withPeripheral peripheral: CBPeripheral) {
            self.peripheral = peripheral
        }
    }
    

    Now your copyPropertyValues function becomes trivial:

    func copyPropertyValues(from oldSensor: Sensor) {
        self.sensorDetails = oldSensor.sensorDetails
    }