I am reading this sample code (AVCam) about AVCaptureSession and etc. And I notice this following line (link to the code):
// In iOS 9 and later, the userInfo dictionary contains information
// on why the session was interrupted.
if ( &AVCaptureSessionInterruptionReasonKey ) {
...
}
The comment in the code make sense. But what doesn't make sense to me is why do we want to get the address of AVCaptureSessionInterruptionReasonKey. It is defined as the following (in AVCaptureSection.h):
AVF_EXPORT NSString *const AVCaptureSessionInterruptionReasonKey NS_AVAILABLE_IOS(9_0);
If the key is defined, how will its address be nil? If this key is not defined, the code will never be compiled, right? Could someone explain to me how this if statement work?
AVCaptureSessionInterruptionReasonKey was added in iOS 9.0. Such an if statement is needed only if your app also supports iOS 8 or earlier.
When the code is run on a device with iOS 9 or later, the value will be non-nil and the if statement will be true. On devices with iOS 8 or earlier, the value will be nil and the if statement will be false.
If your app only supports iOS 9 or later, there is no need for the if statement.
Read the SDK Compatibility Guide in the iOS docs for more details on this type of check.
Related
NOTE: Please do not do a knee-jerk close recommendation based on "more code required for a minimal reproducible example" especially if you don't understand the question. If you follow my logic, I think you will see that more code is not required.
I'm doing some platform specific Flutter code where I have a platform method "stopRec" (stop recording) which awaits a byte array from the native host.
On the Dart side, it looks like this:
Uint8List? recordedBytes;
recordedBytes = await platform.invokeMethod('stopRec');
As you can see it's expecting to get a byte array (Dart Uint8List) back.
I've written the Android code and it works -- it tests out fine, the recorded bytes come back through and playback correctly.
This is what the Android (Java) code looks like:
byte[] soundBytes = recorder.getRecordedBytes();
result.success(soundBytes);
I hope you understand why "more code" is not yet necessary in this question.
Continuing, though, on the IOS side, I'm getting the following error when calling the platform method:
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: type
'List<Object?>' is not a subtype of type 'Uint8List?' in type cast
The Dart line where the error occurs is:
recordedBytes = await platform.invokeMethod('stopRec');
So what is happening is that it's not getting a the Dart Uint8List it expects sent back from IOS.
The IOS code looks like this:
var dartCompatibleAudioBytes:[UInt8]?
var audioBytesAsDataFromRecorder: Data?
// ..... platform channel section
case "stopRec":
self?.stopRec()
result(self?.dartCompatibleAudioBytes!) // <---- wrong data type getting sent back here
break
// ..... platform channel section
private func stopRec() {
myRecorder.stopRecording()
audioBytesAsDataFromRecorder = myRecorder.getRecordedAudioFileBytesAsData()
dartCompatibleAudioBytes = [UInt8] (audioBytesAsDataFromRecorder!)
}
I have tested the same IOS implementation code as a stand-alone IOS app that is not connected to Flutter, so I know that at the end of the the stopRec() method, the dartCompatibleAudioBytes variable does contain the expected data which plays back properly.
I hope you can see why "more code" is still not necessary.
The Dart code works
The Android code Works
The Dart code works together with the Android Code
The IOS code works
The IOS code does NOT work together with the Dart code
Using what I've shown, can anyone see immediately why the expected data type is not making its way back through the method channel?
According to the documentation, you should be using FlutterStandardTypedData(bytes: Data) in swift in order for it to be deserialized as Uint8List in dart.
I have this code from a library I am using.
#ifdef __IPHONE_8_0
if (&SKStoreProductParameterAffiliateToken) {
if (self.affiliateToken) {
[appParameters setObject:self.affiliateToken forKey:SKStoreProductParameterAffiliateToken];
if (self.campaignToken) {
[appParameters setObject:self.campaignToken forKey:SKStoreProductParameterCampaignToken];
}
}
}
#endif
Xcode is saying that the first line will always evaluate to be true but what is this line doing exactly? I never saw a if with & and a constant in that way.
SKStoreProductParameterAffiliateToken is defined as
SK_EXTERN NSString * const SKStoreProductParameterAffiliateToken NS_AVAILABLE_IOS(8_0);
What is the developer trying to check, the address of a constant? Is he trying to check if the version of iOS has this constant defined and by doing that, he is trying to check the instruction inside the if should run? But he already has ifdef __IPHONE_8_0... (??!!)
I don't get it.
Anyway I am compiling for iOS 9.3, so I can delete the if and the ifdef, right?
It is a check to see if a weak-linked symbol is available. If the library/framework containing the symbol has been weakly linked and is not available its address will evaluate to NULL and the if condition will be false.
See Using Weakly Linked Methods, Functions, and Symbols in Apple's Using SDK-Based Development for full details.
#ifdef __IPHONE_8_0 checks if Xcode should compile code inside. Ohterwise older version of Xcode will show an error about unknown variable SKStoreProductParameterAffiliateToken.
But when using newer Xcode version (with iOS SDK 8+), we may still can set a lower minimum target for our project. In this case to avoid crash on devices with lower than iOS 8 version we should check first if variable, class, method or function exists.
In your case, we are checking if pointer to SKStoreProductParameterAffiliateToken is not NULL, which means app is currently running at least on iOS 8.
Unlike the author of this previous question, I'm having the opposite problem. I'm attempting to make use of a component written in C++ in my iPad application that uses setlocale() from the standard C library. When running via the simulator (either i386 or x86_64), I'm able to set a locale successfully and get an expected return value:
(lldb) p (const char *)setlocale(2, "ja_JP")
(const char *) $1 = 0x000000010e776bd0 "ja_JP"
However, when running on a device (on either iOS8 or iOS9, on both armv7 and arm64 devices), this call always seems to fail and return null.
(lldb) p (const char *)setlocale(2, "ja_JP")
(const char *) $0 = 0x00000000
Actually setting the region and language on the iOS device doens't seem to make a difference either.
Is this an example of something like iOS9 preventing the use of sysctl(), or am I missing something bigger here?
This was actually answered on the Apple Dev Forums recently, sharing it here as well:
The problem here is not setlocale per se, but rather in reading the
locale in the first place. Notably, newlocale also returns NULL when
you call it with similar parameters. This fails because no C-style
locales are available to you on iOS. On the simulator this works
because the locale code picks up the locale data from OS X (in
/usr/share/locale). I’m not sure if this directory is populated on
iOS (I suspect not) but that doesn’t matter because the sandbox won’t
let your process get to it.
I am developing an app using SDK 8.1, Apple LLVM 6.0 and Xcode 6.1.1. The deployment target is 6.0. I'm using NSOperationQueue and I want to use QoS whenever it's available.
The code I'm using is:
if ([self.operationQueue respondsToSelector:#selector(setQualityOfService:)]
&& (&NSOperationQualityOfServiceUserInitiated)) {
[self.operationQueue performSelector:#selector(setQualityOfService:) withObject: NSOperationQualityOfServiceUserInitiated];
} else {
//Other stuff not related to the scope of this question
}
The error I get is:
Use of undeclared identifier 'NSOperationQualityOfServiceUserInitiated'
I added the if (&NSOperationQualityOfServiceUserInitiated) part to check if this constant exists. This code worked with older versions of Xcode/Obj-C Compiler.
I am able to use selectors with performSelectorWithIdentifier but what about constants that do not have a defined value in the docs? The value of this constant is set by NSQualityOfServiceUserInitiated but there is no definition for this value that can be hardcoded.
How do I fix that?
There are several things wrong with the code.
NSOperationQualityOfServiceUserInitiated is a native type (NSInteger) so you can't use it the way that you are in either line.
The qualityOfService is a property of type NSQualityOfService. Your attempt to pass an argument to the qualityOfService method (getter method) makes no sense. If you are trying to set the quality of service, you need to call the setter but you can't use performSelector.
You want:
if ([self.operationQueue respondsToSelector:#selector(qualityOfService)]) {
self.operationQueue.qualityOfService = NSOperationQualityOfServiceUserInitiated;
} else {
//Other stuff not related to the scope of this question
}
This code will compile fine as long as your Base SDK is iOS 8.0 or later. The Deployment Target doesn't matter.
If you also want to build this code with Xcode 5 or earlier (a Base SDK of iOS 7 or earlier) then you need to wrap the code with the proper compiler directives to check for the Base SDK.
I have a problem about iswalpha() on iOS.
I am tuning my app in Xcode 4.5 and I tried to pass the Spanish character ú to iswalpha(). The xcode displays the int value of ú is 250.
When I tried to run the app on a real device, iswalpha() returns 0; but in the simulator (I run Xcode on a MacBook air with 10.8.2) it returns 1.
I guess the reason might be iOS has a different implementation of wide-character than does MacOS. What is the best way to resolve this?
Enhanced details:
UTF-16(unicode)encoding of Spanish character ú is 250 in int value. I think iswalpha()should return 1, as MACOS does, other than in iOS return a 0.
Dam new user could not post image here. so for UTF-16 encoding of ú please refer to :
http://www.fileformat.info/info/unicode/char/fa/index.htm
Well I can answer my own question now, as well as a development log in case I forgot this later:
It seems to be a fault of Apple's implementation of libc in iOS. The implementation of iswalpha() is incomplete considering letters in languages other than English. The specific letters(ú,á,ó,...) in different languages could not be recognized by iswalpha(), because they fall out of the 0x7F ASCII boundry, and for somehow it could not be recognized by iOS's locale processing functions, but obviously in different locale those should still be readable alphabet letters.
Some details about it:
iswalph() in iOS is tracked down to:
__DARWIN_CTYPE_static_inline int
__istype(__darwin_ct_rune_t _c, unsigned long _f)
{
#ifdef USE_ASCII
return !!(__maskrune(_c, _f));
#else /* USE_ASCII */
return (isascii(_c) ? !!(_DefaultRuneLocale.__runetype[_c] & _f)
: !!__maskrune(_c, _f));
#endif /* USE_ASCII */
}
and it is __maskrune(_c, _f)) that in the end returns 0.
It is understandable that Apple missed this point since, nobody will use iswalpha() in Objective-C. However it may still be useful to note this point for some porting projects. It was a widely used function so maybe important to many legacy projects that porting to iOS. Hope Apple could fix it in later release.
My workaround now to this problem is to have a wrapper function of iswalpha(), which handle these Latin letters by my own code. Now the app runs flawlessly in my iPhone!