Swift Package Index



String Scanner in pure Swift (supports unicode)


  • 1.1.0 and master
  • 1.1.0 and master

Build Status codecov Platform Platform Language: Swift CocoaPods Carthage


SwiftScanner is a pure native Swift implementation of a string scanner; with no dependecies, full unicode support (who does not love emoji?), lots of useful featurs and swift in mind, StringScanner is a good alternative to built-in Apple's NSScanner.

Related Projects

I'm also working on several other projects you may like. Take a look below:

Library Description
SwiftDate The best way to manage date/timezones in Swift
Hydra Write better async code: async/await & promises
FlowKit A new declarative approach to table managment. Forget datasource & delegates.
SwiftRichString Elegant & Painless NSAttributedString in Swift
SwiftLocation Efficient location manager
SwiftMsgPack Fast/efficient msgPack encoder/decoder

Main Features

SwiftScanner is initialized with a string and mantain an internal index used to navigate backward and forward through the string using two main concepts:

  • scan to return string which also increment the internal index
  • peek to return a string without incrementing the internal index

Results of these operations returns collected String or Indexes. If operation fail due to an error (ie. eof, notFound, invalidInt...) and exception is thrown, in pure Swift style.

API Documentation


scan functions


let scanner = StringScanner("Hello this is SwiftScanner")
let firstChar = try! scanner.scanChar() // get 'H'


let scanner = StringScanner("15 apples")
let parsedInt = try! scanner.scanInt() // get Int=15


let scanner = StringScanner("45.54 $")
let parsedFloat = try! scanner.scanFloat() // get Int=45.54
  • 0x[VALUE] (example: 0x0000000000564534)
  • 0X[VALUE] (example: 0x0929)
  • #[VALUE] (example: #1602)

If scan succeded scanner's position is updated at the end of the represented string, otherwise an exception ((.notFound, ).invalidHex, .eof) is thrown and index is not touched.


let scanner = StringScanner("#1602")
let value = try! scanner.scanHexInt(.bit16) // get Int=5634

let scanner = StringScanner("#0x0929")
let value = try! scanner.scanHexInt(.bit16) // get Int=2345

let scanner = StringScanner("#0x0000000000564534")
let value = try! scanner.scanHexInt(.bit64) // get Int=5653812


let scanner = StringScanner("Hello <bold>Daniele</bold>")
let partialString = try! scanner.scan(upTo: "<bold>") // get "Hello "


let scanner = StringScanner("Hello, I've at least 15 apples")
let partialString = try! scanner.scan(upTo: CharacterSet.decimalDigits) // get "Hello, I've at least "


let scanner = StringScanner("HELLO i'm mark")
let partialString = try! scanner.scan(untilIn: CharacterSet.lowercaseLetters) // get "HELLO"


let scanner = StringScanner("This is a simple test I've made")
let partialString = try! scanner.scan(upTo: "I've") // get "This is a simple test "


let scanner = StringScanner("Never be satisfied 💪 and always push yourself! 😎 Do the things people say cannot be done")
let delimiters = CharacterSet(charactersIn: "💪😎")
while !scanner.isAtEnd {
  let block = scanner.scan(untilTrue: { char in
    return (delimiters.contains(char) == false)
  // Print:
  // "Never be satisfied " (first iteration)
  // "and always push yourself!" (second iteration)
  // "Do the things people say cannot be done" (third iteration)
  print("Block: \(block)")
	try scanner.skip() // push over the character


let scanner = StringScanner("Never be satisfied")
let partialString = scanner.scan(5) // "Never"

peek functions

Peek functions are the same as concept of scan() but unless it it does not update internal scanner's position index. These functions usually return only starting index of matched pattern.


let scanner = StringScanner("Never be satisfied")
let index = try! scanner.peek(upTo: "b") // return 6


let scanner = StringScanner("You are in queue: 123 is your position")
let index = try! scanner.peek(upTo: CharacterSet.decimalDigits) // return 18


let scanner = StringScanner("654 apples")
let index = try! scanner.peek(untilIn: CharacterSet.decimalDigits) // return 3


let scanner = StringScanner("654 apples in the bug")
let index = try! scanner.peek(upTo: "in") // return 11


let scanner = StringScanner("I'm very 💪 and 😎 Go!")
let delimiters = CharacterSet(charactersIn: "💪😎")
while !scanner.isAtEnd {
  let prevIndex = scanner.position
  let finalIndex = scanner.peek(untilTrue: { char in
    return (delimiters.contains(char) == false)
  // Distance will return:
  // - 9 (first iteration)
  // - 5 (second iteration)
  // - 4 (third iteration)
	let distance = scanner.string.distance(from: prevIndex, to: finalIndex)
	try scanner.skip(length: distance + 1)

Other Functions

let scanner = StringScanner("💪 and 😎")
let match = scanner.match("😎") // return false
let scanner = StringScanner("I'm very 💪 and 😎 Go!")
scanner.match("I'm very") // return true


pod 'SwiftScanner'


github 'malcommac/SwiftScanner'

Swift Package Manager

Add swiftline as dependency in your Package.swift

  import PackageDescription

  let package = Package(name: "YourPackage",
    dependencies: [
      .Package(url: "https://github.com/malcommac/SwiftScanner.git", majorVersion: 0),

Run them with

swift test

Current version is compatible with:

  • Swift 4.x >= 1.0.4

  • Swift 3.x: up to 1.0.3

  • iOS 8 or later

  • macOS 10.10 or later

  • watchOS 2.0 or later

  • tvOS 9.0 or later

  • ...and virtually any platform which is compatible with Swift 3 and implements the Swift Foundation Library

As open source creation any help is welcome!

The code of this library is licensed under MIT License; you can use it in commercial products without any limitation.

The only requirement is to add a line in your Credits/About section with the text below:

Portions SwiftScanner - http://github.com/malcommac/SwiftScanner
Created by Daniele Margutti and licensed under MIT License.