Swift NSCoder Not Working For Double Variable - ios

I'm restoring the state of my app and am storing a few variables using the NSCoder in Xcode 10.1. The NSCoder is fine at storing strings and other variable types, but won't allow me to store Doubles. I've tried numerous scenarios but it just doesn't seem to like Doubles!
If I convert the double into a string it will work. I've tried 'as? Double' and this still returns nil. Any ideas why I just can't use a Double?
override func encodeRestorableState(with coder: NSCoder) {
...
// Both don't work:
coder.encode(myDoubleVariable, forKey: "Length")
coder.encode(1.2345252, forKey: "Length")
}
override func decodeRestorableState(with coder: NSCoder) {
...
// Both don't work:
myDoubleVariable = coder.decodeObject(forKey: "Length")
myDoubleVariable = coder.decodeDouble(forKey: "Length")
}

Related

On iOS, can you add multiple CIFilters to a SpriteKit node?

On iOS, can you add more than one CIFilter to a SKEffectsNode?
CIFilterGenerator seems like what I want but it isn't available on iOS.
I know you can use multiple filters on an image by passing the output of one as the input of the next, but that's not helpful if you want to affect non-image nodes.
Does this mean I have to create an artificial hierarchy of SKEffectNode and add a filter to each of them, with my actual content at the very bottom? Is there a better way?
Following the useful suggestion by dfd I ended up going with this simple subclass. I'm marking his answer correct because a) he suggested this approach and I want to give him credit, and b) it has more general use info about using CIFilterConstructor to register your filter.
Useful references:
- Apple Docs
- Related Question
- Free Core Image eBook
class MyChainFilter: CIFilter {
let chainedFilters: [CIFilter]
#objc dynamic var inputImage: CIImage?
init(filters: [CIFilter]) {
self.chainedFilters = filters
super.init()
}
// run filters in order on the specified source image
override var outputImage: CIImage? {
get {
let imageKey = "inputImage"
var workingImage = self.inputImage
for filter in chainedFilters {
assert(filter.inputKeys.contains(imageKey))
filter.setValue(workingImage, forKey: imageKey)
guard let result = filter.outputImage else {
assertionFailure("filter failed: \(filter.name)")
return nil
}
workingImage = result
}
return workingImage
}
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
Where it's difficult or impossible to "chain" together multiple CIFilter calls to achieve the desired effect - maybe due to a class that has a single property, one way to overcome this is to do the following:
Subclass CIFilter, overriding everything you need to. This may include attributes, setValue(forKey:), and most importantly, outputImage.
Subclass CIFilterConstructor, and create a registerFilter() method.
For example, let's say you wish to combine a gaussian blur and then add a monochrome red tone to an image. At it's most basic you can do this:
class BlurThenColor:CIFilter {
let blurFilter = CIFilter(name: "CIGaussianBlur")
override public var attributes: [String : Any] {
return [
kCIAttributeFilterDisplayName: "Blur then Color",
"inputImage": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Image",
kCIAttributeType: kCIAttributeTypeImage]
]
}
override init() {
super.init()
}
#available(*, unavailable) required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func setValue(_ value: Any?, forKey key: String) {
switch key {
case "inputImage":
blurFilter?.setValue(inputImage, forKey: "inputImage")
default:
break
}
}
override public var outputImage: CIImage {
return (blurFilter?.outputImage)! .applyingFilter("CIColorMonochrome", parameters: ["inputColor": CIColor(red: 1.0, green: 0.0, blue: 0.0)])
}
}
If you wish to expose more attributes, you can simply add them to the attributes and setValue(forKey:) overrides along wist adding variables and setDefaults. Here I'm simply using the defaults.
Now that you've chained your effect together into one custom filter, you can register it and use it:
let CustomFilterCategory = "CustomFilter"
public class CustomFilterConstructor: NSObject, CIFilterConstructor {
static public func registerFilter() {
CIFilter.registerName(
"BlurThenColor",
constructor: CustomFilterConstructor(),
classAttributes: [
kCIAttributeFilterCategories: [CustomFilterCategory]
])
}
public func filter(withName name: String) -> CIFilter? {
switch name {
case "BlurThenColor":
return BlurThenColor()
default:
return nil
}
}
}
To use this, just be sure to register the filter (I tend to put mine in AppDelegate if possible):
CustomFilterConstructor.registerFilter()
From there, you can use BlurThenColor just like any other CIFilter. Instantiate it, use setValue, and call outputImage.
Please note, this code will crash because of force-unwrapping of inputImage and/or typos. I'm sure you can make this more safe - but rest assured that I've tested this and it works. (I created this custom filter and replaced it in an app where the force-unwraps don't happen.)

ios swift method parameters

I'm reading the swift getting started apple's guide. And they give the following code :
required init?(coder aDecoder: NSCoder)
Why are they two parameters name ? coder and aDecoder.
I don't understand why we don't use this syntax that works to :
required init?(coder: NSCoder)
Thank you.
coder is external parameter name. It is used when calling method:
SomeClass(coder: someCoder)
aDecoderis local parameter name. It is used inside method:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
You may specify just local parameter name:
required init?(coder: NSCoder)
In this case external parameter name automatically takes the same value coder.
The main use is readability. Your example is not very obvious. Lets take a better one from the link posted by Eric D:
func sayHello(to person: String, and anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted"))
This is more readable (better explains what method is doing) when calling sayHello method than
func sayHello(person: String, anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(person: "Bill", anotherPerson: "Ted"))
Of course you can write
func sayHello(to: String, and: String) -> String {
return "Hello \(to) and \(and)!"
}
print(sayHello(to: "Bill", and: "Ted"))
But in this case local variables are using bad naming style

Does not conform to protocol 'NSCoding' - Swift 3

I have seen several questions similar to mine; however, those are pertaining to swift 2/1 and I am currently using swift 3. I believe Apple has changed it slightly.
class Person: NSObject, NSCoding {
var signature: UIImage
init(signature: UIImage) {
self.signature = signature
}
required convenience init(coder aDecoder: NSCoder) {
let signature = aDecoder.decodeObject(forKey: "signature") as! UIImage
self.init(signature: signature)
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encode(signature, forKey: "signature")
}
}
You will notice how Swift 3 now forces me to use required convenience init( instead of required init(. Perhaps that has something to do with it.
How can I resolve this issue? Thanks!
The encode method in Swift 3 has been renamed to
func encode(with aCoder: NSCoder)
When you get the do not conform error you can easily find out which required methods are missing
Press ⌘B to build the code.
Press ⌘4 to show the issue navigator.
Click on the disclosure triangle in front of the issue line.

'self' used before super.init call and property not initialized at super.init call

I'm trying to init an array of Int in my custom UIView:
var graphPoints:[Int]
required init?(coder aDecoder: NSCoder) {
RestApiManager.sharedInstance.requestData() { (json: JSON) in
for item in json[].array! {
let n=item["n"].intValue
print(n)
if(n>=0) {
self.graphPoints.append(n)
}
}
}
super.init(coder: aDecoder)
}
But int this row RestApiManager.sharedInstance.requestData() { i reiceved this error: 'self' used before super.init call
And at this row super.init(coder: aDecoder) the error: property self.graphPoint not initialized at super.init call
There are a few seperate issues here
The first related two you question being that you must call the super.init method before you reference self. Simply move this line to the beginning on the init method.
Unfortunately because you are preforming an asynchronous request this will still cause you problems as mentioned in the comments.
Also, note init?(coder aDecoder: NSCoder) is not intended for this use. Another issue is if you are requesting data from your API you should create a model object. As opposed to directly creating a UIView and then when you wish to display it to a user create a UIView from that model.
class GraphPointsModel {
var graphPoints:[Int]
init(graphPoints: [Int]) {
self.graphPoints = graphPoints
}
class func retreiveGraphPoints(handler: (GraphPointsModel -> ())) {
RestApiManager.sharedInstance.requestData() { (json: JSON) in
//Error checking...
//Create instance of GraphPointsModel
let instance = GraphPointsModel(...)
//Call handler to do with as you wish with result
handler(instance)
}
}
}
Swift requires all parameters to be initialized before calling super.init(). There are several ways to make this happen.
Declare graphPoints with an empty initializer like this:
var graphPoints:[Int] = [] or var graphPoints = [Int]()
You can also change the graphPoints to an Optional, like this:
var graphPoints:[Int]?
You can also leave the declaration alone, and just initialize it to an empty array before calling super.init()
You will also need to move your RestAPI call below your super.init call.
Hope this helps.
Maybe you must to do this:
super.init(coder: aDecoder)
put that before your requestData call.

NSCoding required initializer in inherited classes in Swift

I have class Foo which conforms to NSObject and NSCoding which I want to be able to persist with NSKeyedArchiver I want to create class Bar, a subclass of Foo that will also conform to NSObject and NSCoding. I am having a problem understanding how to create the required convenience init?(coder aDecoder: NSCoder) in the subclass.
so class Foo...
class Foo: NSObject, NSCoding {
let identifier:String
init(identifier:String) {
self.identifier = identifier
}
override var description:String {
return "Foo: \(identifier)"
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(identifier, forKey: "identifier")
}
required convenience init?(coder aDecoder: NSCoder) {
guard let identifier = aDecoder.decodeObjectForKey("identifier") as? String
else {
return nil
}
self.init(identifier:identifier)
}
}
Then class Bar ...
class Bar:Foo {
let tag:String
init(identifier:String, tag:String) {
self.tag = tag
super.init(identifier: identifier)
}
override var description:String {
return "Bar: \(identifier) is \(tag)"
}
}
I can get this to compile by adding the following methods on to make this NSCoding compliant
override func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(tag, forKey: "tag")
super.encodeWithCoder(aCoder)
}
this makes sense because I call super.encodeWithCoder(...) reusing the super makes this DRY. The problem I am having is creating the required convenience init?(...) the only way I can seem to get it to compile is by doing this...
required convenience init?(coder aDecoder:NSCoder) {
guard let identifier = aDecoder.decodeObjectForKey("identifier") as? String,
let tag = aDecoder.decodeObjectForKey("tag") as? String
else {
return nil
}
self.init(identifier:identifier, tag:tag)
}
I basically have copied the superclass required initializer and then added the additional decode method for the subclass property. This approach does not seem correct...
Is there a better way to implement this??
Right after you decode and assign all the subclass properties in the required init method, call:
super.init(coder: aDecoder)
Have thought about this for a while and believe that this is the correct way to implement this.
The reason is the way Swift enforces object initialization. Convenience initializers can only call the required initializers on self. Only the required initializer can call the init on super.
Therefore the only way to initialize the subclass object is to decode all of the required initialization parameters before you call the subclass required initializer...which then calls the super class initializer
Here is code you can copy to a playground https://gist.github.com/vorlando/dc29ba98c93eaadbc7b1
I have try your code in playground, it just auto to add the code when I tick the red circle of the Error.
The coding is like your function required convenience init.
sample code:
required convenience init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}

Resources