How to use UnsafeMutablePointer<OpaquePointer> in Swift? - swift

How does one use an UnsafeMutablePointer<OpaquePointer> in Swift with some Core Foundation framework? Why have an UnsafeMutablePointer<OpaquePointer>?
Given, general: some UnsafeMutablePointer<SomeType> where typealias SomeType = OpaquePointer
Specific Example API
// SOURCE: import ApplicationServices.PrintCore
typealias PMPrinter = OpaquePointer
func PMSessionGetCurrentPrinter(_ printSession: PMPrintSession, _ currentPrinter: UnsafeMutablePointer<PMPrinter>)
func PMPrinterGetPaperList(PMPrinter, UnsafeMutablePointer<Unmanaged<CFArray>?>)
Specific Example Use Case: get list of papers supported by a printer
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
var currentPrinterOptional: PMPrinter? = nil
PMSessionGetCurrentPrinter(printSession, &currentPrinterOptional!)
guard let currentPrinter = currentPrinterOptional else { return }
// Get the array of pre-defined PMPapers this printer supports.
// PMPrinterGetPaperList(PMPrinter, UnsafeMutablePointer<Unmanaged<CFArray>?>)
var paperListUnmanaged: Unmanaged<CFArray>?
PMPrinterGetPaperList(currentPrinter, &paperListUnmanaged)
guard let paperList = paperListUnmanaged?.takeUnretainedValue() as [AnyObject]? else { return }
Observed Errors
What compiles does not run. What seems like (maybe) reasonable syntax does not compile.
The above example gets the following (expected) Runtime "fatal error: unexpectedly found nil while unwrapping an Optional value".
Some select other attempts:
// Compile Error: Address of variable 'currentPrinter' taken before is is initialized
var currentPrinter: PMPrinter
PMSessionGetCurrentPrinter(printSession, &currentPrinter)
// Compile Error: Nil cannot initialze specified type 'PMPrinter' (aka 'OpaquePointer')
var currentPrinter: PMPrinter = nil
PMSessionGetCurrentPrinter(printSession, &currentPrinter)
// Compile Error: Variable 'currentPrinterPtr' used before being initialized
var currentPrinterPtr: UnsafeMutablePointer<PMPrinter>
PMSessionGetCurrentPrinter(printSession, currentPrinterPtr)
// Compile OK: actually compiles
// Runtime Error: unexpectedly found nil while unwrapping an Optional value
var currentPrinterOptional: PMPrinter? = nil
PMSessionGetCurrentPrinter(printSession, &currentPrinterOptional!)
Resources
Apple: Core Printing ⇗
Apple: Using Swift with Cocoa and Objective-C ⇗
While the docs have useful information, a workable implementation for UnsafeMutablePointer<PMPrinter> with typealias as UnsafeMutablePointer<OpaquePointer> has been elusive.

PMPrinter and PMPaper are defined in the PrintCore framework
as pointer to an "incomplete type"
typedef struct OpaquePMPrinter* PMPrinter;
typedef struct OpaquePMPaper* PMPaper;
Those are imported into Swift as OpaquePointer, and are a bit
cumbersome to use.
The second argument to PMSessionGetCurrentPrinter() is a pointer to
a non-optional PMPrinter variable, and in Swift it must be
initialized before being passed as an inout argument. One possible way
to initialize a null-pointer is to use unsafeBitCast.
The easiest way to get the PMPaper objects from the array seems to
be to use CFArrayGetValueAtIndex() instead of bridging it to a
Swift array. That returns a UnsafeRawPointer which can be converted
to an OpaquePointer.
This worked in my test:
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
var currentPrinter = unsafeBitCast(0, to: PMPrinter.self)
PMSessionGetCurrentPrinter(printSession, &currentPrinter);
var paperListUnmanaged: Unmanaged<CFArray>?
PMPrinterGetPaperList(currentPrinter, &paperListUnmanaged)
guard let paperList = paperListUnmanaged?.takeUnretainedValue() else {
fatalError()
}
for idx in 0..<CFArrayGetCount(paperList) {
let paper = PMPaper(CFArrayGetValueAtIndex(paperList, idx))!
var width = 0.0, height = 0.0
PMPaperGetWidth(paper, &width)
PMPaperGetHeight(paper, &height)
print(width, height)
}

Related

Initializer for conditional binding must have Optional type, even if it is a Optional type

I got an error I do not understand, as I thought I understood unwrapping a conditional var/let. But when I try to force unwrap it in the if I get the supplied error.
Error:
Initializer for conditional binding must have Optional type, not 'String'
Code:
let imNotSet: String?
print(type(of: imNotSet)) // Optional<String>
if let unwrappedVar = imNotSet! { // error on this line
print(unwrappedVar)
}
if let unwrappedVar = imNotSet! { // error on this line
print(unwrappedVar)
}
imNotSet! forcefully unwrapped imNotSet. So it is no longer an optional but rather a string.
To keep it an optional, remove the forced unwrapping.
if let unwrappedVar = imNotSet { // error fixed
print(unwrappedVar)
}
if let allows you to safely unwrap the optional, unlike the forced unwrapping that you were doing before.
As for Constant 'imNotSet' used before being initialized error, Either provide it a value like let imNotSet: String? = "Sample", if it truly is a constant, before you use it. Or make it a var if you need to reset it later like var imNotSet: String? = nil
the var to be used with if let it must be an optional and this
imNotSet!
isn't , so replace
if let unwrappedVar = imNotSet! {
with
guard let unwrappedVar = imNotSet else { return }

Optional value nil found after using AudioFileGetProperty() with kAudioFilePropertyDataFormat

I'm trying to use swift AudioToolbox API to playback an audio file. However, when I get the AudioStreamBasicDescription using AudioFileGetProperty() the app crashes when I try to unwrap the value, saying "Unexpectedly found nil while unwrapping an optional value". This makes no sense because:
1) The OSStatus after I call AudioFileGetProperty() is zero.
2) I can see that the value is "nil" at first in the console, but after calling AudioFileGetProperty() it changes to "Some" and suddenly it is populated with values.
Perhaps am I using pointers incorrectly? Am I using optionals incorrectly?
func playAudioFileWithToolbox(){
var aqData = AQPlayerState()
let url2 = bundle.path(forResource: "dave_speaking", ofType: "m4a")!
var filePathArray = Array(url2.utf8)
let audioFileUrl = CFURLCreateFromFileSystemRepresentation(nil, &filePathArray, filePathArray.count, false)
//Problem: how do we pass aqData.mAudioFile? Initially it is nil
//We are supposed to pass by reference
let status = AudioFileOpenURL(audioFileUrl!, permissions, 0, &(aqData.mAudioFile))
var dataFormatSize:UInt32 = UInt32(MemoryLayout<AudioStreamBasicDescription>.size)
//Here I populate aqData.mDataFormat
var propertyStatus = AudioFileGetProperty(aqData.mAudioFile!, kAudioFilePropertyDataFormat, &dataFormatSize, &(aqData.mDataFormat))
//Next line crashes saying that it is unwrapping an optional value
var audioStreamDescription = aqData.mDataFormat!
}
struct AQPlayerState{
var mDataFormat:AudioStreamBasicDescription?
var mQueue:AudioQueueRef?
var mBuffers:AudioQueueBufferRef?
var mAudioFile: AudioFileID?
var bufferByteSize:UInt32 = 0
var mCurrentPacket:Int64 = 0
var mNumPacketsToRead:UInt32 = 0
var mPacketDescs : UnsafeMutablePointer<AudioStreamPacketDescription>?
var mIsRunning : Bool = false
init(){
}
}
So I figured out I was using a combination of pointers and optional values incorrectly. Because mDataFormat was by itself an optional:
var mDataFormat:AudioStreamBasicDescription?
passing a reference to it didn't make much sense, i.e., &mDataFormat.
Therefore I changed this variable to make it non-optional:
var mDataFormat:AudioStreamBasicDescription = AudioStreamBasicDescription()
That way, there was memory allocated to it, as well as an address, and it now made sense to do something like this:
&aqData.mDataFormat
var propertyStatus = AudioFileGetProperty(aqData.mAudioFile!, kAudioFilePropertyDataFormat, &dataFormatSize, &aqData.mDataFormat)
Anyway I am still learning how to combine optionals with pointers so any additional feedback you can give me would help.

Using reduce() to build a dictionary in Swift

I'd like to build a dictionary using a functional programming style. My reduce() doesn't seem to work - I get a "fatal error: unexpectedly found nil while unwrapping an Optional value"
func loadMoveToCalendarsRules(calendarIndex: Int) -> [String]? {
// return something like ["phone call", "buzz", "ring"]
return NSUserDefaults.standardUserDefaults().objectForKey(generateMoveToCalendarsRules_NSUserDefaultsKey(calendarIndex)) as? [String]
}
// Add indeces to an array of any type
func addIndices<T>(toArray: [T]) -> [(index: Int, value: T)] {
return Array(zip(toArray.indices, toArray))
}
typealias CalendarRules = [EKCalendar : [String]]?
func buildCalendarRules(cals: [EKCalendar]) -> CalendarRules {
let sortedCals = cals.sort { $0.title.lowercaseString < $1.title.lowercaseString }
// build move to cal rules.
let indexedCalList = addIndices(sortedCals)
// go through the sorted calendars and build a dictionary that associates each calendar with a string array. (These are keywords that apply to the given calendar.)
let calendarRules = indexedCalList.reduce(nil as CalendarRules) {
accumulator, nextValue in
var retVal: [EKCalendar : [String]]? = accumulator
// if there are values found in NSUserDefaults for this calendar index then retrieve them.
if let rulesForCurrentCal = loadMoveToCalendarsRules(nextValue.index) {
retVal![nextValue.value] = rulesForCurrentCal // fatal error: unexpectedly found nil while unwrapping an Optional value
}
return retVal
}
print("------------ built calendar rules -------------")
print(Array(arrayLiteral: calendarRules?.keys))
print(Array(arrayLiteral: calendarRules?.values))
return calendarRules
}
Your retVal is optional, and starts as nil (the initial value you pass in), yet you are using retVal! to force-unwrap it. You could just use [:] (an empty dictionary) as the initial value, and then retVal wouldn't need to be optional at all.
You are starting with nil, and never instantiate a CalendarRules dictionary, so the attempt to performed a forced unwrapping of it with ! is going to fail. Instead, test to see if it's nil and if so, instantiate one.
Before I get to that, I'd first suggest defining calendar rules as a non-optional type. It makes things less confusing this way:
typealias CalendarRules = [EKCalendar : [String]]
Then, you could use nil-coalescing operator, ??, to instantiate the CalendarRules object when needed:
let calendarRules = indexedCalList.reduce(nil as CalendarRules?) { accumulator, nextValue in
if let rulesForCurrentCal = loadMoveToCalendarsRules(nextValue.index) {
var retVal = accumulator ?? CalendarRules()
retVal[nextValue.value] = rulesForCurrentCal
return retVal
}
return accumulator
}
It strikes me that there might be more efficient approaches, but this should address your "unexpectedly found nil" error.

How to check if a variable is nil

I have a variable
var a: [AnyObject? -> Void]
and I am adding data in to it by append method. Now I want to check if the variable is nil or not. I tried using [] but not working and also tried "", this also not working, can anyone tell what is the meaning of this variable and how to check if it is nil.
As far as I understand, var a is an Array of functions that take an optional Object of any type, and return void. So these functions's parameter IS optional, but the Array itself isn't : it cannot be nil, or it would be declared [AnyObject? -> Void]? , no?
EDIT : if, nevertheless, you declared this a as an optional (but WHY would you do that ?) - adding a ? - you check an optional existence with if let :
if let b = a {
// a not nil, do some stuff
} else {
// a is null
}
If you just want to check if the array is empty, use isEmpty method from Swift Array
Update: Xcode 7.3 Swift 2.2
If you want to check if a variable is nil you should use if let to unwrap if for you. There is no need to create a second var.
let str = "123"
var a = Int(str)
if let a = a {
print(a)
}
Or
if let a = Int(str) {
print(a)
}
In Swift, nil is not a pointer—it is the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.
So, You can check it with below code:
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
if convertedNumber != nil {
println("convertedNumber contains some integer value.")
}
// prints "convertedNumber contains some integer value."
Please refer this about nil for more information.
In Swift 3.0
if let imageURL = dictObj["list_image"] as? String {
print(imageURL)
}
You can use if let. if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value.
var a:Int=0
if let b=a{
println(a)
} else {
println("Value - nil")
}
But for Strings you can also use .isEmpty() If you have initialized it to "".
var str:String=""
if !str.isEmpty(){
println(str)
}
For me none of the above solutions worked when I was using an AVFoundation object.
I would get Type 'AVCaptureDeviceInput does not conform to protocol 'BooleanType' when I tried if (audioDeviceInput) and I would get Binary operator '!=' cannot be applied to operands of type 'AVCaptureDeviceInput' and 'nil'.
Solution in my situation
if (audioDeviceInput.isEqual(nil))
nil is a pointer like any other and can be referenced as such, which is why this works.

Swift CMutablePointers in factories e.g. NewMusicSequence

How do you use C level factory methods in Swift?
Let's try using a factory such as NewMusicSequence().
var status:OSStatus
var sequence:MusicSequence
status=NewMusicSequence(&sequence)
This errors out with "error: variable 'sequence' passed by reference before being initialized".
Set sequence to nil, and you get EXC_BAD_INSTRUCTION.
You can try being explicit like this:
var sp:CMutablePointer<MusicSequence>=nil
status=NewMusicSequence(sp)
But then you get a bad access exception when you set sp to nil. If you don't set sp, you get an "error: variable 'sp' used before being initialized"
I'd expect this to be the way to do it, but it's not:
import AudioToolbox
var sequence: MusicSequence?
var status:OSStatus = NewMusicSequence(&sequence)
error: cannot convert the expression's type 'OSStatus' to type 'inout MusicSequence?'
var status:OSStatus = NewMusicSequence(&sequence)
Here's the reference.
I think the issue is that you need an optional value. Optional values are always initialized to nil and only optional values can ever be nil.
var sequence: MusicSequence?
let status = NewMusicSequence(&sequence)
This works for me in an Xcode project. It seems to throw EXC_BAD_ACCESS in the playground for some reason.
import Foundation
import AudioToolbox
#objc class SwiftSequenceGenerator : NSObject
{
#objc func createMusicSequence() -> MusicSequence
{
var status : OSStatus = 0
var sequence : MusicSequence = MusicSequence()
status = NewMusicSequence(&sequence)
return sequence
}
}
let generator = SwiftSequenceGenerator()
let sequence = generator.createMusicSequence()
I was testing this in an Obj-C project, hence the #objc attributes. Both this and its Obj-C counterpart were able to generate a new MusicSequence.
It is a little tricky because MusicSequence is actually a struct. In Obj-C it is defined as a pointer OpaqueMusicSequence and Swift uses COpaquePointer. They are basically the same, using a void * to pass the MusicSequence struct to the factory method to be mutated.