How to save generic object to an array in swift - arrays

I have on class
class SomeClass<T>: AsyncOperation, NetworkOperationProtocol {
/// The resource model object that conforms to Parsable Response
public typealias Resource = T
}
I want to save instance of this class to an array, and want retrieve it later.
How can I achieve this?

If your class implements protocol with associatedtype, it's impossible to put it into array because such protocols have Self-requirement - they need to know concrete type for associatedtype.
However, you can use technique called type-erasure to store type without associated type information. So, for example, you can create a protocol without associatedtype, like so
protocol Operation {
func perform()
}
class SomeClass<T> : AsyncOperation, NetworkOperationProtocol, Operation
And then define an array of such operations:
let operations : [Operation] = []
operations.append(SomeClass.init())

If your generic type would be:
struct GenericType {}
You would specify the generic class using:
let array = [SomeClass<GenericType>]()
Or you can let it infer the type on its own with something like:
class SomeClass<T>: AsyncOperation, NetworkOperationProtocol {
/// The resource model object that conforms to Parsable Response
public typealias Resource = T
let resource: Resource
init(with resource: Resource) {
self.resource = resource
}
}
let item = SomeClass(with: GenericType())
let array = [item]

It should be simple
You can declare an array like this
var anArray = [SomeClass<Parsable>]()
Please note that while declaring the array I have defined the Parsable instead of T.
If you don't know the type of T while creating array. You can go following way
var anArray = [AnyObject]()
let anObject = SomeClass<Parsable>()
anArray.append(anObject)
if anArray[0] is SomeClass<Parsable> {
print(true)
}

Related

In Kotlin, how to define a property of a generic type with a upper bound limit?

What is the Kotlin equivalent of the following Java code?
class Test {
Class<? extends Number> types = new Class[] {
Integer.class, Long.class, Short.class, Byte.class, Float.class, Double.class
};
}
You can use out keyword:
var types: Array<Class<out Number>> = arrayOf(
Integer::class.java,
Long::class.java,
Short::class.java,
Byte::class.java,
Float::class.java,
Double::class.java
)
out modifier is used to indicate covariance (similar to ? extends T in Java). Covariance - is the ability to change the generic type argument from a class to one of its parents, i.e. assign List<String> to List<Any>.
What you need here is a covariant array, or out variance on your generic parameter:
var types: Array<Class<out Number>> = arrayOf(
Int::class.java,
Long::class.java,
Short::class.java,
Byte::class.java,
Float::class.java,
Double::class.java
)
This sort of thing is covered in the documentation under variance (although it might take a while to untangle it all, if you're new to it).
In addition to other answers, you could produce the same code in Kotlin using Kotlin data types as;
var types: Array<KClass<out Number>> = arrayOf(
Int::class,
Long::class,
Short::class,
Byte::class,
Float::class,
Double::class
)
Here we use KClass which is the Kotlin class reference type and out is covariance type annotation (producer).

Swift 4 Array get reference

I ran into an issue with arrays in Swift. The problem is that it's a value type in Swift. I'm trying to find a workaround.
Here is the code that I have:
class Object: Codable{
var name : String?
}
var objects: Array<Object>?
objects = Array<Object>()
if var obj = objects { // <----- Creates a copy of array here
let o = Object()
o.name = "1"
objects?.append(o)
print(obj) //<----- this one is missing "o" object
print(objects)
}
I cannot use NSMutableArray because I have an array inside another codable class.
What's everybody's experience on this one? If somebody can share a solutions for that.
Getting used to arrays as value types isn't too tough really. If i were you my version of the code would just look like this
var objects: Array<Object>?
objects = Array<Object>()
if var unwrappedObjs = objects {
let o = Object()
o.name = "1"
unwrappedObjs.append(o)
objects = unwrappedObjs
}
or alternatively maybe this:
var objects: Array<Object>?
objects = Array<Object>()
if objects != nil {
let o = Object()
o.name = "1"
objects?.append(o)
}
Lastly you could always try making your own "ReferenceArray" class that wraps the array APIs and gives you reference semantics but that seems like overkill. Sooner rather than later, arrays as value types will seem natural to reason about.
bitwit already mentioned this to a point, but I think that your biggest mistake is simply not accepting the new object as the source. Unless it's important to retain the Array<Object>? you should replace it with the Array<Object> one.
var objects: Array<Object>?
objects = Array<Object>()
if var objects = objects { // <----- Creates a copy of array here
let o = Object()
o.name = "1"
objects.append(o) // objects is now the non-optional one
print(objects)
}
If it needs to be in the same scope, use guard:
var objects: Array<Object>?
objects = Array<Object>()
guard var objects = objects else { // <----- Creates a copy of array here
fatalError()
}
let o = Object()
o.name = "1"
objects.append(o) // objects is now the non-optional one
print(objects)
If you absolutely need an array to be referenced, you can make a container class:
public class ReferenceContainer<Element> {
public var element: Element
init(_ element: Element) {
self.element = element
}
}

JSON parse and encoding with generic types

I have an generic array of objects:
static Array<Level> allLevels = new Array<Level>();
Now I want to encode it to json like this:
Json json = new Json();
String levelsJsonString = json.prettyPrint(allLevels);
The problem occurs when I'm trying to reverse this process:
allLevels = json.fromJson(Level.class, levelsJsonString);
It is raising that Array and Level are incompatible types. How to do this?
As far as I know there's always problem with passing generic type class to a json fromJson() method.
The simpliest workarround is just to wrap Array type like:
public class Levels
{
public static Array<Level> allLevels = new Array<Level>();
}
Then you can use it just like
Levels.allLevels.add( new Level() ); //etc...
and retrieve it from JSON like
... = json.fromJson(Levels.class, jsonLevels);
passing the wrapping class class object to fromJson method. The same you are doing hen converting to JSON - pass wrapping class not just Array object
String levelsJsonString = json.prettyPrint(Levels.allLevels);

Swift empty array does not have a member named .insert

I am new to Swift.
I am trying to get some data from a webservice and to loop the JSON data to make a simple array.
DataManager.getDataFromEndpoint{ (endpointData) -> Void in
let json = JSON(data: endpointData)
if let programsOnAir = json["data"]["data"]["on_air"].array{
var onAirArray = []
for onAir in programsOnAir {
var eventName = onAir["event_name"].string
var eventCover = onAir["event_cover"].string
var tuple = (name: eventName!, cover: eventCover!)
onAirArray.insert(tuple, atIndex: 1)
}
println(onAirArray)
}
}
I get an error where the member .insert does not exist
BUt if I init the array like this var onAirArray = [name: "something, cover: "somethingelse"] then it works.
I need to work with empty arrays and I need to be them mutable, because I have no idea what I may get from the JSON given by the API endpoint.
What am I doing wrong?
The problem is with this line:
var onAirArray = []
Since you haven't given the array an explicit type, this is creating a new instance of NSArray, which doesn't have a method called insert. Which is why this is probably the exact error message you're receiving.
'NSArray' does not have a member named 'insert'
To fix this, explicitly state the type of your array.
var onAirArray: [(String, String)] = []

Converting Swift Array to NSData for NSUserDefaults.StandardUserDefaults persistent storage

I'm trying to get my head around Swift (after being relatively competent with Obj-C) by making a small app. I would like to use NSUserDefaults to persistently save a small amount of data but I am having problems.
I initialise an empty array of tuples like this:
var costCategoryArray: [(name:String, defaultValue:Int, thisMonthsEstimate:Int, sumOfThisMonthsActuals:Int, riskFactor:Float, monthlyAverage:Float)]=[]
When the array has an entry, I want to save the array to NSUserDefaults with standard Swift code such as this:
NSUserDefaults.standardUserDefaults().setObject(costCategoryArray, forKey: "financialData")
NSUserDefaults.standardUserDefaults().synchronize()
I get an error saying that the tuple array doesn't conform to the AnyObject class. So I tried to turn it into NSData:
var myNSData: NSData = NSKeyedArchiver.archivedDataWithRootObject(costCategoryArray)
var myUnarchivedData: Array = NSKeyedUnarchiver.unarchiveObjectWithData(myNSData)
...but I get the same error during the conversion to NSData. The object being held by my array doesn't conform to AnyObject. I've also tried at each stage to make it immutable by using:
let immutableArray = costCategoryArray
Ive also tried creating a class instead of using tuples which I understood would make it comply with AnyObject:
class costCategory : NSObject {
var name : String
var defaultValue : Int
var thisMonthsEstimate : Int
var sumOfThisMonthsActuals : Int
var riskFactor : Float
var monthlyAverage : Float
init (name:String, defaultValue:Int, thisMonthsEstimate:Int, sumOfThisMonthsActuals:Int, riskFactor:Float, monthlyAverage:Float) {
self.name = name
self.defaultValue = defaultValue
self.thisMonthsEstimate = thisMonthsEstimate
self.sumOfThisMonthsActuals = sumOfThisMonthsActuals
self.riskFactor = riskFactor
self.monthlyAverage = monthlyAverage
}
}
But the new error is:
"Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType')"
What is the problem with an array of tuples? Why can't I store an array of class objects? I feel like I need some expert advice as so far everything I try to do with Swift is pretty much incompatible...
Thanks!
Anything you are archiving to NSData and back needs to implement the NSCoding protocol. I found that in addition, my Swift class had to extend NSObject. Here is a quick example of a Swift class that encodes and decodes:
class B : NSObject, NSCoding {
var str : String = "test"
required init(coder aDecoder: NSCoder) {
str = aDecoder.decodeObjectForKey("str") as String
}
override init() {
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(str, forKey: "str")
}
}
// create an Object of Class B
var b : B = B()
// Archive it to NSData
var data : NSData = NSKeyedArchiver.archivedDataWithRootObject(b)
// Create a new object of Class B from the data
var b2 : B = NSKeyedUnarchiver.unarchiveObjectWithData(data) as B
value of "financialData" should be in quotes:
NSUserDefaults.standardUserDefaults().setObject("costCategoryArray", forKey: "financialData")
NSUserDefaults.standardUserDefaults().synchronize()

Resources