2

I somehow miss what they want me to do when I want to cut a String in Swift. They told me Swift was "objective". But it rather is "not working". Removing a sub range from a string is explained in the docs (https://developer.apple.com/documentation/swift/string/3018537-removesubrange), which I read. But before I move back to ObjC, let me ask this question: From

measurements.removeSubrange(1..<4)

I made

logString = logString.removeSubrange(logSizeLimit..logString.count)

It said Cannot find operator '..' in scope; did you mean '...'? I changed it to be .... It now tells me:

(1.) Cannot assign value of type '()' to type 'String'
(2.) Instance method 'removeSubrange' requires the types 'String.Index' and 'Int' be equivalent

This is my code:

public let logSizeLimit = 40960
public var logString = ""
func senderLoggedMessage(_ message:String) {
    var msg = message + "\n"
    msg += logString
    logString = msg
    if logString.count > logSizeLimit {
        logString = logString.removeSubrange(logSizeLimit ... logString.count) // error line
    }
}

Well. Does anyone know the answer?

Anticro
  • 685
  • 4
  • 12
  • 1
    Please read: https://stackoverflow.com/questions/39676939/how-does-string-index-work-in-swift – Sweeper Feb 10 '22 at 18:38
  • 1
    It seems like you don't need `removeSubrange` at all. You can just do `logString = String(logString.prefix(logSizeLimit))` – Sweeper Feb 10 '22 at 18:42
  • 1
    Aren't we supposed to use ```removeSubrange``` on a collection? Not sure. – udi Feb 10 '22 at 18:48
  • @Sweeper: Thank you for the advice. But `logString.removeSubrange(logSizeLimit.. – Anticro Feb 10 '22 at 19:27

1 Answers1

2

There are a few problems with your code:

  1. removeSubrange is a mutating method that returns Void. So logString.removeSubrange(_:) modifies logString in place. It does not return a new String. You would have exactly the same sort of problem with the -[NSMutableString deleteCharactersInRange:] method in Objective-C.

  2. You are trying to use Int to index into a String's characters. But Swift Strings do not use Int indices.

    There is a type, String.Index, which you can use to index into a String with removeSubrange and other functions. But I don't recommend it.

    Swift's String is very, very careful about handling Unicode properly. So a String is not just an array of bytes or of UTF-16 code units as in many other languages. A String is a collection of Characters, and a Character is a “user-perceived character” as discussed in Unicode Standard Annex #29: Unicode Text Segmentation.

    The Character ‘a’ is one code point (U+0061) and one UTF-8 code unit, but the Character ‘‍‍‍’ is seven code points (U+1f468 U+200d U+1f469 U+200d U+1f467 U+200d U+1f466) and 25 UTF-8 code units.

    Because each Character in a String may use a different amount of storage, manipulating a String.Index is not trivial the way manipulating an Int is. You have to use methods on String like index(after:) and distance(from:to:) instead of operators like + and -.

In my experience, it is much, much easier to slice up a String using operations like .prefix(_:) and .drop(while:). These operations generally return a Substring. So at the end you can use String.init to convert the Substring back to a String if needed.

I would write your function like this:

public let logSizeLimit = 40960
public var logString = ""
func senderLoggedMessage(_ message:String) {
    logString = String("\(message)\n\(logString)"
        .prefix(logSizeLimit))
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • This is basically a rather detailed explanation and you got an upvote for it. It is just missing something: In my case the compiler complained about `..`, but the Apple docs say one must use `..`. It makes sense that `String.Index` is not equivalent to `Int`. But if Apple's docs say `measurements.removeSubrange(1..<4)` was correct, how do you actually call that method in real life and real code? How do you create `String.Index`? Let it be some string of unknown length and you want to remove 90% of the characters from the middle of the string (Remaining 5% at start and end each). – Anticro Feb 16 '22 at 13:02
  • 1
    You start with `string.startIndex` or `string.endIndex` and then use other methods like `string.index(_:offsetBy:)`. But again I'd avoid indices and instead say `string.prefix(string.count / 20) + string.suffix(string.count / 20)`. – rob mayoff Feb 16 '22 at 13:18