String concatenation issue with Swift - iphone

I'm attempting to use string concatenation to create a URL for a network call. However, I'm getting intermittent results when trying to concatenate strings.
Attached image shows it concatenates to just "h". Relevant code added below.
Am I missing something?
Thank you.
/* Constants */
private let baseURL = "https://someurl.com/"
private let URLSuffix = "anotherString"
private let typeItem = "item/"
class HITNetworkCoordinator: NSObject {
class var sharedInstance : HITNetworkCoordinator {
return _HITNetworkCoordinatorInstance
}
func downloadItem (itemID: Int) {
let taskURLString = baseURL + typeItem + String(itemID) + URLSuffix
let taskURL = NSURL.URLWithString(taskURLString)
let task = NSURLSession.sharedSession().dataTaskWithURL(taskURL, completionHandler: { (data, response, error) -> Void in
var responseDict : NSDictionary? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? NSDictionary
println(responseDict)
})
task.resume()
}

Try this - let taskURLString = "(baseURL) (typeItem) + String(itemID) + (URLSuffix) " and see what happens

String concatenation seems to get flakier as you increase the number of concat operators (+) in an expression. I gave up a while ago, and started using evaluators instead. E.g.
let taskURLString = "\(baseURL)\(typeItem)\(itemID)\(URLSuffix)"

Related

Ambiguous reference to member 'count' #2

I have these two functions:
func search_DocumentMultimedia(documentId: Int) -> Array<Int>{
var array:[Int]
let downloadUrl = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
let path = downloadUrl.appendingPathComponent("mwb_" + language + "_" + issue + ".db")
do{
let db = try Connection(path.absoluteString)
let documentMultimediaTable = Table("DocumentMultimedia")
let documentMultimediaIdField = Expression<Int>("DocumentMultimediaId")
let documentIdField = Expression<Int>("DocumentId")
for media in try db.prepare(documentMultimediaTable.select(documentMultimediaIdField, documentIdField).where(documentIdField == documentId)) {
array.append(media[documentMultimediaIdField])
}
} catch{
print(error)
}
return array
}
func search_Multimedia(multimediaID: [Int]) -> Array<mediaData>{
var array:[mediaData]
let downloadUrl = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
let path = downloadUrl.appendingPathComponent("mwb_" + language + "_" + issue + ".db")
ForEach(0..<multimediaID.count, id: \.self) { index in
do{
let db = try Connection(path.absoluteString)
let documentMultimediaTable = Table("Multimedia")
let MultimediaIdField = Expression<Int>("MultimediaId")
let keySymbol = Expression<String>("KeySymbol")
let track = Expression<Int>("Track")
let issueTagNumber = Expression<Int>("IssueTagNumber")
let mimeType = Expression<String>("MimeType")
let filePath = Expression<String>("FilePath")
for media in try db.prepare(documentMultimediaTable.select(MultimediaIdField, keySymbol, track, issueTagNumber, mimeType, filePath).where(MultimediaIdField == multimediaID[index])) {
let data = mediaData(mediaID: media[MultimediaIdField], mediaType: media[keySymbol], track: media[track], issueTagNumber: media[issueTagNumber], mimeType: media[mimeType], filePath: media[filePath])
array.append(data)
}
} catch{
print(error)
}
}
return array
}
I call them like this:
func getMedia(documentId: Int, nextDocumentId: Int) /*-> Array<videoData>*/ {
let multimediaId:[Int] = JW.search_DocumentMultimedia(documentId: documentId)
let mediaData:[mediaData] = JW.search_Multimedia(multimediaID: multimediaId)
print(mediaData)
}
In search_Multimedia I keep getting an error that says 'Ambiguous reference to member 'count''. I get this error on ForEach statement that uses multimediaID.count. I have tried everything but can't find how to resolve this. Please could you lend a hand? I saw a similar question on here but it seems to be outdated - hence my post.
You are using ForEach where ForEach is not appropriate. The purpose of ForEach is to turn a collection of values into a collection of SwiftUI Views inside the body of some collection view like a List or a VStack.
You don't need to create a collection of Views here. Use a regular Swift for/in statement. Replace this:
ForEach(0..<multimediaID.count, id: \.self) { index in
with this:
for index in 0 ..< multimediaID.count {
I think you have other errors, but this one is particularly important. ForEach (like most SwiftUI types) relies heavily on type inference. Mistakes in a ForEach call can seriously confuse the Swift compiler's type inference implementation. Then the compiler often prints useless or misleading error messages. So if you replace your ForEach with for/in loop, the compiler will probably give you better messages about any other errors you have made in this code.

How to convert json into String array

Hi I'm very new to Swift and I'm trying to make a simple application.
The app gets data from server as JSON format.
func addLangList(completion: #escaping ([String], [String]) -> Void) {
let request = NetworkRequest()
let reqUrl = NetworkInformation.serverAddr + "/word/purpose"
let parameters: Parameters = ["category": "lang"]
request.sendGetRequest(url: reqUrl, parameters: parameters, success: { (response) in
let json = JSON(response)
let isSuccess = json[ServerResponseKey.KEY_RESULT]
if isSuccess == true {
var resultMessage:JSON = json[ServerResponseKey.KEY_MESSAGE]
let lang = resultMessage["lang"].arrayValue
let purpose = resultMessage["purpose"].arrayValue
completion(lang, purpose)
}
}, fail: request.CommonNetworkFailureHandler)
}
By using Swiftyjson, the function converts the data received into JSON format. Inside the closure, 'completion' is called for further process in caller. An error occurs at 'completion(lang, purpose). Xcode says
" Cannot convert value of type '[JSON]' to expected argument type '[String]'".
The error, I guess, because .arrayValue doesn't change resultMessage["lang"] into [String] type....Can anyone give me some advice??
Those 2 arrays
let lang = resultMessage["lang"].array
let purpose = resultMessage["purpose"].array
are of type JSON which isn't String , you need to cast them
let langStr = lang.map { $0.string }
let purposeStr = purpose.map { $0.string }
let langStr = lang.map { $0.string }
let purposeStr = purpose.map { $0.string }

Swift Get Next Page from header of NSHTTPURLResponse

I am consuming an API that gives me the next page in the Header inside a field called Link. (For example Github does the same, so it isn't weird.Github Doc)
The service that I am consuming retrieve me the pagination data in the following way:
As we can see in the "Link" gives me the next page,
With $0.response?.allHeaderFields["Link"]: I get </api/games?page=1&size=20>; rel="next",</api/games?page=25&size=20>; rel="last",</api/games?page=0&size=20>; rel="first".
I have found the following code to read the page, but it is very dirty... And I would like if anyone has dealt with the same problem or if there is a standard way of face with it. (I have also searched if alamofire supports any kind of feature for this but I haven't found it)
// MARK: - Pagination
private func getNextPageFromHeaders(response: NSHTTPURLResponse?) -> String? {
if let linkHeader = response?.allHeaderFields["Link"] as? String {
/* looks like:
<https://api.github.com/user/20267/gists?page=2>; rel="next", <https://api.github.com/user/20267/gists?page=6>; rel="last"
*/
// so split on "," the on ";"
let components = linkHeader.characters.split {$0 == ","}.map { String($0) }
// now we have 2 lines like '<https://api.github.com/user/20267/gists?page=2>; rel="next"'
// So let's get the URL out of there:
for item in components {
// see if it's "next"
let rangeOfNext = item.rangeOfString("rel=\"next\"", options: [])
if rangeOfNext != nil {
let rangeOfPaddedURL = item.rangeOfString("<(.*)>;", options: .RegularExpressionSearch)
if let range = rangeOfPaddedURL {
let nextURL = item.substringWithRange(range)
// strip off the < and >;
let startIndex = nextURL.startIndex.advancedBy(1) //advance as much as you like
let endIndex = nextURL.endIndex.advancedBy(-2)
let urlRange = startIndex..<endIndex
return nextURL.substringWithRange(urlRange)
}
}
}
}
return nil
}
I think that the forEach() could have a better solution, but here is what I got:
let linkHeader = "</api/games?page=1&size=20>; rel=\"next\",</api/games?page=25&size=20>; rel=\"last\",</api/games?page=0&size=20>; rel=\"first\""
let links = linkHeader.components(separatedBy: ",")
var dictionary: [String: String] = [:]
links.forEach({
let components = $0.components(separatedBy:"; ")
let cleanPath = components[0].trimmingCharacters(in: CharacterSet(charactersIn: "<>"))
dictionary[components[1]] = cleanPath
})
if let nextPagePath = dictionary["rel=\"next\""] {
print("nextPagePath: \(nextPagePath)")
}
//Bonus
if let lastPagePath = dictionary["rel=\"last\""] {
print("lastPagePath: \(lastPagePath)")
}
if let firstPagePath = dictionary["rel=\"first\""] {
print("firstPagePath: \(firstPagePath)")
}
Console output:
$> nextPagePath: /api/games?page=1&size=20
$> lastPagePath: /api/games?page=25&size=20
$> firstPagePath: /api/games?page=0&size=20
I used components(separatedBy:) instead of split() to avoid the String() conversion at the end.
I created a Dictionary for the values to hold and removed the < and > with a trim.

Swift String to Array

I have a string in Swift that looks like this:
["174580798","151240033","69753978","122754394","72373738","183135789","178841809","84104360","122823486","184553211","182415131","70707972"]
I need to convert it into an NSArray.
I've looked at other methods on SO but it is breaking each character into a separate array element, as opposed to breaking on the comma. See: Convert Swift string to array
I've tried to use the map() function, I've also tried various types of casting but nothing seems to come close.
Thanks in advance.
It's probably a JSON string so you can try this
let string = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
let data = string.dataUsingEncoding(NSUTF8StringEncoding)!
let jsonArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: nil) as! [String]
as the type [String] is distinct you can cast it forced
Swift 3+:
let data = Data(string.utf8)
let jsonArray = try! JSONSerialization.jsonObject(with: data) as! [String]
The other two answers are working, although SwiftStudiers isn't the best regarding performance. vadian is right that your string most likely is JSON. Here I present another method which doesn't involve JSON parsing and one which is very fast:
import Foundation
let myString = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
func toArray(var string: String) -> [String] {
string.removeRange(string.startIndex ..< advance(string.startIndex, 2)) // Remove first 2 chars
string.removeRange(advance(string.endIndex, -2) ..< string.endIndex) // Remote last 2 chars
return string.componentsSeparatedByString("\",\"")
}
toArray(myString) // ["174580798", "151240033", "69753978", ...
You probably want the numbers though, you can do this in Swift 2.0:
toArray(myString).flatMap{ Int($0) } // [174'580'798, 151'240'033, 69'753'978, ...
which returns an array of Ints
EDIT: For the ones loving immutability and functional programming, have this solution:
func toArray(string: String) -> [String] {
return string[advance(string.startIndex, 2) ..< advance(string.endIndex, -2)]
.componentsSeparatedByString("\",\"")
}
or this:
func toArray(string: String) -> [Int] {
return string[advance(string.startIndex, 2) ..< advance(string.endIndex, -2)]
.componentsSeparatedByString("\",\"")
.flatMap{ Int($0) }
}
Try this. I've just added my function which deletes any symbols from string except numbers. It helps to delete " and [] in your case
var myString = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
var s=myString.componentsSeparatedByString("\",\"")
var someArray: [String] = []
for i in s {
someArray.append(deleteAllExceptNumbers(i))
}
println(someArray[0]);
func deleteAllExceptNumbers(str:String) -> String {
var rez=""
let digits = NSCharacterSet.decimalDigitCharacterSet()
for tempChar in str.unicodeScalars {
if digits.longCharacterIsMember(tempChar.value) {
rez += tempChar.description
}
}
return rez.stringByReplacingOccurrencesOfString("\u{22}", withString: "")
}
Swift 1.2:
If as has been suggested you are wanting to return an array of Int you can get to that from myString with this single concise line:
var myArrayOfInt2 = myString.componentsSeparatedByString("\"").map{$0.toInt()}.filter{$0 != nil}.map{$0!}
In Swift 2 (Xcode 7.0 beta 5):
var myArrayOfInt = myString.componentsSeparatedByString("\"").map{Int($0)}.filter{$0 != nil}.map{$0!}
This works because the cast returns an optional which will be nil where the cast fails - e.g. with [, ] and ,. There seems therefore to be no need for other code to remove these characters.
EDIT: And as Kametrixom has commented below - this can be further simplified in Swift 2 using .flatMap as follows:
var myArrayOfInt = myString.componentsSeparatedByString("\"").flatMap{ Int($0) }
Also - and separately:
With reference to Vadian's excellent answer. In Swift 2 this will become:
// ...
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! [String]
} catch {
_ = error // or do something with the error
}

How to print call stack in Swift?

In Objective-C, you can print the call stack by doing the following:
NSLog(#"%#", [NSThread callStackSymbols]);
How do you do this in Swift without using Foundation class?
As Jacobson says, use the following:
Swift 2:
print(NSThread.callStackSymbols())
Swift 3 / Swift 4:
print(Thread.callStackSymbols)
That's Swift code. It's using a Foundation method, but so does 90%+ of what you do on iOS.
EDIT:
Note that the formatting looks better if you use:
Thread.callStackSymbols.forEach{print($0)}
From the debugger command line you can type
e Thread.callStackSymbols.forEach{print($0)}
For Swift 3 use:
print(Thread.callStackSymbols)
or for better formatting
for symbol: String in Thread.callStackSymbols {
print(symbol)
}
This improves the output a little.
for symbol: String in NSThread.callStackSymbols() {
NSLog("%#", symbol)
}
print(Thread.callStackSymbols.joined(separator: "\n"))
With this code, one can see the calls in different lines each.
1 MyApp 0x0000000100720780 $s9MyAppModule....
2 CoreFoundation 0x0000000181f04c4c EA9C1DF2-94C7-379B-BF8D-970335B1552F + 166988
3 CoreFoundation 0x0000000181f99554 EA9C1DF2-94C7-379B-BF8D-970335B1552F + 775508
4 CoreFoundation 0x0000000181f6eb34 EA9C1DF2-94C7-379B-BF8D-970335B1552F + 600884
5 CoreFoundation 0x0000000181f19754 _CFXNotificationPost + 696
6 Foundation 0x0000000183634138 86D8A58D-B71F-34C6-83E0-014B2B835F1D + 106808
Here's a great utility class I found on github:
https://github.com/nurun/swiftcallstacktrace
You get a tuple (class,method) of any stack trace symbol so you can do a clean printout.
CallStackAnalyser.classAndMethodForStackSymbol(NSThread.callStackSymbols()[2])
Edit: swift 4.1 update
https://github.com/GDXRepo/CallStackParser
I needed to write the callstack to a log file so I tweaked it like so.
var ErrorStack = String()
Thread.callStackSymbols.forEach {
print($0)
ErrorStack = "\(ErrorStack)\n" + $0
}
Thread.callStackSymbols() is nice to have. But the traceback is ugly. Demangling would be nice. The Swift 4.1+ demangler linked in #MikeS's answer is extremely comprehensive and impressive, but it's also over 4000 lines of code, overkill if you just need app, class and method, and it's quite a lot to add to a project, which I'd prefer to not to risk forgetting not to ship my app with :-)
This is a quick prototype of something that does some basic demangling of appname, class and method (which are the easy part to figure out). It's not polished. For example, it doesn't check for nil/failures in the regex ops, since it just gets a line from the callstack, which should be consistent enough to avoid problems. However, improved versions of it are welcome answers.
I added it to a class I named Debug, where I keep other debugging stuff, and invoke it from wherever in my app as:
Debug.whence()
    ... note: "where" is a Swift reserved word, and whence means basically the same thing.
It prints a line of this form (only one line, not full stack):
EventEditorViewController.configureNavigationItem():419 I'll probably add an argument to take an optional object arg and then do a refined display of the object and its address without some of the parameters and syntax swift's builtin obj dump logging does, so that it would display obj info and where it is being traced.
This probably can only parse lines inside the app. It probably can't demangle non-Swift calls (like Foundation), not sure. If you need a more comprehensive demangler, check #MikeS's answer.
static func whence(_ lineNumber: Int = #line) {
func matchRegex(_ matcher: String, string : String) -> String? {
let regex = try! NSRegularExpression(pattern: matcher, options: [])
let range = NSRange(string.startIndex ..< string.endIndex, in: string)
guard let textCheckingResult = regex.firstMatch(in: string, options: [], range: range) else {
return nil
}
return (string as NSString).substring(with:textCheckingResult.range(at:1)) as String
}
func singleMatchRegex(_ matcher: String, string : String) -> String? {
let regex = try! NSRegularExpression(pattern: matcher, options: [])
let range = NSRange(string.startIndex ..< string.endIndex, in: string)
let matchRange = regex.rangeOfFirstMatch(in: string, range: range)
if matchRange == NSMakeRange(NSNotFound, 0) {
return nil
}
return (string as NSString).substring(with: matchRange) as String
}
var string = Thread.callStackSymbols[1]
string = String(string.suffix(from:string.firstIndex(of: "$")!))
let appNameLenString = matchRegex(#"\$s(\d*)"#, string: string)!
let appNameLen = Int(appNameLenString)!
string = String(string.dropFirst(appNameLenString.count + 2))
let appName = singleMatchRegex(".{\(appNameLen)}", string: string)!
string = String(string.dropFirst(appNameLen))
let classNameLenString = singleMatchRegex(#"\d*"#, string: string)!
let classNameLen = Int(classNameLenString)!
string = String(string.dropFirst(classNameLenString.count))
let className = singleMatchRegex(".{\(classNameLen)}", string: string)!
string = String(string.dropFirst(classNameLen))
let methodNameLenString = matchRegex(#".(\d*)"#, string: string)!
let methodNameLen = Int(methodNameLenString)!
string = String(string.dropFirst(methodNameLenString.count + 1))
let methodName = singleMatchRegex(".{\(methodNameLen)}", string: string)!
let _ = appName
print("\(className).\(methodName)():\(lineNumber)")
}