How to store non NSObject derived data - ios

Custom object (not derived from NSObject) to be successfully stored in NSUserDefaults.StandardUserDefaults dictionary
I wasn't even able to store a List of string items into the dictionary
NSArchiver also doesn't work on objects which are not derived from NSObject
Is it mandatory to derive objects from NSObject ...
Also if an object contains child objects ( which are non-standard ) are they mandated to be derived from NSOBJECT as well?

You tagged your question as monotouch, so I guess you expect a monotouch answer.
You can wrap your objects as NSObjects quite easily. strings can be wrapped in NSStrings. List can be converted to/from NSArrays.
var nsobject = NSObject.FromObject (myCustomObject);
var string = new NSString ("hello, world");
var array = NSArray.FromStrings ("hello", "world");
NSString and NSArray, are NSObjects

Related

Swift 4 What is the best way to store an array of custom types persistently?

I have an array which contains objects that each have custom and non-primitive type properties like URL and StorageReference (Firebase). What is the best way to store the contents of this array persistently? I thought of using Realm, but Realm only stores objects that have primitive type properties. The amount of objects retrieved from persistent storage will continue to increase as the app is used more because there will be more and more items to retrieve from Firebase.
Realm supports the following property types: Bool , Int , Int8 , Int16
, Int32 , Int64 , Double , Float , String , Date , and Data.
So, ideally you should be able to cover any custom types by converting them to a suitable primitive type.
For instance, you can save your URL as a String while saving to Realm and convert back when retrieved.
If you don't want to include third party frameworks you can use the Codable protocol and save things with NSKeyedArchiver. https://developer.apple.com/documentation/foundation/nskeyedarchiver
Simple example:
struct A: Codable {
var myString: String? // primitive types are already codable
}
struct B: Codable {
var myInt: Int?
var a: A? // custom types need to be Codable
}
// save
let myObject = B()
NSKeyedArchiver.archiveRootObject(myObject, toFile: "/path/to/archive")
// load
let myObject = NSKeyedUnarchiver.unarchiveObjectWithFile("/path/to/archive") as? B
If your custom type is more complex you might want to implement the custom encode/decode methods to have more control over how you data is saved and loaded. https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

Extracting NSManagedObject attribute values from NSOrderedSet

I have an initializer that takes an array of Strings as a parameter. Rather than re-write the class and risk breaking things, I'd prefer to feed the initializer what it wants.
I'm trying to extract NSStrings from NSManagedObjects stored in an NSOrderedSet.
Here's what I've tried:
let soundsToPlay = sequenceToPlay.sounds as NSOrderedSet
for element in soundsToPlay {
// this prints out the object in console
print("\(element)")
// this, doesn't give me accessors for the sound object
// print("\(element.fileName)")
}
I'm missing something basic, but I'm not sure what. How would I enumerate the objects in an NSOrderedSet and extract the value of the attributes for the entities contained within the set?
I would suggest reading the documentation on KVC (Key Value Coding) as you can write this as one line of code:
let filenameArray = soundsToPlay.valueForKey("fileName").array()
The call to valueForKey will return an NSOrderedSet of the string and then you can convert that to an array with a call to array()
I figured it out. I was missing a step:
let soundsToPlay = sequenceToPlay.sounds as NSOrderedSet
for element in soundsToPlay {
// I have to tell the compiler what type of thing my thing is
let whatIWantToAccess = element as! MyObjectINeedAccessorsFor
print("\(whatIWantToAccess.fileName)")
}
Thats probably because compiler thinks that "element" is instance of NSManagedObject and it does not have fileName , try explicit type-casting , something like
for element: YourClass in soundsToPlay

Mutable Swift array becomes NSArray when I initialize with Obj-C

I'm creating my base models in Swift(2.0) and then controlling the views in Objective-C. I'm still new to Swift, so hopefully I'm just overlooking something simple, but here is the problem:
I’m making a mutable array in Swift, but when I initialize the array in my Objective-c portion of the program, it becomes an NSArray, more specifically it becomes: Swift._SwiftDeferredNSArray
Why is it becoming immutable when I initialize? Here’s my Swift code:
import Foundation
#objc public class Model : NSObject {
var books:[Book]
override init(){
self.books = [Book]()
}
}
And here’s my Obj-c Code;
Model *bookCollection = [[Model alloc]init];
I’m unable to add objects to my bookCollection.books array (because it has become an NSArray) and when I set a breakpoint and po it, I can see that it is a Swift._SwiftDeferredNSArray. bookCollection.books is supposed to be an NSMutableArray.
Any thoughts?
In swift, the difference between mutable and immutable array is;
var books:[Book] // is a mutable array
let books:[Book] = [book1, book2]; // is immutable array due to let
but I don't think, same rule is followed when bridging to ObjC.
Just for a fix, you may have mutableArray specifically.
import Foundation
#objc public class Model : NSObject {
var books:NSMutableArray = NSMutableArray();
override init(){
super.init();
// other code
}
}
You will need to parse the values to Book Class when retrieving from the array.
bookCollection.books is supposed to be an NSMutableArray.
No, it is not. Var does not mean that the bridged Objective-C object is to be mutable: it means that the property can be assigned to.
The Swift array type is a structure, not a class. This has important bridging implications. The reference itself cannot be shared without passing it as an inout value, and even then the references cannot be stored. If it bridged as a NSMutableArray, it would be possible to have undetectable mutating references, and Swift does not allow that.
You should be able to assign a new NSArray to your property from Objective-C code, though. For instance, this should work:
bookCollection.books = [bookCollection.books arrayByAddingObject:myNewBook];
Your other option, obviously, is to declare books as a NSMutableArray from the Swift side.

When to cast in swift

So this is probably a super simple question, but I have looked and cannot find the answer.
Let's say I have made a class and I fill an NSMutableArray with instances of this class and that is it. I want to use a for in loop to loop through. What is the easiest way to cast so I don't have to everytime I want to call the current instance in the loop?
Here it is shown:
for m in objects {
m.randomVar = "hello"
}
where m is my custom object that has a randomVar as a String and objects is an array of custom objects.
Of course the code above will not execute because xcode assumes m is an [AnyObject?]. Where and how would be the best was to get m in its own class. Let's assume we are changing more than one variable so casting everytime would not be optimal.
Sorry if I am unclear. Thank you
Avoid the problem.
Don't use NSArray/NSMutableArray — just use let/var variables of pure Swift arrays, such as [MyClass].
Or, if your array is coming from Objective-C, in Xcode 7+ you can use the NSArray<MyClass *> * syntax to expose it to Swift as [MyClass] rather than [AnyObject].
Cast the array.
Use objects as! [MyClass] (unsafe) if you are absolutely sure it contains instances of MyClass.
Cast the variable.
for m in objects {
if let m = m as? MyClass { ...
// or
let myobj = m as! MyClass // unsafe
or in Swift 2 (Xcode 7)
for case let m as MyClass in objects {
// this is executed only for objects which are instances of MyClass,
// ignoring other objects in the array.
}

EXC_BAD_INSTRUCTION object array assign Swift

I have an array of Printable objects, but I need them Equatable and AnyObject compliant.
private(set) var items: [Printable] = []
class func withItems<T: AnyObject where T: Equatable, T: Printable>(items: [T], selectedItem: T? = nil) {
... instance init ...
instance.items = items
}
And it result on EXC_BAD_INSTRUCTION:
fatal error: array cannot be bridged from Objective-C
This is one try to this problems:
Generic function and attribute with Equatable and Printable as parameters in Swift
why?
A Swift Array must contain all one kind of object (e.g. all String or all Int). An Objective-C NSArray can contain many different kinds of objects (e.g. some NSStrings and some NSNumbers). Hence if you get that kind of array from Objective-C you can't magically assign it into a Swift array reference.
What I do in that situation is munge the array to make it acceptable to Swift. I don't know what the details are of what you're getting back from Objective-C; your actual strategy will depend on those details and what you want to do with the array. One approach is to assign / cast into a Swift array of AnyObject. Or you might decide to leave it as an NSArray and work with it entirely through NSArray methods.
Here's an example from my own code. arr is an NSArray that's a mixed bag of NSString and NSNull objects. I know none of the NSString objects are the empty string, so I substitute the empty string for all the NSNull objects, thus giving me an array of just strings, which Swift can deal with:
let arr2 = (arr as Array).map { $0 as? String ?? "" }
Now arr2 is a pure Swift [String] array.

Resources