Using Sysctlbyname() from Swift

Using sysctlbyname() from Swift

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)
}

Use the sysctlbyname function within a Linux Swift Package

So one can add C, C++ or Objective-C targets to a Swift package so it's possible to import the needed system headers, and then create some wrapper functions, that makes what is needed, accessible to Swift, but this breaks Swift playgrounds app development compatibility, since that support Swift-only targets (a possible workaround is to put the C/C++ target in a separate swift package to use it as a dependecy conditionally just for linux, for more details see the relative swift package documentation).

So adding a C/C++ target could have solved the problem, BUT the issue is that in the Linux kernel version 5.5 and onwards the sysctl functions have been deprecated and even on the older kernels they weren't available on all the cpu architectures Linux supports, and so on a computer running a recent kernel or some particular non-x86 cpu architecture, such Swift package would not have been built successfully.

The current way to access the information that used to be provided by the sysctl functions is to read it directly from the file system inside the /proc/sys directory and it works on all supported cpu architectures, and it's were the sysctl command line utility gets that data.

So only on linux the code have to modified like this, to successfully gather that data on all platforms:


import Foundation

#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif

///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}

public extension SysctlFetch{

#if !os(Linux)
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{

var size: size_t = 0

let name = namePrefix + valueName

var res = sysctlbyname(name, nil, &size, nil, 0)

if res != 0 {
return nil
}

var ret = [CChar].init(repeating: 0, count: size + 1)

res = sysctlbyname(name, &ret, &size, nil, 0)

return res == 0 ? String(cString: ret) : nil
}

///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()

var size = MemoryLayout.size(ofValue: ret)

let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)

return res == 0 ? ret : nil
}
#else
///Gets a `String` from `/proc/sys`
static func getString(_ valueName: String) -> String?{

let path = "/proc/sys/" + (namePrefix + valueName).replacingOccurrences(of: ".", with: "/")

var contents = ""

do{
contents = try String(contentsOfFile: path)
}catch let err{
return nil
}

if contents.last == "\n"{
contents.removeLast()
}

return contents
}

///Gets an Integer value from `/proc/sys`
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
guard let str = getString(valueName) else { return nil }
return T(str)
}
#endif

///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}

return res == 1
}

}

So at the end i figured it out on my own, i hope this can be useful to anyone having to do the same thing.

sysctlbyname failing in swift 1.2

The solution is there in the comment in your code: Size is now Int rather than Uint in 1.2, so this compiles:

var deviceModelIdentifier: String {
var size : Int = 0
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)!
}

(you can also write var size : size_t = 0 if you prefer)

Error message hints at this when you wade through the unsafe pointer boiler plate:

note: expected an argument list of type '(UnsafePointer< Int8 >,
UnsafeMutablePointer< Void >, UnsafeMutablePointer<Int>,
UnsafeMutablePointer< Void >, Int)'

Get computer informations from Swift

You should only ask one question at a time but I'll try to summarize:

  1. To get the operating system version

    ProcessInfo.processInfo.operatingSystemVersionString

  2. To get the machine type will not be so easy. You can get only the Model Identifier AFAIK MacPro5,1. You would need to use the terminal command

    sysctl hw.model

  3. Processor I think you will need the terminal command as well.

    sysctl -n machdep.cpu.brand_string
    sysctl -n machdep.cpu.core_count

  4. To get the physical memory. I am not sure if you can get the free memory as well.

    ProcessInfo.processInfo.physicalMemory


To use the terminal command in Swift you can do something like:

extension DataProtocol {
var string: String? { String(bytes: self, encoding: .utf8) }
}


extension Process {
static func stringFromTerminal(command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.launchPath = "/bin/bash"
task.arguments = ["-c", "sysctl -n " + command]
task.launch()
return pipe.fileHandleForReading.availableData.string ?? ""
}
static let processor = stringFromTerminal(command: "machdep.cpu.brand_string")
static let cores = stringFromTerminal(command: "machdep.cpu.core_count")
static let threads = stringFromTerminal(command: "machdep.cpu.thread_count")
static let vendor = stringFromTerminal(command: "machdep.cpu.vendor")
static let family = stringFromTerminal(command: "machdep.cpu.family")
}

Usage:

Process.processor

Getting system uptime in iOS/Swift

As you ask for a pure-Swift solution, I converted the ObjC code from the answer you mentioned Getting iOS system uptime, that doesn't pause when asleep.

func uptime() -> time_t {
var boottime = timeval()
var mib: [Int32] = [CTL_KERN, KERN_BOOTTIME]
var size = strideof(timeval)

var now = time_t()
var uptime: time_t = -1

time(&now)
if (sysctl(&mib, 2, &boottime, &size, nil, 0) != -1 && boottime.tv_sec != 0) {
uptime = now - boottime.tv_sec
}
return uptime
}

// print(uptime())

To make it a bit prettier, we can use sysctlbyname instead of sysctl:

// var mib: [Int32] = [CTL_KERN, KERN_BOOTTIME]
sysctlbyname("kern.boottime", &boottime, &size, nil, 0)

Objective-C to Swift conversion for C function

It is not too difficult if one knows two things:

  • The C int type is a 32-bit integer, this is Int32 in Swift,
    not Int.
  • sizeof() from C is MemoryLayout<T>.stride in Swift.

Then we get:

var mib : [Int32] = [ CTL_HW, HW_MEMSIZE ]
var physicalMemorySize: Int64 = 0
var size = MemoryLayout<Int64>.stride
if sysctl(&mib, UInt32(mib.count), &physicalMemorySize, &size, nil, 0) == 0 {
print(physicalMemorySize)
} else {
print("sysctl failed")
}

Capture model identifier for Apple Watch from WatchOS

You never allocate a buffer to receive the machine string.
Change

var machine = CChar()

to

var machine = [CChar](repeating: 0, count: size) 

and you should be good to go!

How to determine the current iPhone/device model?

I made this "pure Swift" extension on UIDevice.

If you are looking for a more elegant solution you can use my µ-framework DeviceKit published on GitHub (also available via CocoaPods, Carthage and Swift Package Manager).

Here's the code:

import UIKit

public extension UIDevice {

static let modelName: String = {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}

func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity
#if os(iOS)
switch identifier {
case "iPod5,1": return "iPod touch (5th generation)"
case "iPod7,1": return "iPod touch (6th generation)"
case "iPod9,1": return "iPod touch (7th generation)"
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4"
case "iPhone4,1": return "iPhone 4s"
case "iPhone5,1", "iPhone5,2": return "iPhone 5"
case "iPhone5,3", "iPhone5,4": return "iPhone 5c"
case "iPhone6,1", "iPhone6,2": return "iPhone 5s"
case "iPhone7,2": return "iPhone 6"
case "iPhone7,1": return "iPhone 6 Plus"
case "iPhone8,1": return "iPhone 6s"
case "iPhone8,2": return "iPhone 6s Plus"
case "iPhone9,1", "iPhone9,3": return "iPhone 7"
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus"
case "iPhone10,1", "iPhone10,4": return "iPhone 8"
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
case "iPhone10,3", "iPhone10,6": return "iPhone X"
case "iPhone11,2": return "iPhone XS"
case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
case "iPhone11,8": return "iPhone XR"
case "iPhone12,1": return "iPhone 11"
case "iPhone12,3": return "iPhone 11 Pro"
case "iPhone12,5": return "iPhone 11 Pro Max"
case "iPhone13,1": return "iPhone 12 mini"
case "iPhone13,2": return "iPhone 12"
case "iPhone13,3": return "iPhone 12 Pro"
case "iPhone13,4": return "iPhone 12 Pro Max"
case "iPhone14,4": return "iPhone 13 mini"
case "iPhone14,5": return "iPhone 13"
case "iPhone14,2": return "iPhone 13 Pro"
case "iPhone14,3": return "iPhone 13 Pro Max"
case "iPhone14,7": return "iPhone 14"
case "iPhone14,8": return "iPhone 14 Plus"
case "iPhone15,2": return "iPhone 14 Pro"
case "iPhone15,3": return "iPhone 14 Pro Max"
case "iPhone8,4": return "iPhone SE"
case "iPhone12,8": return "iPhone SE (2nd generation)"
case "iPhone14,6": return "iPhone SE (3rd generation)"
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2"
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)"
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)"
case "iPad6,11", "iPad6,12": return "iPad (5th generation)"
case "iPad7,5", "iPad7,6": return "iPad (6th generation)"
case "iPad7,11", "iPad7,12": return "iPad (7th generation)"
case "iPad11,6", "iPad11,7": return "iPad (8th generation)"
case "iPad12,1", "iPad12,2": return "iPad (9th generation)"
case "iPad13,18", "iPad13,19": return "iPad (10th generation)"
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air"
case "iPad5,3", "iPad5,4": return "iPad Air 2"
case "iPad11,3", "iPad11,4": return "iPad Air (3rd generation)"
case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)"
case "iPad13,16", "iPad13,17": return "iPad Air (5th generation)"
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini"
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2"
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3"
case "iPad5,1", "iPad5,2": return "iPad mini 4"
case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)"
case "iPad14,1", "iPad14,2": return "iPad mini (6th generation)"
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)"
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return "iPad Pro (11-inch) (1st generation)"
case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)"
case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": return "iPad Pro (11-inch) (3rd generation)"
case "iPad14,3", "iPad14,4": return "iPad Pro (11-inch) (4th generation)"
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)"
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return "iPad Pro (12.9-inch) (3rd generation)"
case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)"
case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11":return "iPad Pro (12.9-inch) (5th generation)"
case "iPad14,5", "iPad14,6": return "iPad Pro (12.9-inch) (6th generation)"
case "AppleTV5,3": return "Apple TV"
case "AppleTV6,2": return "Apple TV 4K"
case "AudioAccessory1,1": return "HomePod"
case "AudioAccessory5,1": return "HomePod mini"
case "i386", "x86_64", "arm64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))"
default: return identifier
}
#elseif os(tvOS)
switch identifier {
case "AppleTV5,3": return "Apple TV 4"
case "AppleTV6,2": return "Apple TV 4K"
case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))"
default: return identifier
}
#endif
}

return mapToDevice(identifier: identifier)
}()

}

You call it like this:

let modelName = UIDevice.modelName

For real devices it returns e.g. "iPad Pro (12.9-inch) (5th generation)", for simulators it returns e.g. "Simulator iPad Pro (12.9-inch) (5th generation)"

Here's the model references:

  • https://theiphonewiki.com/wiki/Models
  • https://theiphonewiki.com/wiki/BORD


Related Topics



Leave a reply



Submit