cant set stored property with associated object in extention - swift

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/

Related

ReSwiftRecorder Add Action with property

Recently I have used ReSwift API, And I want to add ReSwiftRecorder too!
The sample of ReSwiftRecorder in Github is very simple app
I need to to something more complicated. I have an object which get data from server and I need to It reloads its data when app is not connected to net. Here is my code:
AppState:
struct AppState: StateType {
var menus: Result<[Menu]>?
}
MenuReducer:
func menusReducer(state: Result<[Menu]>?, action: Action) -> Result<[Menu]>? {
switch action {
case let action as SetMenusAction:
return action.menus
default:
return state
}
}
AppReducer:
struct AppReducer: Reducer {
func handleAction(action: Action, state: AppState?) -> AppState {
return AppState(
menus: menusReducer(state: state?.menus, action: action),
)
}
}
MenuActions:
struct SetMenus: Action {
let menus: Result<[Menu]>
}
I know I need to change MenuAction to Something like this:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {}
init(_ standardAction: StandardAction) {}
func toStandardAction() -> StandardAction {
return StandardAction(type: SetMenusAction.type, payload: [:], isTypedAction: true)
}
}
but I got error on init functions
Return from initializer without initializing all stored properties
when I set a initializer code the error disappear but app does not restore saved data! How can I fix it?
You will want to add serialization/deserialization code. The menus property needs to be set. Also, you will want to serialize that property as payload:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {
self.menus = // however you initialize that
}
init(_ standardAction: StandardAction) {
let maybeMenus = standardAction.payload["menus"] as? [Menu]?
self.menus = // create Result from Optional<[Menu]>
}
func toStandardAction() -> StandardAction {
let maybeMenus = self.menus.asOptional // Cannot serialize Result itself
return StandardAction(type: SetMenusAction.type, payload: ["menus" : maybeMenus], isTypedAction: true)
}
}
So problems I see here: JSON serialization depends on Dictionary representation of your payload data, i.e. the properties of your object. Can Result be serialized directly? I guess not, so you need to convert it, probably easiest to nil.
All in all, the payload is the key you missed and now you have to figure out how to use it with the data you have at hand. Also, it makes me a bit suspicious that the Result type itself is part of the AppState. I expected it to be reduced away or handled before dispatching an action, like SettingMenusFailedAction instead of ChangeMenusAction(result:) or similar. Just as a sidenote: actions should be more than typed property setters.

Swift protocol extensions for Value(Structures) types

public struct KZErrorInfo: Unboxable {
var statusCode = -1
var status: String?
var errorMessage: String?
public init() {
}
public init(unboxer: Unboxer) {
self.statusCode = unboxer.unbox("StatusCode")
self.status = unboxer.unbox("Status")
self.errorMessage = unboxer.unbox("Message")
}
}
protocol KZClientResponse: ETClientResponse {
var errorInfo: KZErrorInfo? { get set }
}
var errorInfo: KZErrorInfo? {
get {
if let value = objc_getAssociatedObject(self, &xoAssociationKeyErrorInfo) as? KZErrorInfo {
return value
}
return nil
}
set(newValue) {
if let error = newValue {
objc_setAssociatedObject(self, &xoAssociationKeyErrorInfo, error, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
}
My objective is to have a default implantation for the protocol KZClientResponse and Xcode is giving me a compile error as below. In the case of value types, how to overcome this issue? Appreciate you suggestions.
As the error message is indicating, objc_getAssociatedObject(_:_:) and objc_setAssociatedObject(_:_:_:_:) require AnyClass as the first argument. You cannot use Swift structs as AnyClass.
Think another way to store errorInfo which works with structs.
Why don't you have it as the struct's property?
... giving me a compile error as below. In the case of value types, how to overcome this issue?
You can't overcome the compiler error. You're trying to mix apples with oranges. objc_getAssociatedObject is, by definition, Objective-C. But Objective-C knows nothing of Swift structs; it cannot possibly see them. The only thing it knows about are what it calls objects — that is, classes and their instances. To work with a Swift struct, you cannot use the Objective-C runtime at all: you must operate entirely within Swift itself.

Why is it not possible to define property observers for computed propertys?

I am currently playing around with swift and there is one thing i don't understand.
Lets take a look at the following non compiling code snippet
class A {
var test : String {
get {
return "foo"
}
set {
self.test = newValue
}
willSet {
}
didSet {
}
}
}
For some reason the Compiler is complaining. So i am not able to implement all of them: get and set, didSet and willSet. I thought that observing computed properties is maybe not possible.
So i played around a little bit more and then i discovered that subclasses can override the property observers of computed propertys.
What the hack? This doesnt makes sense to me
import UIKit
class A {
var test : String {
get {
return "name"
}
set {
self.test = newValue
}
}
}
class B : A {
override var test : String {
willSet {
}
didSet {
}
}
}
Why i am not able to add property observers in the first code snippet but i am able to overwrite these observers inside a subclass?
In the first code, you don't need observers, because you already are writing the code that sets the property (set). Thus, if you want to do something before/after the property gets set, you can just do it right there in the setter handler (set):
class A {
var test : String {
get {
return "foo"
}
set {
// will set
self.test = newValue
// did set
}
}
}
Thus, by a kind of Occam's Razor principle, it would be redundant and unnecessary to have separate setter observers: you are the setter, so there is no need to observe yourself.
In your subclass override, on the other hand, where you didn't supply a whole new computed property, the setting is going on behind your back, as it were, so as compensation you are allowed to inject set observation into the process.
“You don’t need to define property observers for non-overridden computed properties, because you can observe and respond to changes to their value in the computed property’s setter.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.1).” iBooks. https://itunes.apple.com/cn/book/swift-programming-language/id881256329?l=en&mt=11

Best way to serialize optional property which has a custom class

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)
}
}
}

How to call instance method inside class using Swift

I am having an issue with calling an instance method within the class itself. If someone can provide me some insight it would be greatly appreciated.
My current code looks like this:
class Rect
{
func printthis() -> String {
return "this is working or what"
}
var toPrint:String = self.printthis()
}
The error I am getting in Xcode is: Use of unresolved identifier 'self'.
What am I missing here?
You can't call an instance method without an instance. The class is merely the template for instances. So i don't what you are trying to do here...
But the answer is no, you cannot call an instance method form the class definition because there is no instance yet.
Perhaps you want to delcare a class method and use that to set an instance variable on creation? If so, you might do that like this:
class Rect {
class func printthis() -> String {
return "this is working or what"
}
var toPrint:String
init() {
toPrint = Rect.printthis()
}
}
var r = Rect()
println(r.toPrint) //-> this is working or what
An instance of a class is not initialized and able to be referenced (even as 'self') until all of its variables have been assigned values.
An option that may work for you is to declare your variable as an implicitly-unwrapped optional, which is assigned nil by default. Then in the class's init method, since all of the variables have been assigned values, you are able to start calling methods on your instance.
class Rect {
var toPrint: String!
init() {
toPrint = printthis()
}
printthis() -> String {
return "this will work"
}
}
the problem is that swift is strict about initing all properties.
you may as a workaround
class Rect
{
func printthis() -> String {
return "this is working or what"
}
var toPrint:String = ""
init() {
toPrint = printthis()
}
}