How can I load GADAdSize constants at runtime in Objective-C? - ios

My company develops an advertising SDK that mediates other ad networks. At runtime, it loads constants from those SDKs using CFBundleGetDataPointerForName, as outlined in this StackOverflow post.
However, I'm not able to get that approach to work to load the GADAdSize constants that AdMob's banner SDK uses. Here's what my code looks like:
HZGADAdSize *hzlookupAdMobAdSizeConstant(NSString *const constantName) {
return CFBundleGetDataPointerForName(CFBundleGetMainBundle(), (__bridge CFStringRef)constantName);
}
HZGADAdSize *hzAdMobAdSizeFlexibleWidthPortrait(void) {
return hzlookupAdMobAdSizeConstant(#"kGADAdSizeBanner");
}
The above code works fine for loading similar constants, like Facebook's FBAdSize struct, but the returned pointer is NULL when trying to load any GADAdSize constant.
Here's how the constant is defined in the AdMob SDK:
/// Do not create a GADAdSize manually. Use one of the kGADAdSize constants. Treat GADAdSize as an
/// opaque type. Do not access any fields directly. To obtain a concrete CGSize, use the function
/// CGSizeFromGADAdSize().
typedef struct GADAdSize {
CGSize size;
NSUInteger flags;
} GADAdSize;
#pragma mark Standard Sizes
/// iPhone and iPod Touch ad size. Typically 320x50.
extern GADAdSize const kGADAdSizeBanner;
Things I've tried so far
Trying with all bundles in the app:
CFArrayRef array = CFBundleGetAllBundles();
for (int i = 0; i < CFArrayGetCount(array); i++) {
CFBundleRef bundle = (CFBundleRef)CFArrayGetValueAtIndex(array, i);
void *ptr = CFBundleGetDataPointerForName(bundle, (__bridge CFStringRef)#"kGADAdSizeBanner");
if (ptr == NULL) {
NSLog(#"pointer was NULL");
} else {
NSLog(#"pointer was present!!");
}
}
None of the bundles in the app have the constant, so I don't think its an issue of the pointers being in a different bundle.
Marking the constants as extern in my code
extern HZGADAdSize const kGADAdSizeSmartBannerPortrait;
The extern approach lets me reference the constants, but if AdMob isn't present when the app is compiled, I get compiler errors about the missing constants, so I don't think this option is feasible since many of our developers won't use AdMob.
Loading function pointers from the same class. Edit: I was mistaken; I can't load function pointers either. I also can't load string constants.
void *ptr = CFBundleGetFunctionPointerForName(CFBundleGetMainBundle(), (__bridge CFStringRef)#"CGSizeFromGADAdSize"); // returns NULL
To avoid XY problems: Our SDK loads constants at runtime so that if e.g. the AdMob SDK is present, we can pass the constants it defines to it. If the SDK isn't present (e.g. when the developer isn't using AdMob but is using other ad networks), we still need our SDK to compile without AdMob being there. If there's a way we can accomplish this without loading values at runtime, that's just as good a solution for me.
Version Information:
AdMob SDK 7.0.0
iPhone 6 Simulator
iOS 8.2
Xcode 6.2

Related

Finding frameworks being linked to a project in Xcode

I saw this question: How can I programatically get the list of frameworks and libraries included in an ios project? here, which tries to answer similar question. However, I've two questions on this
The answer in above link (or see code below) - does it provide all frameworks or "only" frameworks that are linked to the project.
for (NSBundle *framework in [NSBundle allFrameworks])
NSLog(#"%#",framework.bundlePath.lastPathComponent);
if I see a framework appearing in above code, how can I find its usage in my code. I see many frameworks being referred to in above code, but I'm not able to figure out where exactly are they used. As per my knowledge, few of them are not used - is there a proper way to find this out.
Update 1:
I made a simple new app with absolutely no code in it. Then, I executed the for loop above and found that, it also presented me with all the frameworks - which means, the code above simply prints all the frameworks and not the one that I am essentially be using in my app. But, does it mean that all frameworks that are printed are linked to the app?
The dynamic loader dyld(3) provides this information. This code will print all loaded frameworks and shared libraries as well:
#include <stdio.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
int main() {
uint32_t c = _dyld_image_count();
for(uint32_t i = 0; i < c; ++i) {
printf("%d: %s\n", i, _dyld_get_image_name(i));
}
return 0;
}
EDIT: allFrameworks lists only frameworks which are linked to your app, and which contain at least one Objective-C class (see https://developer.apple.com/documentation/foundation/nsbundle/1408056-allframeworks).
Searching for referrers is should be very difficult in general. If you are just looking for a single function, you can add a static implementation of the function and call the loaded variant from it. This technique is used for overwriting __cxa_throw for instance, but it should also work for other functions:
static cxa_throw_t sCxa_throw = 0;
extern "C" void __cxa_throw(void *inException, void *inPvtinfo, void (*inDestination)(void *)) {
if (sCxa_throw == NULL) {
sCxa_throw = (cxa_throw_t)dlsym(RTLD_NEXT, "__cxa_throw");
}
sCxa_throw(inException, inPvtinfo, inDestination);
}
The variable sCXA_throw contains the reference to the dynamic version of this function while the loader uses the static version. Inside of this function you can determine the callers with unwinding the stack with libunwind.

Access to iOS SDK constant via name (reflection)

Inside of iOS SDK, lots of constants defined by Apple can be found looking something like this:
extern const CFStringRef kSomeReallyNiceConstant
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_8_0);
If I check for presence of this constant standard way:
if (NULL == &kSomeReallyNiceConstant)
I am pretty much referencing it and in order for my code to compile properly, I need at least iOS SDK 8.0 or higher in this case.
When it comes to objects and methods, reflection approach works nicely with usage of NSClassFromString, respondsToSelector and performSelector.
Is there a chance to use some kind of reflection (access to string constant by name) in attempt to get it's value if it exists (or none if it doesn't)?
I know that I can use macros to check for iOS version and execute different code paths based on that information, but I don't want to use that approach.
I managed to do this with pointer:
#include <dlfcn.h>
// ...
int *pointer = dlsym(RTLD_SELF, "kSomeReallyNiceConstant");
if (pointer) {
NSLog(#"Thing exists!");
} else {
NSLog(#"Nope, doesn't exist!");
}
but I am not sure if this is something that would cause app rejection. Do you maybe know?
Regardless of this pointer approach, I'm curious to hear if there's any other way to achieve this?
Nothing better than suggested solution found on this topic.
#include <dlfcn.h>
// ...
int *pointer = dlsym(RTLD_SELF, "kSomeReallyNiceConstant");
if (pointer) {
NSLog(#"Thing exists!");
} else {
NSLog(#"Nope, doesn't exist!");
}

Memory management of static consts in Objective-C

I was watching the F8-2016 Building iOS Tooling at Facebook Scale video, when I've noticed an interesting code part at 7:01.
Facebook defined a static constant in Objective-C this way:
static __unsafe_unretained NSString * const kAuthorKey = #"AUTHOR";
Up to now, I declared my static constants the same way, but without the __unsafe_unretained. Without this modifier, the constant will be strong, but since it exists always during the application run, it doesn't matter if it is strong or __unsafe_unretained.
Am I missing something? Do Facebook has any reason to use __unsafe_unretained?

Determining if Facebook app is installed from Unity

We are using the Facebook SDK for Unity (v6.0) and I'd like to now whether there's a way that I can check if the Facebook app is installed on the device.
The reason is an existing bug in the Facebook SDK (see here: bug)
I want to identify this scenario (occurs only when the FB app is installed), and react accordingly.
"In order to use a native plugin you firstly need to write functions
in a C-based language to access whatever features you need and compile
them into a library. In Unity, you will also need to create a C#
script which calls functions in the native library."
from http://docs.unity3d.com/Manual/NativePlugins.html
So, basically you need to write your code in Objective-C and provide the communication between the Unity and the Native Code.
The code that you need to implement for checking Facebook APP is;
(void) checkFacebookApp
{
if ([[UIApplication sharedApplication] canOpenURL:[NSURLURLWithString:#"fb://"]])
{
return true;
}
}
However you need some communication between the Unity and Xcode project. So;
class SomeScript : MonoBehaviour {
#if UNITY_IPHONE || UNITY_XBOX360
// On iOS and Xbox 360 plugins are statically linked into
// the executable, so we have to use __Internal as the
// library name.
[DllImport ("__Internal")]
#else
// Other platforms load plugins dynamically, so pass the name
// of the plugin's dynamic library.
[DllImport ("PluginName")]
#endif
private static extern float checkFacebookApp ();
void Awake () {
// Calls the FooPluginFunction inside the plugin
// And prints 5 to the console
bool check = checkFacebookApp ();
}
}

Loading GLSL shader without using any Apple APIs

What is a good way to load a GLSL shader using C/C++ without using Objective-C or any Apple APIs?
I am currently using the following method, which is from the iPhone 3D Programming book, but it says that it is not recommended for production code:
Simple.vsh
const char* SimpleVertexShader = STRINGIFY
(
// Shader code...
);
RenderingEngine.cpp
#define STRINGIFY(A) #A
#include "Simple.vsh"
// ...
glShaderSource( shaderHandle, 1, &SimpleVertexShader, 0 );
If you want to load your shaders from files in your app bundle, you can get the file paths using the NSBundle object (in Objective-C), or using the CoreFoundation CFBundle object (in pure C).
Either way, you are using Apple-specific APIs. The only thing you're getting by using CFBundle instead of NSBundle is more boilerplate code.
If you don't want to use any Apple APIs, then your options are to embed your shaders as literal strings, or connect to a server on the Internet and download them (using the Unix socket API).
What you really need to do is define an interface by which your RenderingEngine gets the source code for its shaders, and implement that interface using the appropriate platform-specific API on each platform to which your port the RenderingEngine. The interface can be something super simple like this:
RenderingEngineShaderSourceInterface.h
#ifdef __cplusplus
extern "C" {
#endif
// You are responsible for freeing the C string that this function returns.
extern char const *RenderingEngine_shaderSourceForName(char const *name);
#ifdef __cplusplus
}
#endif
Then you create RenderingEngineShaderSource_Windows.cpp, RenderingEngineShaderSource_iOS.m, RenderingEngineShaderSource_Linux.cpp, etc. Each one implements RenderingEngine_shaderSourceForName using the appropriate API for that platform.
I use one of two methods. If it's a short shader, I may just put it code:
const char shader[] =
"uniform vec4 blah;\n" // Note, no semicolon here - it does the right thing
"main ()\n"
"{\n"
...rest of code
"}\n";
Or, if it's longer or going to be re-used in other places, I'll put it into a text file in the resources and read the text file at run time. You can get to it via [NSBundle pathForResource:ofType:].
Consider a C++ raw string literal; no STRINGIFY is needed since the newer features of C++ allow you to do similar things without macro.
I'd retype a good example but here is one.

Resources