Best way to serialize optional property which has a custom class - swift

I would to use custom classes with optional types in realm.
In order to serialize and work with a CLLocation instance my idea is this:
class MyClass : Object {
dynamic var _coordinates :NSData?
var coordinates :CLLocation? {
get {
if _coordinates == nil {
return nil
}
// we can cache it too to avoid deserialization every time
let coordinatesObj : CLLocation = NSKeyedUnarchiver.unarchiveObjectWithData(_coordinates!) as! CLLocation
return coordinatesObj
}
set(newCoordinates) {
if newCoordinates == nil {
_coordinates = nil
} else {
_coordinates = NSKeyedArchiver.archivedDataWithRootObject(newCoordinates!)
}
}
}
...
}
Is there a better way?
Should we have some sort of protocol in Realm which allow us to return optional NSData for a specific property?
Another idea is to have a custom method like ignoredProperties which can be used to implement the logic to convert an object to NSData? and viceversa for a set of custom properties.

I don't really think there is a better way than that. If you're looking to serve more complex objects than the ones that Realm supports, you'll always need to implement some additional logic to serialize/deserialize that data.
Personally, I'm a fan of the latter method you suggested: add a new property, mark it as ignored, and then manually implement its accessors to perform the necessary logic on the related Realm-backed property.

At this time there is not another way or workaround to manage my issue.
However I've found another much clearner way to handle this needs.
In fact the main concept is the same but it uses flatMap in order to avoid some boilerplate checks.
Hope it helps.
class MyClass: Object {
dynamic var _coordinates: NSData?
var coordinates: CLLocation? {
get {
return _coordinates.flatMap(NSKeyedUnarchiver.unarchiveObjectWithData) as? CLLocation
}
set {
_coordinates = newValue.flatMap(NSKeyedArchiver.archivedDataWithRootObject)
}
}
}

Related

Subclassing UserDefaults

tldr; why do we always use UserDefaults.standard instead of subclassing UserDefaults to make something that more precisely fits our needs?
Has anyone out there subclassed UserDefaults before? Or is that considered bad practice?
Say, for example, that we make a ColorDefaults subclass of UserDefaults. When the app, the ColorDefaults object is instantiated, and that object loads all its own data. And the loaded data can then by sent to an appropriate object via delegation, or made universally available via a singleton.
My running theory is that UserDefaults is only meant to store relatively amounts of data, so having to use a singleton enforces that idea.
Bottom line: do we use UserDefaults.standard because:
subclassing is frowned upon
we're supposed to avoid saving too much data to UserDefaults in general
there's just not much value in subclassing anyway?
pretty much anything else.
Your ColorDefaults should not be a subclass of UserDefaults. It should be a plain struct or class with computed properties that are backed by UserDefaults.
Here is an example using static properties but you could refactor this to use a singleton class instead.
struct ColorDefaults {
static var someDefault: String {
get {
return UserDefaults.standard.string(forKey: "someKey") ?? "some initial value"
}
set {
UserDefaults.standard.set(newValue, forKey: "someKey")
}
}
}
let someVal = ColorDefaults.someDefault // read
ColorDefaults.someDefault = "hello" // write
This would also be useful if one of your defaults was more complicated and needed to be encoded/decoded for UserDefaults. The logic goes in here and not all over your app.
Note that such a class should only be used to store small bits of preferences, not full blown app data.
User defaults are a system of storage on file. There is little sense in subclassing unless you want to change some of its logic. But you can create multiple suits like UserDefaults(suiteName: String). What do you expect you would do with subclassing? You could simply just globally define let myDefaults = UserDefaults(suiteName: String) and use it anywhere. I guess you could use methods like
class MyDefaults: UserDefaults {
func saveName(_ name: String) {
setValue(name, forKey: "name_key")
}
}
But then again it might make more sense to just create an extension
extension UserDefaults {
func saveName(_ name: String) {
setValue(name, forKey: "name_key")
}
}
Or make it a bit more complex:
extension UserDefaults {
struct User {
static let defaults = UserDefaults(suiteName: "User")
static func saveName(_ name: String) {
defaults.setValue(name, forKey: "name")
}
}
struct General {
static let defaults = UserDefaults.standard
static func saveLastOpened(date: Date) {
defaults.setValue(date, forKey: "last_opened")
}
}
}
But all of these have one fatal flow: Now you are dependent on using user defaults within the app. At some point you may find the need to rather save these data in some other form like a local JSON file synced with iCloud. I guess UserDefaults.User could be modified to do so but would be very ugly. What we want is not UserDefaults.User.saveName("My name") but User.saveName("My name"). From the interface perspective we do not care where this user name is saved and if a new system is introduced to save these data we don't want the change in interface.
In other words, imagine you are using UserDefaults.User.saveName on 100 places in your application and now want to use another system for saving user name. You will now need to change your code on 100 places to use AnotherSystem.User.saveName while if you simply use User.saveName the interface is still valid.
So the bottom line is there is no sense in (extensively) modifying, extending or subclassing UserDefaults because it is better creating a system that wraps UserDefaults and may later be changed to any other system.
Seems you are looking for something like this
class ColorDefaults : NSObject
{
/// Save Data
class func saveDataInDefaultForKey(_ key: String, _ data: Any){
UserDefaults.standard.set(data, forKey: key)
}
/// Retrieve data
class func retrieveDataFromDefaultsWithKey(_ key: String) -> Any {
return UserDefaults.standard.value(forKey: key) as Any
}
}
Save and get data:
/// Save Data
ColorDefaults.saveDataInDefaultForKey("myArray", myArray)
ColorDefaults.saveDataInDefaultForKey("myString", myString)
/// Get Data
if let valueString = ColorDefaults.retrieveDataFromDefaultsWithKey("myString") as? String {
print("Saved Value String: \(valueString)")
}
else {
print("Error retrieving myString")
}
if let valueArray = ColorDefaults.retrieveDataFromDefaultsWithKey("myArray") as? [String] {
print("Saved Value Array: \(valueArray)")
}
else{
print("Error retrieving myArray")
}
Output:

cant set stored property with associated object in extention

I have custom class, and I want to extend it and add stored property, one of solutions I found is to do it with Associated objects. My Code looks like that:
import ObjectiveC
var kSomeKey = "s"
extension ProductCategory {
var parent: ProductCategory? {
get {
return objc_getAssociatedObject(self, &kSomeKey) as? ProductCategory
}
set(newValue) {
objc_setAssociatedObject(self, &kSomeKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
print(parent ?? "not set")
}
}
}
I make setting of this property like that:
private func makeConnectionsWithParents(forArray sorce: ProductCategory) {
for var cheesItem in sorce.childCategories! {
if cheesItem.childCategories != nil {
cheesItem.parent = sorce
makeConnectionsWithParents(forArray: cheesItem)
}
}
}
in debug I always get nil, but in set method, the newValue is received properly.
Could you, please , advice, where is the issue with this?
what is interesting, when apply this approach to standard items like UINavigationController, it works properly.
it only works right for classes (not structs) and on top only for those that are objc compatible.
for a workaround, see also: https://wezzard.com/2015/10/09/associated-object-and-swift-struct/

Self in protocol always need to be optional?

Example:
internal protocol PropertyProtocol {
var property: Self {
get
}
}
The only option I see to implement it, let us say in a class is
internal final class PropertyClass: PropertyProtocol {
let property: PropertyClass
internal init(otherOne pOtherOne: PropertyClass) {
self.property = pOtherOne
}
}
But then I do not see a possibility to use it.
let test: PropertyProtocol = PropertyProtocol(...) // hmm, how?
Does Self in a protocol property type declaration always have to be optional?
As a stored property, indeed it would have to be optional for you to create an instance, as each instance would require the stored property to be assigned during initialisation – leading to recursive behaviour. Therefore Self doesn't make too much sense as a stored property; it's really more designed to be used with methods or calculated properties.
Depending on what you're using this for (seems like a fairly hypothetical example), you could implement a calculated property like so:
protocol PropertyProtocol {
var property : Self { get }
}
final class PropertyClass : PropertyProtocol {
var property : PropertyClass {
get {
return // ...
}
set {
// ...
}
}
}
That way the class itself can manage the creation of the property when it's accessed, preventing the recursive behaviour of requiring it to be assigned during initialisation.

Object wrapper (surrogate) forwarding methods in Swift

CBPeripheral is a painful object to test as it can not be instanced by itself. Therefore, I'm using a wrapper (HBRPeripheralWrapper) around it (also for other purposes).
I would like to forward most of call on the wrapper
(HBRPeripheralWrapper) to the actual wrapped object CBPeripheral.
It technically works using forwardInvocation but how can I adopt a similar pattern in Swift?
PS: NSInvocation is not available in Swift
class HBRPeripheralWrapper {
let peripheral:CBPeripheral
// I would like to avoid this "manual" forwarding
var identifier: NSUUID {
get {
return peripheral.identifier
}
}
init(peripheral:CBPeripheral) {
self.peripheral = peripheral
}
// target forwarding is great, but how can I make the compiler happy?
override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? {
if(self.peripheral.respondsToSelector(aSelector)) {
return self.peripheral
}
return super.forwardingTargetForSelector(aSelector)
}
}
Instead of making HBRPeripheralWrapper, consider extending CBPeripheral
extension CBPeripheral {
// add whatever else you need here.
}
You don't say exactly what you do in HBRPeripheralWrapper other than forwarding, so I can't provide more help.

Is there any way to get the list of attributes of a class without any instantiated object?

I already know that we can get the list of attributes of a object using reflection in Swift, but all the examples that I found and have implemented so far uses a instantiate object. Something like that:
func attributeList() -> [String] {
var attributeList = [String]()
let serializableMirror = Mirror(reflecting: self) //using instantiate object
for childMirror in serializableMirror.children {
if let label = childMirror.label {
attributeList.append(label)
}
}
return attributeList
}
My question is, there is any way to get the attributes of a class without any reference to it? Some kind of static method where I pass my desired class type and get the attributes list of it.
In all implementations of reflection that I've worked with you need an object for the reflection to work on.
Whether it's one you provide or one the system creates for you, you have to have a concrete object.