Switch from NSDictionary to Dictionary - swift

I have some code to get EXIF data from file, but it uses NS-Types. I like to get Swift 3 conform and use standard swift types like Dictionary or String. When deleting "NS", I get the error that ".value()" does not exist. And no hint by the compiler what is the new function call:
let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil)
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil)! as NSDictionary
let exifDict = imageProperties.value(forKey: "{Exif}") as! NSDictionary
let dateTimeOriginal = exifDict.value(forKey: "DateTimeOriginal") as! NSString
print ("DateTimeOriginal: \(dateTimeOriginal)")
let PixelXDimension = exifDict.value(forKey: "PixelXDimension") as! Double
print ("PixelXDimension: \(PixelXDimension)")
let exifDictTIFF = imageProperties.value(forKey: "{TIFF}") as! NSDictionary
// optional
if let Software = exifDictTIFF.value(forKey: "Software") as? NSString {
print ("Software: \(Software)")
}
Any hint how to change it?
Additionally:
Using this
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil)! as Dictionary
let exifDict = imageProperties["{Exif}"] as! Dictionary
will deliver an error "Ambiguous reference to member 'subScript'" for the second row!

All of the NSDictionary needs to be something like [String:Any]. And all of the value calls should use normal key access.
let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil)
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil) as! [String:Any]
let exifDict = imageProperties["{Exif}"] as! [String:Any]
let dateTimeOriginal = exifDict["DateTimeOriginal"] as! String
print ("DateTimeOriginal: \(dateTimeOriginal)")
let PixelXDimension = exifDict["PixelXDimension"] as! Double
print ("PixelXDimension: \(PixelXDimension)")
let exifDictTIFF = imageProperties["{TIFF}"] as! [String:Any]
// optional
if let Software = exifDictTIFF["Software"] as? String {
print ("Software: \(Software)")
}
This code is terrible. All of those uses of ! are a bad idea. Proper, safe unwrapping and casting should be used throughout this code.

Related

Can't cast to NSMutableString in Swift

I have Swift struct like this:
if(isSearching == true){
let contactDict :NSDictionary = self.filteredArray?.object(at: indexPath.row) as! NSDictionary;
let strArray :NSArray = contactDict.object(forKey: kName) as! NSArray
nameString = strArray.componentsJoined(by: "") as! NSMutableString
//nameString = (contactDict.object(forKey: kName) as? String as! NSMutableString)
companyNameString = (contactDict.object(forKey: kCompanyName) as AnyObject).object(at: 0) as? NSString;
designationString = (contactDict.object(forKey: kDesignation) as AnyObject).object(at: 0) as? NSString;
profileImage = contactDict.object(forKey: kProfilePic) as? UIImage;
connectStatus = contactDict.value(forKey: kLinkStatus) as? NSString;
if(profileImage?.accessibilityIdentifier == "Img_placeholder"){
profileImage = nil;
}
The error showing like this :
Could not cast value of type 'NSTaggedPointerString' (0x1b5b89900) to 'NSMutableString' (0x1b5b959c0)
How can i solve this issue?
If you need nameString to be a NSMutableString, then try this:
nameString: NSMutableString = strArray.componentsJoined(by:"").mutableCopy()
The error comes from
nameString = strArray.componentsJoined(by: "") as! NSMutableString
because you can't downcast here. NSTaggedPointerString is a (private) subclass of NSString but not of NSMutableString. You should create a new mutable string instead:
nameString = NSMutableString(string: strArray.componentsJoined(by: ""))
But as #Sweeper said in the comments it should be better to use Swift strings.

Swift Cannot assign through subscript: subscript is get only

I am fairly new to the Swift syntax and am receiving this error with my code "Cannot assign through subscript: subscript is get only"
This is from the line: friendDictionary[(friendUID as? String)!] = ["name": friendsData!["name"]]
Any advice on the correct way of doing it would be very helpful.
func getFriendsUIDs() {
if FBSDKAccessToken.currentAccessToken() == nil {
print("failed to start graph request")
return
}else{
}
if FBSDKAccessToken.currentAccessToken() != nil {
}
let parameters = ["fields": "name, id, picture"]
FBSDKGraphRequest(graphPath: "/me/friends", parameters: parameters).startWithCompletionHandler {
(NSURLConnection, result, requestError) in
let friendIds = result["id"] as? NSDictionary
let friendsData = friendIds!["data"] as? [NSDictionary]
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("friendUIDs").observeEventType(.Value, withBlock: { (snapshot) in
self.FriendUIDs = NSArray()
self.FriendUIDs = (snapshot.value as? NSArray)!
print(self.FriendUIDs)
var friendDictionary = NSDictionary()
for friendUID in self.FriendUIDs {
friendDictionary[(friendUID as? String)!] = ["name": friendsData!["name"]]
}
self.fetchFriendFeed(friendDictionary)
}) { (error) in
print(error.localizedDescription)
}
}
}
func fetchFriendFeed(friendDictionary: NSDictionary) {
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
for friendUID in FriendUIDs {
ref.child("users").child(friendUID as! String).child("Agenda").observeEventType(.ChildAdded, withBlock: { (snapshot) in
print(snapshot)
if let dictionary = snapshot.value as? [String: AnyObject] {
let friendPost = FriendPost()
friendPost.picture = friendDictionary[friendUID as! String]? ["picture"] as? String
friendPost.activity = dictionary["activity"] as? String
friendPost.date = dictionary["date"] as? String
friendPost.time = dictionary["time"] as? String
friendPost.friendname = friendDictionary[friendUID as! String]? ["name"] as? String
self.friendPosts.append(friendPost)
dispatch_async(dispatch_get_main_queue(), {
self.collectionView?.reloadData()
Nothing to do with Swift. You've elected to use Objective-C, in effect, by making friendDictionary an NSDictionary. NSDictionary is immutable; you can't assign into it or alter it in any way. That is simply a fact about Objective-C. The Swift var declaration makes no difference to this fact.
A better choice, since you are writing in Swift, would be to use a Swift dictionary, which is [AnyHashable:Any]() (in Swift 3). This will interchange with NSDictionary when you are talking to Objective-C, but it will give you a mutable dictionary because you (rightly) declared it with var.
Have you tried using NSMutableDictionary? That solved the issue for me.
For those who get stuck here, another reason for this happens when you try to assign something that does not conform the actual dictionary, in my example i was doing something like this:
var dict = [Date : UUID]()
let randomUUID = UUID()
dict[randomUUID] = Date.now
whereas I meant to write UUID : Date but I was sleepy so i made a mistake, and Swift gave me a misleading error saying subscript is get-only. So this error also appears with type mismatch for Swift 5.7.

Firebase syntax in Swift 3

I've updated Xcode to version 8 and now there is some problem with Firebase. This is a piece of code:
let target = snapshot.value!["target"] as! NSDictionary
self.myZodiac.text = snapshot.value!["zodiac"] as! String
let nsHeight = snapshot.value!["height"] as! NSNumber
// Type 'Any' has no subscript members
In Swift 2.3 all of this works! how to fix it?
One more:
var messagesDictionary = [[String:Int]]()
userRef.observe(FIRDataEventType.value, with: { (snapshot) in
for item in snapshot.children.allObjects {
for itemDic in self.messagesDictionary {
for (key,value) in itemDic {
if (item as AnyObject).key == key {
var photo = UIImage()
let age = (item as AnyObject).value!["age"] as! NSNumber as Int //error Type 'Any' does not conform to protocol 'AnyObject'
let name = (item as AnyObject).value!["name"] as! String //error Type 'Any' does not conform to protocol 'AnyObject'
if (item as AnyObject).hasChild("avatar"){
let avatar = (item as AnyObject).value!["avatar"] as! String //error Type 'Any' does not conform to protocol 'AnyObject'
self.storageRef.child(key).child(avatar).data(withMaxSize: 5 * 1024 * 1024, completion: { (data, error) -> Void in
if (error != nil) {
} else {
photo = UIImage(data:data!)!
}
})
////
}else{
photo = UIImage(named: "no_avatar")!
}
}
}
}
}
})
first example i used:
let target = (snapshot.value as? NSDictionary)?["target"] as! NSDictionary
self.myZodiac.text = (snapshot.value as? NSDictionary)?["zodiac"] as! String
let nsHeight = (snapshot.value as? NSDictionary)?["height"] as! NSNumber
now what to do with item as AnyObject from second piece of code?
FIRDataSnapshot.value is of type Any, so you can't simply subscript it.
The solution is to first downcast the value to a dictionary:
ref!.observe(.value, with: { (snapshot) in
for child in snapshot.children {
let msg = child as! FIRDataSnapshot
print("\(msg.key): \(msg.value!)")
let val = msg.value! as! [String:Any]
print("\(val["name"]!): \(val["message"]!)")
}
})
From my answer here:
Ambiguous Use of Subscript (Swift 3)
Try this:-
if let snapDict = snapshot.value as? [String:AnyObject]{
let target = snapDict["target"] as! [String:AnyObject]
self.myZodiac.text = snapDict["zodiac"] as! String
let nsHeight = snapDict["height"] as! Float
}

EXC Bad Instruction

In was wondering why I keep getting this error message, EXC Bad Instruction could someone help me out and tell me why.
Here is the code.
func updateStocks() {
let stockManager:StockManagerSingleton = StockManagerSingleton.sharedInstance
stockManager.updateListOfSymbols(stocks)
//Repeat this method after 15 secs. (For simplicity of the tutorial we are not cancelling it never)
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(15 * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(),
{
self.updateStocks()
}
)
}
//4
func stocksUpdated(notification: NSNotification) {
let values = (notification.userInfo as! Dictionary<String,NSArray>)
let stocksReceived:NSArray = values[kNotificationStocksUpdated]!
stocks.removeAll(keepCapacity: false)
for quote in stocksReceived {
let quoteDict:NSDictionary = quote as! NSDictionary
let changeInPercentString = quoteDict["ChangeInPercent"] as! String
let changeInPercentStringClean: NSString = (changeInPercentString as NSString).substringToIndex((changeInPercentString as NSString).length-1)
stocks.append(quoteDict["symbol"] as! String,changeInPercentStringClean.doubleValue)
}
tableView.reloadData()
NSLog("Symbols Values updated :)")
}
}
The line with the error in it is,
let changeInPercentString = quoteDict["ChangeInPercent"] as! String
The error states that Swift attempted to unwrap a nil value, as you stated on this line
let changeInPercentString = quoteDict["ChangeInPercent"] as! String
Swift attempts to force setting the value of quoteDict["ChangeInPercent"] to a String, because you use as!, instead, you should use as?, which will set the value to nil if the value cannot be found
let changeInPercentString = quoteDict["ChangeInPercent"] as? String
You could set this to a default value by using the ?? operator. For example, if you wanted the default value to be 0.0%, you could use
let changeInPercentString = (quoteDict["ChangeInPercent"] as? String) ?? "0.0%"
The inherent problem is most likely either that quoteDict["ChangeInPercent"] does not exist, or quoteDict["ChangeInPercent"] is not a String - it may be an NSString or simply a Double value.
If you find out that it is supposed to be an NSString, for example, you will need to change how you cast the value
let changeInPercentString: NSString = (quoteDict["ChangeInPercent"] as? NSString) ?? "0.0%"

'AnyObject?' does not have a member named 'count' compiler error

First of all, I tried using JSON Serialization in the following code and I tried looping through each items available in the array. However, it must be type casting that I am missing something. Here is the snippet of what I am trying to do:
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as
NSDictionary
var items = [[String:String]()]
var item: AnyObject
var authorDictionary: AnyObject
for var i = 0; i < jsonResult["items"].count; i++ {
items.append([String:String]())
items[i]["content"] = item["content"] as? NSString
items[i]["title"] = item["title"] as? NSString
items[i]["publishedDate"] = item["published"] as? NSString
authorDictionary = item["author"] as NSDictionary
items[i]["author"] = item["displayName"] as? NSString
}
To resolve your error, change this:
jsonResult["items"]
to this:
(jsonResult["items"] as! NSArray)
Sorry, i am not with my xcode here, but looking the code i think in something about this.
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as
NSDictionary
var items = [[String:String]()]
var item: AnyObject
var authorDictionary: AnyObject
if let itemsFromJson:NSArray = jsonResult["items"] as? NSArray
{
for itemJson in itemsFromJson {
items.append([String:String]())
items[i]["content"] = itemJson["content"] as? NSString
items[i]["title"] = itemJson["title"] as? NSString
items[i]["publishedDate"] = itemJson["published"] as? NSString
authorDictionary = itemJson["author"] as NSDictionary
items[i]["author"] = itemJson["displayName"] as? NSString
}
}