What is difference between NSDictionary vs Dictionary in Swift? - arrays

I'm learning Swift, and I can see Dictionary in it.
But there are lots of examples that are using NSDictionary with Swift.
What's the difference between these two?
I want to use an array with index in Swift like an array in PHP.
Which one is better to use?

Dictionary is a native Swift struct. NSDictionary is a Cocoa class. They are bridged to one another (within the usual limits), as the docs explain very clearly and fully.
It's exactly parallel to Array and NSArray.

In practice and with regard to Swift's strong type concept the significant difference is
NSDictionary is generally type unspecified
Dictionary is supposed to have a specific type.
Hence native Dictionary is preferable.

Here's the deal,
For all intents and purposes, they are the same thing. So long as you import Foundation, the compiler will know them as the same thing.
As for specific differences, thus Apple doc helps.
Overview
NSObject is the root class of most Objective-C class hierarchies. Through NSObject, objects inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.
What that means, in a nutshell, is that NSObjects are ancient relics from olden (objective c) times.
As for what is better, that is up to you. I find swift objects better than the ns counterpart just for the purpose of keeping code modern, however you may have to use ns objects if you are using code like NSURLCONNECTION that requires ns objects.
Hope this helps.

Consider this example
let dataArray = NSMutableArray()
let d0 = ["code":"AA","name":"American Airlines"]
let d1 = ["code":"BA","name":"British Airlines"]
let d2 = ["code":"DA","name":"Delta Airlines"]
dataArray.addObject(d0)
dataArray.addObject(d1)
dataArray.addObject(d2)
later...
let d0 = dataArray.objectAtIndex(0) as! [String:String]
let lbl = UILabel()
lbl.text = d0["code"] // no xcode warnings
Using NSDictionary in swift
let d0 = dataArray.objectAtIndex(0) as! NSDictionary
Now you need to coerce the dictionary value into shape.
lbl1.text = d0["name"] as! String
From experience - when dealing with json responses - I've used NSDictionary as it's loosely typed and I can't guaranteed to know exactly what it contains - just that is valid json. There's a growing library of native swift tools to handle this use case, I suggest looking here
https://github.com/search?q=json+NSDictionary+swift&type=Code&utf8=%E2%9C%93
In this case - you need to hack around data with the 'if let' dance.
if let results = json["result"] as? NSDictionary
lastly - NSMutableDictionary is more on par with Dictionary as you won't be able to mutate the key / value pairs on a NSDictionary.
eg.
results["code"] = 100 // <-- BOOM
the work around -
let mResults = NSMutableDictionary(dictionary:results)
mResults["code"] = NSNumber(100) // <-- OK
which begs the clarification - you can only stick NSObjects (NSNumbers / NSArrays / NSCount/NSSet) inside the NSMutableDictionaries. The Swift Dictionary gets around this.

Related

Error when trying to set array in userdefaults: Thread 1: "Attempt to insert non-property list object

I have solved the issue now, thanks for your help. I shouldn't have tried to save arrays with UITextViews, but I should have saved their text as strings instead. Here was the original question:
I have tried a lot, and googled a lot, but I can't solve this problem on my own. Whenever I try to save an array in userdefaults, it just is not working. I get the following error:
Thread 1: "Attempt to insert non-property list object (\n "<UITextView: 0x14001f800; frame = (0 0; 355 180); text = 'D'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600003f01d10>; layer = <CALayer: 0x6000031c83e0>; contentOffset: {0, 0}; contentSize: {355, 30}; adjustedContentInset: {0, 0, 0, 0}>"\n) for key content"
I don't know what a non-property list object is. And I do not know how to solve the problem. Below is the lines of code that do not work.
var contentList: [Any] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
contentList.append(label)
defaults.setValue(contentList, forKey: "content")
If I take out the last line of code by turning it into a comment everything runs just fine. How should I replace that line of code? I essentially want to save an array of UITextViews and make it larger every time I call a fucntion (this code is part of a larger function). The reason why I have created another two lists (cl and contentList) is that it helps me with a problem down the line. What I cannot understand however, is why the last line of code doesn't work. If anyone has any ideas, please help me, it would be much appreciated.
Use only String as stated in comments :
var contentList: [String] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
If lbText = label.text {
contentList.append(lbText)
defaults.setValue(contentList, forKey: "content")
}
You can only store a very limited list of data types into UserDefaults, commonly referred to as "property list objects" (Since property list (or plist) files will only store the same data types.
To quote the Xcode docs on UserDefaults, in the section titled "Storing Default Objects":
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary [or Data, String, NSNumber, Date, Array, or Dictionary types in Swift.] If you want to store any other type of object, you should typically archive it to create an instance of Data.
(I added the equivalent Swift types to the above quote in square brackets, since it looks like Apple hasn't updated it for Swift.)
That's worded a little awkwardly. The idea is that you can only store data of the types listed. Because the Array and Dictionary types are "container" types, you can store any combination of arrays and dictionaries that contain combinations of any of the above types. For example, you can store an array that contains a dictionary, 3 dates, 2 floats, a Double, some Data, and 2 arrays, and those dictionaries and arrays can contain other dictionaries and/or arrays.)
It is almost always wrong to archive UIView objects like UITextViews. You should save the text properties of your text views instead.
If you want to manage a vertical stack of UITextView objects, I suggest adding a vertical stack view to your user interface, and then writing code that adds or removes UITextView subviews to your stack view. You should be able to find plenty of examples of adding and removing objects from stack views online. (It's really easy.)
If you want to manage a scrolling list of feeds of arbitrary length, you might want to use a table view or collection view instead. Those require that you set up a data model and implement a "data source". That takes a little practice to get right, but is very powerful.

Swift: filter array of Any objects with a certain type

I have an array (Any type) with list of custom object (example object: Vehicle, Parking, Motorbike).
I need to filter this array with a specific type: example Vehicle.
Can you help me?
With compactMap and as?:
let values: [Any] = [1, "string", 0.0, ["array values"]]
let strings = values.compactMap { $0 as? String } // ["string"]
Make sure to do this as soon as possible (as soon as data comes into the system). You should not need [Any] as a class property, for example. If you feel you need that, you probably have mis-designed your data model and actually wanted an enum. Any is not a proper type in Swift; it exists to handle certain edge cases, mostly involving Objective-C, and very special cases like print. If you don't mean "absolutely any type at all is ok here," then you don't mean Any.

Store a NSMutableArray of NSManagedObject arrays in core data | swift 2

I'm somewhat new to swift, and I'm not a master when it comes to arrays
I'm trying to find a way where I can store an array of NSManagedObjects into core data.
Here's an example:
let array = [["a", "b", "c",],["1", "2", "3",],["apple", "orange", "pear"]]
This is the way I did it:
I have several arrays of NSManagedObjects that I added to a NSMutableArray
Then I stored it as transformable data. Like this.
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity_entry = NSEntityDescription.entityForName("EntityForArrays", inManagedObjectContext: managedContext)
let new_value = NSManagedObject(entity: entity_entry!, insertIntoManagedObjectContext: managedContext)
new_value.setValue(array, forKey: "attribute")
So am I going down the right path by using a NSMutableArray or should I try something new?
Also if can store this array, how can I receive it?
Whether it's the right path depends on how you need to use that array. It's convenient to put it all into one transformable attribute, but you can't use predicates to fetch objects based on their array contents.
Saving the array like that should work. To get it back, fetch objects using NSFetchRequest and then look up the array value using valueForKey().

Realm object in array?

Question
How can I create an array of objects containing Realm objects?
Code
let realm = try! Realm()
let data: [A] = realm.objects(A)
Error
Cannot invoke 'objects' with an argument list of type '(Object.type)'
How can I create an array of objects containing Realm objects?
From your code sample, I'll further assume that you want to make an array from a Realm Results, not just "standalone" Realm objects.
Since Results conforms to SequenceType, you can use SequenceType.map() to convert it into an array:
let arrayFromResults = results.map({ $0 })
Note, however, that this is almost always the wrong pattern to use.
From your tweet on the same topic, a preferable way to do this would be to encode what you want to display on screen as a Realm query:
self.results = realm.objects(A).filter("poppedOff == NO")
And "popping off" an object (whatever that means) would update the poppedOff property of that object.
Since Realm Results are auto-updating, this won't risk getting out of sync with the contents of the Realm, unlike the array conversion approach, which would have to be updated on every Realm change notification.

How would you create a multidimensional array with n dimensions in Swift?

For instance, asume
var hierarchicalFileSystem: [[String]] = []
This allows one to support one layer of folders, but there appears to be no way to create an array in Swift like the one above but with an undefined number of nested String arrays.
Am I missing something here?
An array of arrays (of arrays of arrays...) of strings doesn't really make much sense to represent a file system.
What I'd instead recommend is making a class or struct to represent objects in the file system. Perhaps something like this:
struct FileSystemObject {
let name: String
let extension: String?
let isFolder: Bool
let contents: [FileSystemObject]?
}
Something like this let's us represent a file system quite nicely.
let fileSystem = [FileSystemObject]()
So, your fileSystem variable here is an array of FileSystemObjects and it represents the root. Each object within the root has its own set of details (its name, its file extension if it has one, and whether or not its a folder), and if it's a folder it has a non-nil contents property, and if it's a non-empty folder, that contents array of FileSystemObjects contains more file system objects (some of which are folders of course, which contain contents themselves).
What you can perhaps do is create an array with AnyObject and add new depths as you need it
var fileSystem: [AnyObject] = []
This would be a very bad way of representing a file system however and you should really go with some kind of tree structure like
struct Node {
children: [Node]?
parent: Node?
name: String
}
Swift is type-safe language. You have to declare type of your variable, or set it to AnyObject, but please don't. So, answering your question: yes it's possible:
var array: [AnyObject] = [[[1,2,3], [1,2,3]], [[1,2,3],[1,2,3]]]
But this is awful. Try to figure out better representation for your problem. Maybe custom structures.
you can have as much dimensional array as you want. is it a good idea? i don't think ...
var threeDArray: Array<Array<Array<String>>> = []
let oneDArray = ["1","2","3"]
let twoDArray1: Array<Array<String>> = [oneDArray, oneDArray, oneDArray, oneDArray, oneDArray]
let twoDArray2 = twoDArray1 + [["4","5","6"],["7","8","9"]]
threeDArray.append(twoDArray1)
threeDArray.append(twoDArray2)
let arr = [threeDArray,threeDArray,threeDArray]
print(arr.dynamicType) // Array<Array<Array<Array<String>>>>

Resources