componentsSeparatedByString method in Swift - ios

I had some troubles last night accessing the elements of an array created via the componentsSeparatedByStringMethod. My goal is to extract some information from an url content variable.
var arr = urlContent.componentsSeparatedByString("<span class=\"Some HTML class\">")
I printed arr to the log, it works perfectly
I'd like to access the second element of the array arr, in order to get the info I need (the information right after the span tag). I thought:
var info = arr[1]
would work, but it doesn't. I get an error message saying that the subscript method doesn't work for an object of type "AnyObject" or something similar. So the problem is arr is not an Array Object type but an AnyObject type. I tried to convert it to an array but it didn't work either.
Could anybody help through this little trouble? :)

In xcode 6.1.1 componentsSeperatedByString method returns [AnyObject]. You have to cast it to [String] like so:
if let arr = urlContent.componentsSeparatedByString("<span class=\"Some HTML class\">") as? [String] {
// arr is now [Sstring]
}
In xcode 6.3 beta this method returns [String] so cast is not needed.

Related

Using firstIndex(of: Element) in Swift

I have the following code in Swift 4, on Xcode 10
var newCard = deck.randomElement()!
deck.remove(at: deck.firstIndex(of: newCard)!)
dealtCards.append(newCard)
I'm trying to take a random element from deck: [SetCard] and place it into dealtCards: [SetCard]
However, Xcode gives me the following error:
I've been looking through the documentation, but as far as I can tell, func firstIndex(of element: Element) -> Int? is a method that exists in Array, so I don't understand why Xcode wants me to change 'of' to 'where', when the function has the exact behaviour I want.
What am I missing here?
The problem has been already explained in the other answers. Just for the sake of completeness: If you choose a random index instead of a random element in the array then the problem is avoided and the code simplifies to
if let randomIndex = deck.indices.randomElement() {
let newCard = deck.remove(at: randomIndex)
dealtCards.append(newCard)
} else {
// Deck is empty.
}
Your deck array elements should conform to Equatable protocol,
For example as String conforms to Equatable protocol, the below code snippet works,
var arr = ["1", "2", "3"]
let newCard = arr.randomElement()!
arr.remove(at: arr.firstIndex(of: newCard)!)
The problem is that your SetCard type has not been declared Equatable. Thus there is no way to know if a card is present or what its index is, because the notion of a card that “matches” your newCard is undefined.
(The compiler error message is not as helpful on this point as it could be. That is a known issue. Bug reports have been filed.)

Ambiguous use of 'mutableCopy()' Swift3

I tried to update Swift 3 and I got the following error :
Ambiguous use of 'mutableCopy()'
Before update to swift 3. It runs well.
Swift 2.3
NSUserDefaults.standardUserDefaults().objectForKey("listsavednews")?.mutableCopy() as! NSMutableArray
Swift 3.0
(UserDefaults.standard.object(forKey: "listsavednews")? as AnyObject).mutableCopy() as! NSMutableArray
I found that mutableCopy in Swift3 return Any that doesnt have method mutableCopy() so that it needs to cast to AnyObject.
Any helps thanks.
I dont know why I can't comment.
Thanks all, I'll be using :
UserDefaults.standard.mutableArrayValue(forKey: "listsavednews")
mutableCopy is an Objective-C method from NSObject. There's little reason to use it in Swift 3.
Since you are dealing with UserDefaults and mutableCopy, you must be dealing with either an array or dictionary. Or it could be a string.
The proper way to do this in Swift 3 is to use the proper UserDefaults method to get an array or dictionary. And assign the result to a var. That combination will give you a mutable array or mutable dictionary.
var someArray = UserDefaults.standard.array(forKey: "somekey")
or:
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey")
In the two above cases, you end up with an optional since there might not be any data for the given key. And you also get a non-specific array or dictionary which isn't ideal. It would be better to cast the result to the appropriate type.
Let's say you have an array of strings and you want an empty array if there is nothing currently in user defaults. You can then do:
var someArray = UserDefaults.standard.array(forKey: "somekey" as? [String]) ?? []
Adjust as necessary if the array contains something other than String.
If you actually have a dictionary, the code would be similar.
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey") as? [String:String] ?? [:]
If your original object is just a string, then you could do:
var someString = UserDefaults.standard.string(forKey: "somekey") ?? ""

Intricate access to dictionary key

From a server I receive a JSON string, then I try to convert it to an NSDictionary this way:
let JSON = try NSJSONSerialization.JSONObjectWithData(rToData!, options:[])
guard let JSONDictionary:NSDictionary = (JSON as! NSDictionary) else {
print("My grandma is way more NSDictionary than this")
return
}
Once converted, I try to get some data contained in the dictionary: in particular I need an array I can access this way:
let myArray = JSONDictionary["data1"][0]["data2"];
XCode really doesn't like this idea, it puts an arrow under the first bracket and says Value of optional type "AnyObject?" not unwrapped, did you mean to use "!" or "?" ?. I follow its suggestion and I insert a "!", converting my preceding code to this:
let myArray = JSONDictionary["data1"]![0]["data2"];
At this point, the following line (where I count the number of elements in data2) shows an error, stating AnyObject has no member count.
The only thing that seems to work fine is this solution but, apart from being ugly and unreadable, I really don't understand it:
let myArray = (JSONDictionary["data1"]?[0]["data2"])!;
Can you help me understand why this basic access to a key in a dictionary must be so intricate?
I must say I like Swift but I spend a lot of time dealing with optionals and bizarre XCode alerts.
There is no guarantee that your JSON dictionary will contain a value for the key data1 (OK, you know it will, but Swift doesn't) so JSONDictionary["data1"] returns an optional. You need to unwrap the optional with ? or !
Also, since you have an NSDictionary, not a Swift dictionary, Swift doesn't know the type of the values, so they are AnyObject. Now again, you know it is an array, but Swift doesn't so you get an error stating that AnyObject doesn't have a count method.
While it is more verbose, it is cleaer for both the compiler and anyone else looking at your code if you split the line into multiple lines. It also lets you downcast the various objects so that Swift knows what is going on and handle any malformed JSON;
if let array1 = JSONDictionary["data1"] as? NSArray {
if let dictionary1 = array1[0] as? NSDictionary {
if let data2Array = dictionary1["data2"] as? NSArray {
let count=data2Array.count
}
}
}
You could implement appropriate else statements to handle errors
Optionals are one of Swift's most powerful features. They help avoid a whole family of bugs associated with uninitialised variables and special sentinnel values for boundary conditions. It is important that you learn how they can help you and not just throw ? or ! at your code until it compiles.

Cast from 'MDLMaterialProperty?!'?

I'm working with the parse framework for an App that I'm converting to Xcode 7. One of the interesting errors which has occurred as part of migrating the project is the following:
Cast from 'MDLMaterialProperty?!' to unrelated type 'PFUser' always fails
The offending line seems to be the "if let" clause below. I've commented the old line which worked fine in the previous version of Swift for comparison.
With respect to what this is actually doing - I've passed an array of Parse objects into "likesForThankful" where a pointer "userID" refers to a related PFUser. As part of this method I'm writing individual PFUsers to an array.
Any help is appreciated - thanks in advance.
//Add PFUsers who Like Post to our FeedItem
private func callbackFromLikesProcessing(likesForThankful:[AnyObject], againstFeedItem:FeedItem){
//Instantiate our Objective C compatible array for processing later
againstFeedItem.parseUsersObjectsWhoLikePost = NSMutableArray()
//Loop through likes and add PFUsers to array of users who like post
for usersWhoLikePost in likesForThankful{
// if let parseUserWhoLikesPost = usersWhoLikePost["userID"] as PFUser{
if let parseUserWhoLikesPost = usersWhoLikePost["userID"] as? PFUser {
againstFeedItem.parseUsersObjectsWhoLikePost.addObject(parseUserWhoLikesPost)
}
}
Andrew
Figured this out if it can help anyone - it's basic Swift Syntax (though I'm not sure why the compiler let me get away with this in the first version of Swift!).
Because usersWhoLikePost is a PFObject which happens to contain a pointer to a PFUser object I needed to conditionally unwrap this first like so:
for usersWhoLikePost in likesForThankful{
if let parseLikeObject = usersWhoLikePost as? PFObject{
if let parseUserWhoLikesPost = parseLikeObject["userID"] as? PFUser {
againstFeedItem.parseUsersObjectsWhoLikePost.addObject(parseUserWhoLikesPost)
}
}
}
After this I could continue as I had previously done and access the "userID" property with a conditional unwrap of the PFUser object.

Creating and populating an empty Array

I'm actually learning swift in order to develop iOS apps. I'd like, as an exercise, to create and populate an empty array, that would be filled by using a textfield and a button on the storyboard.
var arr = []
// When button is pressed :
arr.append(textfield.text)
XCode tells me that the append method is not a method of NSArray. So I have used the addObject one, but it is still not correct as the arr variable contains nil.
So here are my three questions :
Is it possible to create an empty array, and if so, how to populate it ?
Sometimes, in my ViewController, when I create a non-empty array, the append method is apparently not valid, and I don't understand why..
Finally, why even though I use the syntax :
var arr = [1] // For example
The arr object is NSArray object and not a NSMutableArray object, making it impossible to add/remove any object that is contained in it?
I hope my questions are clear, if not I'll upload more code of what I'm trying to build,
thank you for your answers !
Try to define your array as a String array, like this:
var arr: [String] = []
And then append to your list, either by:
arr.append("the string")
or
arr += ["the string"]
Empty array can be created using the following syntax.
var emptyArray = [String]()
emptyArray.append("Hi")
see this
You can use following also to add elements to your array.
//append - to add only one element
emptyArray.append("Hi")
//To add multiple elements
emptyArray += ["Hello", "How r u"]
emptyArray.extend(["am fine", "How r u"])
//Insert at specific index
emptyArray.insert("Who r u", atIndex: 1)
//To insert another array objects
var array1 = ["who", "what", "why"]
emptyArray.splice(array1, atIndex: 1)

Resources