# Forward declare \`let\` to be initialized later

**URL:** <https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206>\
**Category:** Using Swift\
**Created:** [December 23, 2022, 5:10pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206 "2022-12-23T17:10:11Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![young](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/young/32/7878_2.png) [@young](https://forums.swift.org/u/young)\
**Post date:** [December 23, 2022, 5:10pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/1 "2022-12-23T17:10:12Z")

</div>

```swift
func breakApart(_ s: AttributedString) -> (integer: String, decimalSeparator: String, fraction: String, percent: String) {
    let integerPart: String
    let decimalSeparatorPart: String
    let fractionPart: String
    let percentPart: String
    
    s.runs.forEach { run in
        if let numberRun = run.numberPart {
            switch numberRun {
            case .integer:
                integerPart = s[run.range].description // Cannot assign to value: 'integerPart' is a 'let' constant
            case .fraction:
                fractionPart = s[run.range].description // Cannot assign to value: 'fractionPart' is a 'let' constant
            }
        }
        
        if let symbolRun = run.numberSymbol {
            switch symbolRun {
            case .decimalSeparator:
                fractionPart = s[run.range].description // Cannot assign to value: 'fractionPart' is a 'let' constant
            case .percent:
                percentPart = s[run.range].description // Cannot assign to value: 'percentPart' is a 'let' constant
            case .groupingSeparator:
                break
            case .sign:
                break
            case .currency:
                break
            @unknown default:
                break
            }
        }
    }
    
    return (integerPart, decimalSeparatorPart, fractionPart, percentPart)
}

```

So the errors are because the compiler don't know if all the let's are assigned?

How should I do this?

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [December 23, 2022, 5:19pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/2 "2022-12-23T17:19:19Z")

</div>

Use `var`, and possibly Optional. I know it stinks, but that’s what you do when you know more than the compiler does. (In this case you know that each value will be assigned exactly once by the end of the loop. Or do you? Maybe there are some edge cases you need to handle.)

---

<div class="post-metadata">

**Author:** ![young](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/young/32/7878_2.png) [@young](https://forums.swift.org/u/young)\
**Post date:** [December 23, 2022, 5:29pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/3 "2022-12-23T17:29:55Z")

</div>

So this is what I end up:

```swift
func breakApart(_ s: AttributedString) -> (integer: String, decimalSeparator: String, fraction: String, percent: String) {
    var integerPart: String?
    var decimalSeparatorPart: String?
    var fractionPart: String?
    var percentPart: String?
    
    s.runs.forEach { run in
        if let numberRun = run.numberPart {
            switch numberRun {
            case .integer:
                integerPart = String(s[run.range].characters) // is this the right way?
            case .fraction:
                fractionPart = String(s[run.range].characters) // is this the right way?
            }
        }
        
        if let symbolRun = run.numberSymbol {
            switch symbolRun {
            case .decimalSeparator:
                decimalSeparatorPart = String(s[run.range].characters) // is this the right way?
            case .percent:
                percentPart = String(s[run.range].characters) // is this the right way?
            case .groupingSeparator:
                break
            case .sign:
                break
            case .currency:
                break
            @unknown default:
                break
            }
        }
    }
        
    
    guard let integerPart, let decimalSeparatorPart, let fractionPart, let percentPart else {
        fatalError("This AttributedString is not in percent format")
    }
    
    return (integerPart, decimalSeparatorPart, fractionPart, percentPart)
}

```

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [December 23, 2022, 5:39pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/4 "2022-12-23T17:39:06Z")

</div>

Yep, that’s what I was thinking. You may also want to check if a string has two of the same numberPart, etc

---

<div class="post-metadata">

**Author:** ![young](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/young/32/7878_2.png) [@young](https://forums.swift.org/u/young)\
**Post date:** [December 23, 2022, 5:58pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/5 "2022-12-23T17:58:08Z")

</div>

> [@jrose](#):
>
> You may also want to check if a string has two of the same numberPart, etc

What do you mean? My `AttributedString` is created using percent format:

0.49754.formatted(.percent.attributed)

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [December 23, 2022, 6:19pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/6 "2022-12-23T18:19:33Z")

</div>

If you control the input string then it’s less important, sure. But that’s now an unchecked precondition of your function, and it’s worth acknowledging that. :-)

---

<div class="post-metadata">

**Author:** ![young](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/young/32/7878_2.png) [@young](https://forums.swift.org/u/young)\
**Post date:** [December 23, 2022, 6:46pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/7 "2022-12-23T18:46:35Z")

</div>

> [@jrose](#):
>
> precondition

There is really no way to be wrong because the user input is a Double and I just formatted it using `.percent.attributed` and get this AttributedString. So no way to get some wrong formatted AttributedString because it's the FormatStyle that produce this.

Anyway, just to catch any possible error, the guard at the tail before return is a runtime check.

Can I do better?

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [December 23, 2022, 6:54pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/8 "2022-12-23T18:54:46Z")

</div>

That’s guarding against a missing section, but it doesn’t guard against a duplicate section like “1.2.3%” (assuming that was attributed appropriately). You’d have to check for nil before assigning to your result variables to catch that.

---

<div class="post-metadata">

**Author:** ![young](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/young/32/7878_2.png) [@young](https://forums.swift.org/u/young)\
**Post date:** [December 23, 2022, 7:00pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/9 "2022-12-23T19:00:23Z")

</div>

> [@jrose](#):
>
> “1.2.3%”

How is this possible?

```swift
func foo(_ value: Double) {
    var s = value.formatted(.percent.attributed) // <== it's from here
    // so now how is that "duplicate segment" happen?

    // I then go on to "parse" out the 4 runs that's now in this AttributtedString
}

```

If I understand, you want me to check for nil before assigning? and if it's not nil it's an error condition?

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [December 23, 2022, 7:04pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/10 "2022-12-23T19:04:25Z")

</div>

Again, if you control the input it shouldn’t happen. But you don’t _quite_ control the input here; you’re using Foundation, which could change across OS versions. (Unless they document that the output will have precisely one of each attribute, in which case it would be rather unfair of them to change it!)

Anyway, you’re probably fine. I just wanted to cover all bases in case someone else has a similar problem but _doesn’t_ have as much control over the input string.

---

<div class="post-metadata">

**Author:** ![young](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/young/32/7878_2.png) [@young](https://forums.swift.org/u/young)\
**Post date:** [December 23, 2022, 7:09pm UTC](https://forums.swift.org/t/forward-declare-let-to-be-initialized-later/62206/11 "2022-12-23T19:09:30Z")

</div>

My code as of now:

```swift
    static func applyingFormatStyle(_ value: Double) -> AttributedString {
        var s = value.formatted(.percent.precision(.fractionLength(1...2)).attributed)
        
        let font = Font.system(size: 250, weight: .bold, design: .rounded)
        let fontSmall = Font.system(size: 150, weight: .bold, design: .rounded)
        
        var integerRange: Range<AttributedString.Index>?
        var decimalSeparatorRange: Range<AttributedString.Index>?
        var fractionRange: Range<AttributedString.Index>?
        var percentRange: Range<AttributedString.Index>?

        s.runs.forEach { run in
            if let numberRun = run.numberPart {
                switch numberRun {
                case .integer:
                    assert(integerRange == nil, "Seen integer part already form input \(value)")
                    integerRange = run.range
                case .fraction:
                    assert(fractionRange == nil, "Seen fraction part already form input \(value)")
                    fractionRange = run.range
                @unknown default:
                    break
                }
            }
            
            if let symbolRun = run.numberSymbol {
                switch symbolRun {
                case .decimalSeparator:
                    assert(decimalSeparatorRange == nil, "Seen decimalSeparator part already form input \(value)")
                    decimalSeparatorRange = run.range
                case .percent:
                    assert(percentRange == nil, "Seen percent part already form input \(value)")
                    percentRange = run.range
                case .groupingSeparator:
                    break
                case .sign:
                    break
                case .currency:
                    break
                @unknown default:
                    break
                }
            }
        }
            
        
        guard let integerRange, let decimalSeparatorRange, let fractionRange, let percentRange else {
            fatalError("This AttributedString is not in percent format")
        }
        
        s[integerRange].font = font
        s[decimalSeparatorRange].font = fontSmall
        s[fractionRange].font = fontSmall
        s[percentRange].font = font

        
        return s
    }

```
