Trim End Off of String in Swift, Getting Error at Runtime

Trim end off of string in swift, getting error at runtime

There are two different advance() functions:

/// Return the result of advancing `start` by `n` positions. ...
func advance<T : ForwardIndexType>(start: T, n: T.Distance) -> T

/// Return the result of advancing start by `n` positions, or until it
/// equals `end`. ...
func advance<T : ForwardIndexType>(start: T, n: T.Distance, end: T) -> T

Using the second one you can ensure that the result is within the valid bounds of the string:

let truncatedText = answerString.substringToIndex(advance(answerString.startIndex, 8, answerString.endIndex))

Update for Swift 2/Xcode 7:

let truncatedText = answerString.substringToIndex(answerString.startIndex.advancedBy(8, limit: answerString.endIndex))

But a simpler solution is

let truncatedText = String(answerString.characters.prefix(8))

Update for Swift 3/Xcode 8 beta 6: As of Swift 3, "collections move
their index", the corresponding code is now

let to = answerString.index(answerString.startIndex,
offsetBy: 8,
limitedBy: answerString.endIndex)
let truncatedText = answerString.substring(to: to ?? answerString.endIndex)

The simpler solution

let truncatedText = String(answerString.characters.prefix(8))

still works.

Conforming String.CharacterView.Index to Strideable: fatal error when using stride(to:by:): cannot increment endIndex

Simply declaring the protocol conformance

extension String.CharacterView.Index : Strideable { }

compiles because String.CharacterView.Index conforms to
BidirectionalIndexType , and ForwardIndexType/BidirectionalIndexType have default method implementations for advancedBy() and distanceTo()
as required by Strideable.

Strideable has the default protocol method implementation
for stride():

extension Strideable {
// ...
public func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
}

So the only methods which are "directly" implemented for
String.CharacterView.Index are – as far as I can see - the successor() and predecessor() methods from BidirectionalIndexType.

As you already figured out, the default method implementation of
stride() does not work well with String.CharacterView.Index.

But is is always possible to define dedicated methods for a concrete type. For the problems of making String.CharacterView.Index conform to Strideable see
Vatsal Manot's answer below and the discussion in the comments – it took me a while to get what he meant :)

Here is a possible implementation of a stride(to:by:) method for String.CharacterView.Index:

extension String.CharacterView.Index {
typealias Index = String.CharacterView.Index

func stride(to end: Index, by stride: Int) -> AnySequence<Index> {

precondition(stride != 0, "stride size must not be zero")

return AnySequence { () -> AnyGenerator<Index> in
var current = self
return AnyGenerator {
if stride > 0 ? current >= end : current <= end {
return nil
}
defer {
current = current.advancedBy(stride, limit: end)
}
return current
}
}
}
}

This seems to work as expected:

let str = "01234"
str.startIndex.stride(to: str.endIndex, by: 2).forEach {
print($0,str.characters[$0])
}

Output

0 0
2 2
4 4

Remove Last Two Characters in a String

update: Xcode 9 • Swift 4 or later

String now conforms to RangeReplaceableCollection so you can use collection method dropLast straight in the String and therefore an extension it is not necessary anymore. The only difference is that it returns a Substring. If you need a String you need to initialize a new one from it:

let string = "0123456789"
let substring1 = string.dropLast(2) // "01234567"
let substring2 = substring1.dropLast() // "0123456"
let result = String(substring2.dropLast()) // "012345"

We can also extend LosslessStringConvertible to add trailing syntax which I think improves readability:

extension LosslessStringConvertible {
var string: String { .init(self) }
}

Usage:

let result = substring.dropLast().string

SwiftyJSON - Unable to get transactionId from JSON String

The problem is not the code. Consider:

func parse(message: String) -> String {
let json = JSON(parseJSON: message)
return json["Request"]["transactionId"].stringValue
}

let input = """
{"Request": {"content": {"Reset": {}}, "transactionId": "f7c4d630-552b-46d9-a37d-44450537b48d"}}
"""

let transactionID = parse(message: input)

print("transactionId:", transactionID)

let response = String(format: "{\"Response\":{\"transactionId\":\"%@\",\"content\":{\"Reset\":{}}}}", transactionID)

print("response:", response)

The result of the above, as you’d expect, is:

transactionId: f7c4d630-552b-46d9-a37d-44450537b48d

response: {"Response":{"transactionId":"f7c4d630-552b-46d9-a37d-44450537b48d","content":{"Reset":{}}}}

So I suspect that the input message is not quite what you expected. So I might suggest adding some error handling so you can diagnose precisely where it is going wrong:

func parse(message: String) -> String {
let json = JSON(parseJSON: message)
if let error = json.error {
fatalError("JSON parsing error: \(error)")
}

let request = json["Request"]
if let error = request.error {
fatalError("request error: \(error)")
}

let transactionId = request["transactionId"]
if let error = transactionId.error {
fatalError("transactionId error: \(error)")
}

return transactionId.stringValue
}

Now, in practice, you probably wouldn’t use fatalError, but rather would do some graceful error handling (e.g. change parse such that it throws, then throw the errors encountered, if any, and then catch the error where you call parse and handle any runtime issues gracefully). But during this diagnostic process, fatalError is useful because it will stop your debugger at the offending line, simplifying your diagnostic process.

Bottom line, the request must not be quite in the form you expect. Note, it’s going to be very sensitive to capitalization, malformed JSON, etc. So, by looking at the errors provided by SwiftyJSON, you should be able to narrow down the issue quite quickly.


Below, you tell us that the Data is:

<7b225265 71756573 74223a20 7b22636f 6e74656e 74223a20 7b225265 73657422
3a207b7d 7d2c2022 7472616e 73616374 696f6e49 64223a20 22643937 36303036
622d3130 38302d34 3864652d 39323232 2d623139 63363663 35303164 31227d7d
00>

The problem is that last byte, 0x00. If you remove that, it works.

FWIW, when I convert that hex string back to a Data and run it through JSONSerialization, it confirms the diagnosis:

Error Domain=NSCocoaErrorDomain

Code=3840 "Garbage at end."

UserInfo={NSDebugDescription=Garbage at end.}

You need to figure out why that 0x00 was included in the end of your payload and remove it.

Can't form Range with upperBound lowerBound

You should first parse your date strings, which are UTC time, into date objects using DateFormatter or ISO8601DateFormatter and get the month and day representation from the resulting dates:

extension Formatter {
static let monthDay: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM.dd"
return dateFormatter
}()
}

extension Formatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
}()
}

Playground testing:

let dealStatus: [String: Any] = ["dateStart": "2019-08-21T14:54:03.285108Z",
"dateEnd" : "2019-09-20T06:15:03.285108Z"]

if let dateStart = dealStatus["dateStart"] as? String,
let dateEnd = dealStatus["dateEnd"] as? String,
let start = Formatter.iso8601.date(from: dateStart),
let end = Formatter.iso8601.date(from: dateEnd) { // ,
// let catTitle = ordersResponseArray[indexPath.row]["title"] as? String {

let startTime = Formatter.monthDay.string(from: start)
let endTime = Formatter.monthDay.string(from: end)
let startEndDates = "(" + startTime + " - " + endTime + ")"
print(startEndDates) // "(08.21 - 09.20)"
// cell.titleAndDatesLabel.text = catTitle + " " + startEndDates
}

Localised Strings Xcode 4 ... Copy .strings file Error Validation failed

Could be as simple as a missing semi-colon at the end of the line.



Related Topics



Leave a reply



Submit