Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Swift equivalent of C#'s 'nameof()' function to get a variable or member's name at compile time?

Ok, there's an existing question here on S/O with the following title:

Swift: Get Variable Actual Name as String

By it's name, it seems that's exactly what I want. However, looking at the accepted answer (and the other non-accepted ones), they are referring to key path manipulation, which isn't what I'm after. (i.e. This is not a duplicate!)

In my case, I want the name of one variable to be stored in a second variable of type string.

In C#, this is trivial using nameof, like so...

int someVar = 3

string varName = nameof(someVar)
// 'varName' now holds the string value "someVar"

Note: nameof() executes at compile-time, not run-time so no reflection or anything else is needed. The compiler simply subs in the name of the variable as if someone manually typed its name as a string constant.

It's pretty handy when you, for instance, want to define a query object where your member names match the query parameters passed in a URL.

Here's a pseudo-code example (i.e. this clearly won't compile, but shows what I'm after.)

Also, please don't focus on the URL aspects of this. This is definitely *bad code*. In real code I'd use URLComponents, not string-append a URL like I'm doing here. Again, this is *only* illustrating my question, not building URLs.

struct queryObject{

    let userName  : String
    let highScore : Int

    var getUrl:String{
        return "www.ScoreTracker.com/postScore?\(nameof(userName))=\(userName)&\(nameof(highScore))=\(highScore)"
    }
}

Here's how you'd use it and what it would return:

let queryObject = QueryObject(userName:"Maverick", highScore:123456)

let urlString = queryObject.getUrl

The return value would be:

www.ScoreTracker.com/postScore?userName=Maverick&highScore=123456

The advantages of having access to a string representation of a variable, instead of using string constants are many:

  1. No hard-coded strings.
  2. You can use refactoring/renaming of symbols as usual and the generated strings track
  3. Allows you to keep your API and implementation in sync which aids in readability.

For instance, say the API changed the query param from userName to userId, all one would have to do is refactor the variable name and the output would update accordingly without having to manually touch any strings.

So, can this be done in Swift? Can you get the actual variable name and store it in a second variable?

like image 669
Mark A. Donohoe Avatar asked Dec 19 '17 16:12

Mark A. Donohoe


People also ask

Is Swift similar to C?

Swift's codes are easy to read and write. Swift algorithms are easy to read and write, similar to Java, JavaScript, and other C languages. Swift is a modern programming language with a relatively simplified and concise syntax.

Can you write C in Swift?

C function pointers are imported into Swift as closures with the C function pointer calling convention, denoted by the @convention(c) attribute. For example, a function pointer that has the type int (*)(void) in C is imported into Swift as @convention(c) () -> Int32 .

Is Objective-C same as Swift?

According to Apple, Swift is approximately 2.6 times faster than Objective C. The speed calculated generally relates to the speed of coding. Swift's syntax is much more simple and direct than Objective-C. This enables developers to focus on the core part of the code rather than the rules on which the codes are based.

Is Swift better than C?

As the performance of swift, it is 2.6x faster than Objective C and 8.4x faster than python. It got an encouraging syntax that make you write clean and consistent code. It provides improve readability and prevent errors.


2 Answers

Yes you can. I've used this as a solution for the next scenario: When I print an object/struct in the console, I like to see the vars names along with associated values:

extension CustomStringConvertible {
    var description: String {
        var description = "\n          *****  \(type(of: self)) *****\n"
        let mirrorS = Mirror(reflecting: self)
        for child in mirrorS.children {
            if let propertyName = child.label {
                description += "   \(getSpaces(forWord: propertyName))\(propertyName):  \(child.value)\n"
            }
        }

        return description
    }
}

And to use this, let's have as an example the following struct:

public struct FavoriteApp: Codable, CustomStringConvertible, Hashable {
    let appId: String
    let appName: String
    let bundleId: String
    let image: NSImage
    let isMacApp: Bool
    let rating: String
    let screenshotUrls: [String]
    let sellerName: String
    let sellerUrl: String
    let releaseDate: String
    let genres: String
    let latestVersion: String

}

let anApp:FavoriteApp = getTheAppInfoFromServer()
print(anApp) 

// This will print into console: 
    //        *****  FavoriteApp *****
    //        appId:          1438547269
    //        appName:        Dynamic wallpaper
    //        bundleId:        com.minglebit.DynamicWallpaper
    //        image:           -not visible- (but is there, trust me :))
    //        isMacApp         true
    //        rating:          0
    //
    //        screenshotUrls:  https://is1-ssl.mzstatic.com/image/thumb/Purple128/v4/5c/08/b8/5c08b824-12eb-e0b6-22fb-0e234f396b9e/pr_source.jpg/800x500bb.jpg
    //                        https://is1-ssl.mzstatic.com/image/thumb/Purple128/v4/f3/34/0e/f3340e21-714c-0a17-4437-73ba1828cbba/pr_source.jpg/800x500bb.jpg
    //        sellerName:      MingleBit SRL
    //        sellerUrl:       https://minglebit.com/products/dynamicwallpaper.php
    //        releaseDate:     2018-10-18T14:59:47Z
    //        genres:          Entertainment, Lifestyle
    //        latestVersion:   2.3

So, to concentrate the answer, use Mirror(reflecting: object) to get what you want.

like image 114
C-Viorel Avatar answered Oct 06 '22 15:10

C-Viorel


If you are only dealing with URLs, it is recommended to use URLComponents, which is better.

model:

struct QueryObject{

    var params = [String: String]()

    var getUrl: String{
        let result = "www.ScoreTracker.com/postScore"
        guard var url = URL(string: result) else { return result}
        url.appendQueryParameters(params)
        return url.absoluteString
    }

    init(params: [String: String]) {
        self.params = params
    }

}

then:

    let params = ["userName": "Maverick", "highScore": "123456"]
    let queryObject = QueryObject(params: params)
    let urlString = queryObject.getUrl
    DDLog(urlString) //www.ScoreTracker.com/postScore?userName=Maverick&highScore=123456

Github link

like image 40
Alexander Avatar answered Oct 06 '22 14:10

Alexander