27

I’m using the code from this Gist to determine which iOS device (e.g. iPhone5,1) my app is running on:

- (NSString *)platform
{
    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    char *machine = malloc(size);
    sysctlbyname("hw.machine", machine, &size, NULL, 0);
    NSString *platform = [NSString stringWithUTF8String:machine];
    free(machine);
    return platform;
}

The Swift documentation indicates that C data types are well-supported, but it doesn’t say anything about C functions. Is there a pure Swift way to retrieve the machine identifier, or will I have to bridge into Objective-C for this?

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
bdesham
  • 15,430
  • 13
  • 79
  • 123

2 Answers2

57

You can do the same in Swift (error checking omitted for brevity):

func platform() -> String {
    var size : Int = 0 // as Ben Stahl noticed in his answer
    sysctlbyname("hw.machine", nil, &size, nil, 0)
    var machine = [CChar](count: size, repeatedValue: 0)
    sysctlbyname("hw.machine", &machine, &size, nil, 0)
    return String.fromCString(machine)!
}

Update for Swift 3:

func platform() -> String {
    var size = 0
    sysctlbyname("hw.machine", nil, &size, nil, 0)
    var machine = [CChar](repeating: 0,  count: size)
    sysctlbyname("hw.machine", &machine, &size, nil, 0)
    return String(cString: machine)
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 2
    Well I’ll be darned. I guess I should have tried this first. Thanks! – bdesham Aug 24 '14 at 02:02
  • What are the error checks you omitted, that should be added in this function? Thanks. – Emil Adz Mar 08 '15 at 13:53
  • @EmilAdz: [`sysctlbyname()`](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/sysctlbyname.3.html) has a *return value* which is zero on success and -1 on failure. – Martin R Mar 08 '15 at 13:56
  • So what should I check test in case the method returned -1 in case of failure? – Emil Adz Mar 08 '15 at 14:00
  • 1
    @EmilAdz: I am not sure what you are asking for. If a system call fails then the global variable `errno` is set to an integer indicating the failure reason. It is up to you what to do in that case (return a default value, return nil, abort the program, ...) – In this particular case I do not think that `sysctlbyname()` will ever fail if you pass valid parameters. I just mentioned that for the sake of completeness. – Martin R Mar 08 '15 at 14:14
  • @MartinR, oh... so actually for this specific method there are no other additional checks I need to added for error checking. Thanks a lot for your answer. – Emil Adz Mar 08 '15 at 14:15
  • @EmilAdz: Also `String.fromCString(machine)` could fail (i.e. return nil) if the machine name is not a valid UTF-8 string. – Martin R Mar 10 '15 at 09:47
  • @MartinR, Thanks for the heads up. Do I have any way to test if a valid UTF-8 string is provided? – Emil Adz Mar 10 '15 at 12:21
  • This no longer works in Swift 2.1. Error: '&' used with non-inout argument of type 'UnsafeMutablePointer – Boon Nov 24 '15 at 21:39
  • @MartinR I got it to work, maybe you can review for correctness: https://gist.github.com/nanaimostudio/00d62540fec410bad153 – Boon Nov 24 '15 at 21:46
  • @Boon: The only necessary change is `UInt` -> `Int` in the first line, as already mentioned in a comment after that line (and also noticed in the other answer). – I have updated the answer now so that it directly compiles in all current Swift versions. – Martin R Nov 24 '15 at 21:47
15

As of Swift 1.2 (Xcode 6.3 beta-2), @MartinR's code above needs to be modified slightly. sysctlbyname needs a CChar array (i.e. C string) for the first parameter, and an Int (instead of Uint) for the "size" parameter.

func platformModelString() -> String? {
    if let key = "hw.machine".cString(using: String.Encoding.utf8) {
        var size: Int = 0
        sysctlbyname(key, nil, &size, nil, 0)
        var machine = [CChar](repeating: 0, count: Int(size))
        sysctlbyname(key, &machine, &size, nil, 0)
        return String(cString: machine)
    }
    return nil
}

EDIT: Updated 2017-01-04 for Swift 3 syntax

Ben Stahl
  • 1,139
  • 11
  • 11
  • You are right about the type of the size parameter. But passing a Swift string to an `UnsafePointer` parameter still works in Swift 1.2, the compiler will convert that automatically. – Martin R Mar 09 '15 at 06:09
  • I think you’ve got an extra `}` at the end of the function, right? – bdesham Mar 09 '15 at 13:34
  • @bdesham Yeah, sorry about extra } ... sloppy copy-paste job. Fixed. – Ben Stahl Mar 27 '15 at 20:16