Hi i have a question about some code.
Okay, the problem is that i have some code that works in one function, but gives me an error in another function. The first code block is the function that it works in.
BTW, it is only one line:
#IBAction func searchPhotosByPhraseButtonTouchUp(sender: UIButton) {
if (!searchText.text.isEmpty) {
// 2: Replace spaces with +
var escapedSearchText:String = searchText.text.stringByReplacingOccurrencesOfString(" ", withString: "+")
// 3: API Method arguments
let methodArguments = [
"method": METHOD_NAME,
"api_key": API_KEY,
"text": escapedSearchText,
"format": FORMAT,
"nojsoncallback": NO_JSON_CALLBACK,
"extras": EXTRAS,
"safe_search": SAFE_SEARCH
]
// This line is the problem, if i make it in this function there is no problems
let urlString = BASE_URL + encodeParameters(params: methodArguments)
// 4: Call the Flickr API with these arguments
getImageFromFlickrBySearch(methodArguments)
}
else {
self.imageInfoLbl.text = "You did not write anything in the textfield"
}
}
So as you can see, in the code block above all is fine, but if i do it like this:
func getImageFromFlickrBySearch(methodArguments: [String: AnyObject]) {
// 5: Initialize session and url
...
// Here it gives me the error:
// Binary operator '+' cannot be applied to two String operands
let urlString = self.BASE_URL + encodeParameters(params: methodArguments)
...
}
I get a error.
I have removed the rest of the code from the second code block function for clarity.
I should probably say that BASE_URL is a constant.
The only difference of the functions, is that one is a #IBAction??
I'm not all too familiar with Swift but what could be causing your error is the methodArguments: [String: AnyObject]. In the function that works, Swift knows that all objects in the dictionary are String objects, but in the second function, their types are AnyObject.
If all of the key/value pairs are actually strings, change your parameter to methodArguments: [String: String] and see if that works.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am trying to generate QRCode and then read it in. My problem is that I do not know how to get values from the QRCode string.
String inside my QRCode is following: ["firstName": "John", "lastName": "Bush"]
I achieved it like this:
let dict = ["firstName":firstName, "lastName": lastName]
dict.description
Now I want to get the values out of it, how should I do this? I tried to convert the string back to dictionary but couldn't do it.
If you're really interested in parsing this result, there is of course ways to do it. It's never going to be consistently successful unless there is a consistent format coming from your source. In the event that it is consistent, you could of course do something like this:
//get the string representation of our dictionary
let code = "[\"firstName\": \"firstName\", \"lastName\": \"lastName\"]"
//remove the brackets
let bracketless = String(code.characters.dropFirst().dropLast())
//get rid of our quotes
let quoteless = bracketless.replacingOccurrences(of: "\"", with: "")
//split our dictionary by key/value pairs
let pairs = quoteless.components(separatedBy: ", ")
//now split our key/value pairs into each key and value component respectively
let values: [[String]] = pairs.map { $0.components(separatedBy: ": ") }
//create ourself a dictionary to populate with our parsed data
var dict: [String:Any] = [:]
values.forEach { dict[$0[0]] = $0[1] } //populate the dictionary
print(dict) // ["lastName": "lastName", "firstName": "firstName"]
It's always better to use a standardized format (e.g. JSON). Perhaps in your situation this just isn't an option. I would still wonder why then you're in said situation..
This is rather tricky, the result from description is not really meant to be used as an exchange format. The best thing to do would be to use a different format to generate your QR Codes. But let's assume that it's too late for this.
To get the data back you need to write a parser, since the system doesn't provide one for the description format. Sounds hard, but it really is not. You might consider using the String method components(separatedBy:), but this will turn out very inefficient. The Foundation Scanner class is a much better tool for this.
The simplest parser to write is a "recursive descent" parser. This means for each part you want to recognise you write a function that calls such functions for sub-parts. So lets see what we have here:
The outer layer is the dictionary. This starts with the string "[", then we have key/value pairs separated by "," and finally another "]". (There also might be the case of an empty dictionary which is just "[]"
Then we have a pair. This is a string inside quotes, followed by a ":" and another quoted string.
The last part is the quoted string. There we have the quote ", and any other characters up to the next quote.
So in pseudo-code this will look something like this:
func dictionary() {
expect("[")
if !read("]") {
repeat {
pair()
} while read(",")
}
expect("]")
}
func pair() {
quotedString()
expect(":")
quotedString()
}
func quotedString() {
expect("\"")
readUpTo("\"")
expect("\"")
}
Here expect, read and readUpTo are placeholders for the methods provided by the Scanner class. If we provide those this basically is the complete parser. But like this it is not very useful as it just ignores the data. So we need to extend our parser so it actually returns the found data.
The final result then could look something like this:
let scanner = Scanner(string: string)
func dictionary() -> [String: String]? {
guard scanner.scanString("[", into: nil) else { return nil }
var result: [String: String] = [:]
if !scanner.scanString("]", into: nil) {
repeat {
guard let (key, value) = pair() else { return nil }
result[key] = value
} while scanner.scanString(",", into: nil)
}
guard scanner.scanString("]", into: nil) else { return nil }
return result
}
func pair() -> (String, String)? {
guard let key = quotedString(),
scanner.scanString(":", into: nil),
let value = quotedString()
else {
return nil
}
return (key, value)
}
func quotedString() -> String? {
guard scanner.scanString("\"", into: nil) else { return nil }
var result: NSString? = nil
guard scanner.scanUpTo("\"", into: &result), let string = result as? String else { return nil }
guard scanner.scanString("\"", into: nil) else { return nil }
return string
}
A little bit more code that the hacky solution using string splitting, but not really complicated either and much more flexible. Here we won't have any problems if the strings themselves contain "," or ":". The performance will be better too, since here we look at every character only once. The other solution will have to look at each character three times.
It still has one problem though: If any of your strings contain the double quote character this parser will fail. The description property of the dictionary will output this in a backslash-quoted form as \" - this will have to be handled in quotedString.
I came back to my old XCode project after a few months and now I have lots of errors where I had none before, which I'm assuming has to do with an updated syntax.
Why am I seeing:
Type of expression is ambiguous without more context
for this block
#IBAction func submitUrl(sender: UIButton) {
var app = UIApplication.sharedApplication().delegate as! AppDelegate
//Error occurs in below declaration of studentDict
var studentDict = ["latitude": self.latitude, "longitude": self.longitude, "firstName": app.firstName, "lastName": app.lastName, "mediaURL": urlField.text]
var studentInfo = StudentInformation(data: studentDict as! [String : AnyObject])
ParseClient.sharedInstance().postStudent(studentInfo, mapString: self.mapString){(success,data) in
if(success){
self.dismissViewControllerAnimated(true, completion: nil)
}
else{
Shared.showError(self,errorString: data["ErrorString"] as! String)
}
}
}
I tried studentDict:NSArray and studentDict:Dictionary because I thought it just couldn't interpret the stuff in studentDict properly, but that did not help. What exactly is the error telling me that I'm missing?
One or more of the values that you init with is probably optional and therefore can be nil. That's why you receive this error
var studentDict:[String:AnyObject?] = ["latitude": self.latitude, "longitude": self.longitude, "firstName": app.firstName, "lastName": app.lastName, "mediaURL": urlField.text]
In case if somebody still faces this kind of issue:
Golden rule of swift - You have to be clear about the type of your
objects.
When you declare method which expects Array as parameter (without specifying the type of objects with in the array i.e. [String] or [MyModelObject] or [SKProduct]) and would like to use the parameter in the definition, you have two choices:
Either you specify the type of the object when try to use / access it. Below as example:
for object in objects as! [SKProduct] {
self.productsToPurchase.addObject(object)
}
Here, objects is an array defined like this: var objects:NSArray?
Or revise the definition of your method and specify the type of objects. Below as example:
func getMeProducts(products: [SKProduct]) {
NSLog("Okay! Ready for purchase")
}
In the latter case, the compiler won't allow you to send generic Array object. By generic I mean where you don't specify the type of objects array is holding on.
I hope this makes sense. If not, read the Golden rule again :)
So I'm trying to build my app for iOS 9 and am running into one problem. Before, I had a button that would take the string from a label and add it to a string that would take a person to lmgtfy and automatically search for the contents of the string, but now I'm running into an error with map(). Here is the code that worked in iOS 8:
#IBAction func googleButton() {
let replaced = String(map(originalString.generate()) { $0 == " " ? "+" : $0 })
if let url = NSURL(string: "http://google.com/?q=\(replaced)") {
UIApplication.sharedApplication().openURL(url)
}
print(replaced)
}
So now the error I'm getting says, "'map' is unavailable: call the 'map()' method on the sequence." Any ideas? Also, I'm not positive that link will work because it is supposed to be lmgtfy but I couldn't submit this question unless I changed the URL to google.
As of Swift 2, String no longer conforms to SequenceType, therefore you can't call generate on it. Instead you need to use the characters property to obtain a String.CharacterView, which does conform to SequenceType.
Also with Swift 2: map is a method in an extension of SequenceType. Therefore you call it like a method, instead of a free function:
let str = "ab cd ef gh"
let replaced = String(str.characters.map { $0 == " " ? "+" : $0 })
// "ab+cd+ef+gh"
You could also do:
let replaced = str.stringByReplacingOccurrencesOfString(" ", withString: "+")
// "ab+cd+ef+gh"
So I have the following function:
func remoteCall(_url:String, _params:[String: String]?=nil){
...
...
Alamofire.request(.POST, _url, parameters: _params) //requires _params to be of type [String String!]
...
}
As you can see, I can do:
let url = "https://eamorr.com/remote.php"
remoteCall(url) //works great!
Or, I can do:
let url = "https://eamorr.com/remote.php"
let params = [
"email": "eamorr#eamorr.com",
"pword": "secret"
]
remoteCall(url, _params:params) //works great!
However, what I can't do is:
let url = "https://eamorr.com/remote.php"
let params = [
"email": email.text, //where "email" is a UITextField
"pword": pword.text //where "pword" is a UITextField
]
remoteCall(url, _params:params) //this doesn't work
I get this error:
'String!' is not identical to 'String'
I need to be able to accomodate all three situations (pass nothing, pass raw strings, and pass UITextField values)
Unfortunately, if I try to change the function signature (note the '!' after the keyword "String") to:
func remoteCall(_url:String, _params:[String: String!]?=nil){
...
...
}
The UITextField values work, passing nil works, but the raw strings situation fails at run-time.
fatal error: can't unsafeBitCast between types of different sizes
EXC_BAD_INSTRUCTION
I'll be using this function a lot, so I would like not to have to wrap my "params" variable up in messy code.
I'm new to Swift, an Objective-C head, and trying to re-learn everything again...
I guess if I can somehow convert any incorrect [String: String] to [String: String!] neatly inside the function without disturbing any nil's or [String: String!]'s that already work?
I think this should work:
let url = "https://eamorr.com/remote.php"
let params: [String: String] = [ // Note explicitly declared type
"email": email.text, //where "email" is a UITextField
"pword": pword.text //where "pword" is a UITextField
]
remoteCall(url, _params:params)
Problem in your case was that without explicit type declaration Swift will infer one for you. Both values in the dictionary you create are of String! type, which is different from String. Hence the error. If you explicitly tell Swift that params is of [String: String] type that should work fine because it is ok to assign String! value to a String variable.
sorry for such a basic question but I ve only just started working with tuples
this is my code
func test() -> (authorName:String, numberOfViews:Int) {
let author : String = ""
let numberViews = 0
return(authorName : author, numberOfView : numberViews)
}
can anyone provide the correct way to do this
thanks in advance
according to the Apple's swift book:
func test() -> (authorName:String, numberOfViews:Int) {
let author : String = ""
let numberViews = 0
return(author, numberViews)
}
you define the return object at the declaration. and in the return statement just put the values.
For create a tuple simply put it in normal brackets and separate each other with comas, you also can do it on te return function
Example :
let exampleTuple = (23, "A string", 5.583)
The article from Apple :
Tuples group multiple values into a single compound value. The values within a tuple can be of any type and do not have to be of the same type as each other.In this example, (404, "Not Found") is a tuple that describes an HTTP status code. An HTTP status code is a special value returned by a web server whenever you request a web page. A status code of 404 Not Found is returned if you request a webpage that doesn’t exist.
let http404Error = (404, "Not Found")