Cast CFString to String

Hi swift-users,

I have found another weird behavior (IMO) and wanted to ask for the right way to handle this:

Imagine I want to switch over a Swift string which contains a UTI to map that UTI to an enum.

(A playground is attached for you to easily reproduce, this is tested with Xcode 9.1's included toolchain, also happens in projects)

I would expect the following to work:

import MobileCoreServices

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

The error I get here is

warning: UTITest.playground:8:26: warning: 'as' test is always true
        case kUTTypeText as String:
                         ^

error: UTITest.playground:8:14: error: expression pattern of type 'CFString' cannot match values of type 'String'
        case kUTTypeText as String:
             ^~~~~~~~~~~
             ^~~~~~~~~~~
The only way I found to resolve this is to also import Foundation (which makes sense but is not really obvious).

Alright, that gives me this:

import MobileCoreServices
import Foundation

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

warning: UTITest.playground:8:26: warning: 'as' test is always true
        case kUTTypeText as String:
                         ^

error: UTITest.playground:8:14: error: 'CFString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?
        case kUTTypeText as String:
             ^
                         as String

Uhm, okay? So let's do that:

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

As weird as it looks, it works ... My question is: Is this behavior intended?

Thanks!

- Dennis

UTITest.playground.zip (1.99 KB)

I found a related bug here: [SR-6204] Forced to cast twice for case pattern matching · Issue #48756 · apple/swift · GitHub

···

On Dec 5, 2017, at 10:10 AM, Dennis Weissmann via swift-users <swift-users@swift.org> wrote:

Hi swift-users,

I have found another weird behavior (IMO) and wanted to ask for the right way to handle this:

Imagine I want to switch over a Swift string which contains a UTI to map that UTI to an enum.

(A playground is attached for you to easily reproduce, this is tested with Xcode 9.1's included toolchain, also happens in projects)

I would expect the following to work:

import MobileCoreServices

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

The error I get here is

warning: UTITest.playground:8:26: warning: 'as' test is always true
        case kUTTypeText as String:
                         ^

error: UTITest.playground:8:14: error: expression pattern of type 'CFString' cannot match values of type 'String'
        case kUTTypeText as String:
             ^~~~~~~~~~~
             ^~~~~~~~~~~
The only way I found to resolve this is to also import Foundation (which makes sense but is not really obvious).

Alright, that gives me this:

import MobileCoreServices
import Foundation

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

warning: UTITest.playground:8:26: warning: 'as' test is always true
        case kUTTypeText as String:
                         ^

error: UTITest.playground:8:14: error: 'CFString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?
        case kUTTypeText as String:
             ^
                         as String

Uhm, okay? So let's do that:

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

As weird as it looks, it works ... My question is: Is this behavior intended?

Thanks!

- Dennis
<UTITest.playground>
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Another workaround would be

    public init(uti: String) {
        switch uti.lowercased() {
        case String(kUTTypeText):
            self = .text
        default:
            self = .unknown
        }
    }

because there is a `String(_ cocoaString: NSString)` initializer in Foundation.

Regards, Martin

···

Am 05.12.2017 um 10:09 schrieb Dennis Weissmann via swift-users <swift-users@swift.org>:

Hi swift-users,

I have found another weird behavior (IMO) and wanted to ask for the right way to handle this:

Imagine I want to switch over a Swift string which contains a UTI to map that UTI to an enum.

(A playground is attached for you to easily reproduce, this is tested with Xcode 9.1's included toolchain, also happens in projects)

I would expect the following to work:

import MobileCoreServices

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

The error I get here is

warning: UTITest.playground:8:26: warning: 'as' test is always true
        case kUTTypeText as String:
                         ^

error: UTITest.playground:8:14: error: expression pattern of type 'CFString' cannot match values of type 'String'
        case kUTTypeText as String:
             ^~~~~~~~~~~
             ^~~~~~~~~~~
The only way I found to resolve this is to also import Foundation (which makes sense but is not really obvious).

Alright, that gives me this:

import MobileCoreServices
import Foundation

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

warning: UTITest.playground:8:26: warning: 'as' test is always true
        case kUTTypeText as String:
                         ^

error: UTITest.playground:8:14: error: 'CFString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?
        case kUTTypeText as String:
             ^
                         as String

Uhm, okay? So let's do that:

enum MimeType {

    case text
    case unknown

    public init(uti: String) {
        // Source: System-Declared Uniform Type Identifiers
        switch uti.lowercased() {
        case kUTTypeText as String as String:
            self = .text
        default:
            self = .unknown
        }
    }

}

As weird as it looks, it works ... My question is: Is this behavior intended?

Thanks!

- Dennis
<UTITest.playground>
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Thanks Martin, that's much nicer!

- Dennis

···

On Dec 5, 2017, at 11:15 AM, Martin R <martinr448@gmail.com> wrote:

Another workaround would be

   public init(uti: String) {
       switch uti.lowercased() {
       case String(kUTTypeText):
           self = .text
       default:
           self = .unknown
       }
   }

because there is a `String(_ cocoaString: NSString)` initializer in Foundation.

Regards, Martin

Am 05.12.2017 um 10:09 schrieb Dennis Weissmann via swift-users <swift-users@swift.org>:

Hi swift-users,

I have found another weird behavior (IMO) and wanted to ask for the right way to handle this:

Imagine I want to switch over a Swift string which contains a UTI to map that UTI to an enum.

(A playground is attached for you to easily reproduce, this is tested with Xcode 9.1's included toolchain, also happens in projects)

I would expect the following to work:

import MobileCoreServices

enum MimeType {

   case text
   case unknown

   public init(uti: String) {
       // Source: System-Declared Uniform Type Identifiers
       switch uti.lowercased() {
       case kUTTypeText as String:
           self = .text
       default:
           self = .unknown
       }
   }

}

The error I get here is

warning: UTITest.playground:8:26: warning: 'as' test is always true
       case kUTTypeText as String:
                        ^

error: UTITest.playground:8:14: error: expression pattern of type 'CFString' cannot match values of type 'String'
       case kUTTypeText as String:
            ^~~~~~~~~~~
            ^~~~~~~~~~~
The only way I found to resolve this is to also import Foundation (which makes sense but is not really obvious).

Alright, that gives me this:

import MobileCoreServices
import Foundation

enum MimeType {

   case text
   case unknown

   public init(uti: String) {
       // Source: System-Declared Uniform Type Identifiers
       switch uti.lowercased() {
       case kUTTypeText as String:
           self = .text
       default:
           self = .unknown
       }
   }

}

warning: UTITest.playground:8:26: warning: 'as' test is always true
       case kUTTypeText as String:
                        ^

error: UTITest.playground:8:14: error: 'CFString' is not implicitly convertible to 'String'; did you mean to use 'as' to explicitly convert?
       case kUTTypeText as String:
            ^
                        as String

Uhm, okay? So let's do that:

enum MimeType {

   case text
   case unknown

   public init(uti: String) {
       // Source: System-Declared Uniform Type Identifiers
       switch uti.lowercased() {
       case kUTTypeText as String as String:
           self = .text
       default:
           self = .unknown
       }
   }

}

As weird as it looks, it works ... My question is: Is this behavior intended?

Thanks!

- Dennis
<UTITest.playground>
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users