Im trying to do the following in Swift:
CFArrayRef attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, 0);
if (CFArrayGetCount(attachmentsArray)) {
CFBooleanRef notSync;
CFDictionaryRef dict = CFArrayGetValueAtIndex(attachmentsArray, 0);
BOOL keyExists = CFDictionaryGetValueIfPresent(dict,
kCMSampleAttachmentKey_NotSync,
(const void **)¬Sync);
I have:
if CFArrayGetCount(attachmentsArray) != 0 {
let dict = CFArrayGetValueAtIndex(attachmentsArray, 0)
However, dict is a UnsafePointer instead of a dictionary like it should be. When I printed attachmentsArray I got
Optional({ DependsOnOthers = 0; })
I tried casting it to a CFDictionaryRef but it would fail in runtime.
How do I succeed in doing the above Obj-C code in Swift?
However, dict is a UnsafePointer instead of a dictionary like it should be.
No, not "like it should be". Look at the declaration:
func CFArrayGetValueAtIndex(theArray: CFArray!, _ idx: CFIndex)
-> UnsafePointer<Void>
It returns an UnsafePointer-to-void because that is what it is supposed to do. (In Objective-C it returns a const void *.)
Related
I have been trying to convert existing swift2.3 to swift3. I got Ambiguous use on intValue error at the following code.
jobPackageVersion.intJobPackageId = (JobPackageVersionDictionary["intJobPackageId"]! as AnyObject).intValue as NSNumber
Here is the full code
if let url = Bundle.main.url(forResource: "tblJobPackageVersion", withExtension: "csv") {
do {
let strData = try String(contentsOf: url)
let csv = CSwiftV(String: strData)
if csv.keyedRows != nil {
for dictionary in csv.keyedRows! { // [Dictionary<String, String>]
let JobPackageVersionDictionary = dictionary as NSDictionary // Cast to NSDictionary
let JobPackageVersionEntity = NSEntityDescription.entity(forEntityName: "JobPackageVersion", in: context)
let jobPackageVersion = JobPackageVersion(entity: JobPackageVersionEntity!, insertInto: context)
// Set object attributes
jobPackageVersion.intJobPackageId = (JobPackageVersionDictionary["intJobPackageId"]! as AnyObject).intValue as NSNumber
jobPackageVersion.intJobPackageVersionId = (JobPackageVersionDictionary["intJobPackageVersionId"]! as AnyObject).intValue as NSNumber
jobPackageVersion.intStatus = (JobPackageVersionDictionary["intStatus"]! as AnyObject).intValue as NSNumber
jobPackageVersion.intVersion = (JobPackageVersionDictionary["intVersion"]! as AnyObject).intValue as NSNumber
do { // Save object to database and clean up memory
try context.save()
context.refresh(jobPackageVersion, mergeChanges: false)
} catch let error as NSError { Logger.sharedInstance.logMessage("\(#function) JobPackageVersion Saving Error: \(error.userInfo)") }
} // for-loop
Logger.sharedInstance.logMessage("\(#function): Loaded \(csv.keyedRows!.count) tblJobPackageVersion records.")
} else { Logger.sharedInstance.logMessage("\(#function) CSV Parser Warning: no CSV data was parsed in tblJobPackageVersion.csv!") }
} catch { Logger.sharedInstance.logMessage("\(#function) Error reading contents of tblJobPackageVersion.csv.") }
} else { Logger.sharedInstance.logMessage("\(#function) Error locating URL for resource tblJobPackageVersion.csv") }
}
Any help would be appreciated.
Thanks.
You're trying to call intValue on an object of type AnyObject. As the error states, this is too ambiguous because both NSNumber and NSString have intValue properties. Xcode doesn't know which intValue to use, because both NSNumber and NSString fall under the AnyObject umbrella. Since Xcode is confused, you need to be more specific about what type your object is. Try something like this:
jobPackageVersion.intJobPackageId = (JobPackageVersionDictionary["intJobPackageId"]! as NSNumber).intValue
Note 1: You're probably going to get the same error with the other objects you call intValue on, but you can fix them accordingly.
Note 2: Be extremely careful about force unwrapping your objects using !. If the dictionary you're using ever returns nil your program will crash. Instead I would safely unwrap them using either an if let or guard statement depending on your use case. Something like this may work a little better:
guard let intJobPackageId = JobPackageVersionDictionary["intJobPackageId"] as? NSNumber,
let intJobPackageVersionId = JobPackageVersionDictionary["intJobPackageVersionId"] as? NSNumber,
let intStatus = JobPackageVersionDictionary["intStatus"] as? NSNumber,
let intVersion = JobPackageVersionDictionary["intVersion"] as? NSNumber
else {
print("one of the dictionary values is nil")
return
}
jobPackageVersion.intJobPackageId = intJobPackageId.intValue
jobPackageVersion.intJobPackageVersionId = intJobPackageVersionId.intValue
jobPackageVersion.intStatus = intStatus.intValue
jobPackageVersion.intVersion = intVersion.intValue
This may not be exactly what you want, but it should give you an idea on how to safely unwrap your objects so your app doesn't crash. You can play around with it and decide what's best for you.
I was pointed to this objc snippet from WWDC 14, but I work on a Swift project.
CMIOObjectPropertyAddress prop = {
kCMIOHardwarePropertyAllowScreenCaptureDevices,
kCMIOObjectPropertyScopeGlobal,
kCMIOObjectPropertyElementMaster
};
UInt32 allow = 1;
CMIOObjectSetPropertyData(kCMIOObjectSystemObject, &prop, 0, NULL, sizeof(allow), &allow);
I then tried rewriting to Swift:
var prop : CMIOObjectPropertyAddress {
kCMIOHardwarePropertyAllowScreenCaptureDevices
kCMIOObjectPropertyScopeGlobal
kCMIOObjectPropertyElementMaster
}
var allow:UInt32 = 1
CMIOObjectSetPropertyData(kCMIOObjectSystemObject, &prop, 0, nil, sizeof(UInt32), &allow)
But it doesn't even validate. I don't know how to translate the CMIOObjectPropertyAddress struct. Xcode says
/Users/mortenjust/Dropbox/hack/learning/screenrec/screenrec/deleteme.swift:32:61:
Cannot assign to a get-only property 'prop'
A C struct translates as a Swift struct. Use the implicit memberwise initializer:
var prop = CMIOObjectPropertyAddress(
mSelector: UInt32(kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: UInt32(kCMIOObjectPropertyScopeGlobal),
mElement: UInt32(kCMIOObjectPropertyElementMaster))
The cool part is when you type CMIOObjectPropertyAddress(, code completion gives you the rest.
You're right, just got it running right this second. Turns out I also had to correct for some of the types. Here's the complete translation:
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster))
var allow : UInt32 = 1
var dataSize : UInt32 = 4
var zero : UInt32 = 0
CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &prop, zero, nil, dataSize, &allow)
var session = AVCaptureSession()
session.sessionPreset = AVCaptureSessionPresetHigh
var devices = AVCaptureDevice.devices()
for device in AVCaptureDevice.devices() {
println(device)
}
I'm trying to capture a window list in a Mac OS X app using Swift. The CGWindowListCreateImageFromArray function requires a CFArray. I've tried several things and this is the closest I've got. Or is there a better way to convert the array?
import Cocoa
// Example swift array of CGWindowID's
var windowIDs = [CGWindowID]();
windowIDs.append(1);
windowIDs.append(2);
// Convert to CFArray using CFArrayCreate
let allocator = kCFAllocatorDefault
let numValues = windowIDs.count as CFIndex
let callbacks: UnsafePointer<CFArrayCallBacks> = nil
var values: UnsafeMutablePointer<UnsafePointer<Void>> = nil
/* how do I convert windowIDs to UnsafeMutablePointer<UnsafePointer<Void>> for the values? */
let windowIDsCFArray = CFArrayCreate(allocator, values, numValues, callbacks);
let capture = CGWindowListCreateImageFromArray(CGRectInfinite, windowIDsCFArray, CGWindowImageOption(kCGWindowListOptionOnScreenOnly));
You can initialize your UnsafeMutablePointer with your array so long as you set your CGWindowIDs to CFTypeRef:
var windows: [CFTypeRef] = [1, 2]
var windowsPointer = UnsafeMutablePointer<UnsafePointer<Void>>(windows)
var cfArray = CFArrayCreate(nil, windowsPointer, windows.count, nil)
Converted Ian's answer to Swift 4:
let windows = [CGWindowID(17), CGWindowID(50), CGWindowID(59)]
let pointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: windows.count)
for (index, window) in windows.enumerated() {
pointer[index] = UnsafeRawPointer(bitPattern: UInt(window))
}
let array: CFArray = CFArrayCreate(kCFAllocatorDefault, pointer, windows.count, nil)
let capture = CGImage(windowListFromArrayScreenBounds: CGRect.infinite, windowArray: array, imageOption: [])!
let image: NSImage = NSImage(cgImage: capture, size: NSSize.zero)
Swift.print(image)
Arrays in Swift are bridged to NSArray, given they contain objects, e.g., conform to [AnyObject] type. Since CGWindowID is a UInt32, you need to convert it to NS family, array's map() method is an elegant approach.
var windows: [CGWindowID] = [CGWindowID(1), CGWindowID(2)]
var array: CFArray = windows.map({NSNumber(unsignedInt: $0)}) as CFArray
This, however, doesn't reflect on the actual CGWindowListCreateImageFromArray problem. Here's the working solution for that:
let windows: [CGWindowID] = [CGWindowID(17), CGWindowID(50), CGWindowID(59)]
let pointer: UnsafeMutablePointer<UnsafePointer<Void>> = UnsafeMutablePointer<UnsafePointer<Void>>.alloc(windows.count)
for var i: Int = 0, n = windows.count; i < n; i++ {
pointer[i] = UnsafePointer<Void>(bitPattern: UInt(windows[i]))
}
let array: CFArray = CFArrayCreate(kCFAllocatorDefault, pointer, windows.count, nil)
let capture: CGImage = CGWindowListCreateImageFromArray(CGRectInfinite, array, CGWindowImageOption.Default)!
let image: NSImage = NSImage(CGImage: capture, size: NSZeroSize)
Swift.print(image) // <NSImage 0x7f83a3d16920 Size={1440, 900} Reps=("<NSCGImageSnapshotRep:0x7f83a3d2dea0 cgImage=<CGImage 0x7f83a3d16840>>")>
I'm not great at ObjC, please correct if wrong, but from what I understand by playing with the SonOfGrab example and particular piece of code below is that the final pointer structure contains window ids (UInt32) not inside the memory cell (memory property of UnsafePointer instance), but inside memory address (hashValue property).
const void *windowIDs[2];
windowIDs[0] = 10;
windowIDs[1] = 20;
It's interesting, since values aren't stored in the memory, but inside the address descriptors, with oldest architectures being 32-bit UInt32 values fit perfectly into address pointers. Perhaps back in the days when the memory was a limiting factor this made a lot of sense and was a great approach. Discovering this all night in Swift in 2016 made me suicidal.
What's worse it fails in Xcode 7.2 playground with certain window ids, probably because of the way it handles memory, but works in the actual app.
There is a global enum defined in Objective-C:
typedef enum {
UMSocialSnsTypeNone = 0,
UMSocialSnsTypeQzone = 10,
UMSocialSnsTypeSina = 11, //sina weibo
} UMSocialSnsType;
This code sets the sharetype of a platform:
snsPlatform.shareToType = UMSocialSnsTypeDouban;
In Swift, I want to get the sharetype of the platform:
var snstype = snsPlatform!.shareToType
println(snstype)
Result: UMSocialSnsType (has 1 child)
snstype.toRaw()
Error: UMSocialSnsType does not have a member named "toRaw"
From what I can tell, UMSocialSNSType was declared in Objective-C without using the NS_ENUM macro, so it wasn't imported as a Swift enum. That means that instead of being able to use .toRaw() or UMSocialSNSType.Douban you have to use the different enumeration values as constant structs. Unfortunately the type also doesn't have the appropriate operators (== or ~=) set up, so you have to compare the value property.
var snstype = snsPlatform!.shareToType
switch snstype.value {
case UMSocialSnsTypeDouban.value:
println("douban")
case UMSocialSnsTypeEmail.value:
println("email")
default:
println("other")
}
if snstype.value == UMSocialSnsTypeDouban.value {
println("douban")
}
The good news is that it looks like all the constants autocomplete in Xcode, so you should be able to do find the comparisons you need to do that way.
It looks like the Swift-version of the bridged typedef...enum must be something like:
struct UMSocialSnsType {
var value:Int
init(_ val:Int) {
value = val
}
}
let UMSocialSnsTypeNone = UMSocialSnsType(0)
let UMSocialSnsTypeQzone = UMSocialSnsType(10)
let UMSocialSnsTypeSina = UMSocialSnsType(11)
// etc
Whereas if it had been declared in Objective-C with the NS_ENUM macro, it would look like:
enum UMSocialSnsType: Int {
case UMSocialSnsTypeNone = 0
case UMSocialSnsTypeQzone = 10, UMSocialSnsTypeSina // etc.
}
This must be very simple, but I can't figure out how to do this: I have a C-function to monitor current memory usage:
natural_t report_memory(void) {
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&info,
&size);
if( kerr == KERN_SUCCESS ) {
return info.resident_size;
} else {
NSLog(#"Error with task_info(): %s", mach_error_string(kerr));
return 0;
}
}
Now, I would like to use it. How do I declare it in the .h?
I tried the (for me) obvious within the objective c methods:
natural_t report_memory(void);
Calling this somewhere in the code:
NSLog(#"Memory used: %u", rvC.report_memory());
The Compiler complains error: called object is not a function. Thus, I assume, the declaration is somehow wrong. I tried several options, but the best I could get was a runtime error...
How to fix this?
rvC.report_memory()
should be replaced with
report_memory()
since it is a C function.
If you want to use this function in other modules, you should also put in your header (.h) file this line
extern natural_t report_memory(void);