How to build an objective c framework with optional dependencies - xcode

I'm developing an iOS object serialization framework. The framework is capable of serializing objects of classes from various other frameworks (e.g. CLLocation from CoreLocation), so I've imported those frameworks in order to compile my code.
However, I don't want to require my clients to have to edit their XCode projects to "Link Binary With Libraries" for each of the frameworks that my framework serializes. Instead, I'd like the client to be able to exclude the frameworks he doesn't use (e.g. CoreLocation) from his project and still be able to build his project with my serialization framework.
I've configured the project for my framework such that CoreLocation is Optional, but the client still gets linker errors ("Undefined symbols for architecture i386:").
What am I missing? Are there alternative ways to achieve my desired behavior?

I'm still not sure why it wasn't working, but I found a workaround for the location errors, so I thought I'd share that.
The 2 errors were:
(null): "_CLLocationCoordinate2DMake", referenced from:
...
(null): "_OBJC_CLASS_$_CLLocation", referenced from:
...
(null): Symbol(s) not found for architecture i386
By selectively commenting out lines of code from my file, I found the 2 main causes were
Invocations of CLLocationCoordinate2DMake
Invocations of CLLocation class methods
To fix #1, I just built the struct manually.
To fix #2, I abstracted all CLLocation class methods to use NSClassFromString. For example:
[CLLocation class]
becomes
[NSClassFromString(#"CLLocation") class]
The fix for #2 seems hacky, but just wrapping the original code with a conditional did not seem to fix the issue. Interestingly, I can still reference the CLLocation type like so:
CLLocation *loc = ((CLLocation *)obj);
I just can't seem to invoke any class methods without first using NSClassFromString.
Any insight to why I'm seeing this behavior would be appreciated, but for now, I can move on.

Related

Xamarin.iOS linker and reflection issue

In my Xamarin.iOS project I need to reduce the assembly size (Apple's requirement for AppStore applications), and I need to turn on linker, setting the linker behavior either to "Link Framework SDKs only" or "Link All".
When I've selected "Link Framework SDKs only" in Linker behavior I get the compilation error upon building the project:
Can't resolve the reference 'System.Void System.Data.SqlClient.SqlCommandBuilder::DeriveParameters(System.Data.SqlClient.SqlCommand)',
referenced from the method 'System.Void DevExpress.Xpo.DB.MSSqlConnectionProvider::CommandBuilderDeriveParameters(System.Data.IDbCommand)'
in 'System.Data.SqlClient, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
To fix this issue all answers I have found recommendation to turn "Link All" option in the Linker behavior option.
When I've selected "Link All", the project compiles okay, but at the runtime I get system exception on the IoC Container code (type AAA does not implement the interface BBB), because I use reflection, and linker with "Link All" options affects the code with the reflection. And as far as I know this linker option (Link All) is not recommended for the projects where reflection is being used.
What options do I have at this point?
Linker configuration
I suspect your best option is to look at define a configuration file to tell the Linker what must be kept. There is a good set of documentation on Microsofts documentation site.
There are some other options but may not strictly apply in this case or you may wish to use a combination.
Preserve Attribute
You can provide extra definitions to the linker to ensure the type, methods and/or fields are not eliminated from your application. In your own code the preferred way is to use the [Preserve] custom attribute, as discussed in the Linking on iOS and Linking on Android guides.
Linking for each platform is certainly a possibility however I suspect that you want to preserve things in your shared project which is why I think the config file will be right for you.
This approach gives you the ability to define at varying levels (e.g. assembly or class level to keep everything or down to individual properties/methods, etc.).
Actually reference the bits you need kept
I dislike this option very much but some people find it quick and easy to either prove you can keep parts of the code or just accept it is a solution.
You essentially create a class of actual references to the methods/properties that you want to be kept.
public class LinkerPleaseInclude
{
public void Include(MyType arg)
{
arg.MethodIDontWantRemoved();
}
}
Note I have sourced some details from this site

zendesk Xamarin binding for IPS and Android

I went through the documentation for doing Xamarin binding for Zendesk SDK for IOS. The documentation takes us through the scenario where we create .a file and proceed with binding. Zendesk library is provided in the form of a framework and though the Xamarin documentation says that frameworks contain .a file, I still could not find .a file in Zendesk framework. Hence, I am kind of blocked here. Any pointers here?
Link to Zendesk Library : https://github.com/zendesk/zendesk_sdk_ios
Link to Xamarin documentation : https://learn.microsoft.com/en-us/xamarin/ios/platform/binding-objective-c/walkthrough?tabs=vsmac
I ran objective sharpie command. Though it says binding is done, it did not generate apidefinition.cs and structsAndEnums.cs file . Following is the output I see in terminal.
C02VX2ZCHTD6:~ naveen.ramachandrapp$ sharpie bind -output=Zen -namespace=zen -sdk iphoneos11.3 -framework /Users/naveen.ramachandrapp/Downloads/zendesk_sdk_ios-masterObjectiveC/ZendeskSDK/4.1/ZendeskProviderSDK.framework
Parsing 1 header files...
Binding...
[write] ApiDefinitions.cs
[write] StructsAndEnums.cs
Binding Analysis:
Automated binding is complete, but there are a few
APIs which have been flagged with [Verify]
attributes. While the entire binding should be
audited for best API design practices, look more
closely at APIs with the following Verify attribute
hints:
MethodToProperty (30 instances):
An Objective-C method was bound as a C# property
due to convention such as taking no parameters and
returning a value (non-void return). Often methods
like these should be bound as properties to
surface a nicer API, but sometimes false-positives
can occur and the binding should actually be a
method.
StronglyTypedNSArray (25 instances):
A native NSArray* was bound as NSObject[]. It
might be possible to more strongly type the array
in the binding based on expectations set through
API documentation (e.g. comments in the header
file) or by examining the array contents through
testing. For example, an NSArray* containing only
NSNumber* instances can be bound as NSNumber[]
instead of NSObject[].
ConstantsInterfaceAssociation (2 instances):
There's no foolproof way to determine with which
Objective-C interface an extern variable
declaration may be associated. Instances of these
are bound as [Field] properties in a partial
interface into a nearby concrete interface to
produce a more intuitive API, possibly eliminating
the 'Constants' interface altogether.
PlatformInvoke (3 instances):
In general P/Invoke bindings are not as correct or
complete as Objective-C bindings (at least
currently). You may need to fix up the library
name (it defaults to '__Internal') and return/
parameter types manually to conform to C calling
conventionsfor the target platform. You may find
you don't even want to expose the C API in your
binding, but if you do, you'll probably also want
to relocate the definition to a more appropriate
class and expose a stronger type-safe wrapper. For
P/Invoke guidance, see http://www.mono-project.com/
docs/advanced/pinvoke/.
Once you have verified a Verify attribute, you
should remove it from the binding source code. The
presence of Verify attributes intentionally cause
build failures.
For more information about the Verify attribute
hints above, consult the Objective Sharpie
documentation by running 'sharpie docs' or visiting
the following URL:
http://xmn.io/sharpie-docs
You have misread the documentation. It clearly lists 3 different scenarios:
Generally in the iOS ecosystem you can find libraries in 3 flavors:
As a precompiled static library file with .a extension together with
its header(s) (.h files). For example, Google’s Analytics Library
As a precompiled Framework. This is just a folder containing the static
library, headers and sometimes additional resources with .framework
extension. For example, Google’s AdMob Library.
As just source code files. For example, a library containing just .m and .h Objective C
files.
So you can create binding libraries using a .a file or a precompiled .framework (your case) or using .h and .m files. No need to look for .a files in the .framework file.
You can use objective sharpie to help create a ApiDefinition.cs file for your binding like so:
sharpie bind -framework ZendeskCoreSDK.framework -sdk iphoneos11.1
There is loads of documentation for Objective Sharpie here: https://learn.microsoft.com/en-us/xamarin/cross-platform/macios/binding/objective-sharpie/

Xamarin iOS binding, symbol not found in library

I needed a sketch/painting control for my Xamarin.iOS project, and while I couldn't seem to find one compatible with C# I did find a good component available written in Objective C. https://github.com/acerbetti/ACEDrawingView
I've done Xamarin bindings before, so I had hoped that the process would be fairly simple, but unfortunately I've hit a few roadblocks along the way.
I started off creating my static library and used an ant build script to make a FAT binary to cover devices and the simulator:
snippet of my ant script
AceDrawingViewSDK.a: libAceDrawingView-i386.a libAceDrawingView-armv7.a libAceDrawingView-armv7s.a libAceDrawingView-arm64.a xcrun -sdk iphoneos lipo -create -output $# $^
Next, I ran
sharpie bind --sdk=iphoneos10.1 *.h
on the header files to get my ApiDefinitions and Structs and Enum files.
I checked and removed a the Verify attributes. (They all looked fine.) But this is where some of my other issues started.
The type ACEDrawingLabelViewTransform' already contains a definition forTransform' (CS0102) (AceDrawingViewBinding).
For the sake of just trying to move on and get something working, I just commented out this reference.
I then got multiple problems similar to this:
The type or namespace name `IACEDrawingTool' could not be found. Are you missing an assembly reference? (CS0246) (AceDrawingViewBinding)
I figured it related to this:
// #interface ACEDrawingPenTool : UIBezierPath
[BaseType(typeof(UIBezierPath))]
interface ACEDrawingPenTool : IACEDrawingTool
and this:
// #protocol ACEDrawingTool
[Protocol, Model]
[BaseType(typeof(NSObject))]
interface ACEDrawingTool
I tried to fix this my making the interface name consistent (I tried both IACEDrawingTool and ACEDrawingTool.) Doing this got past this error and allowed me to compile
One of my enums came out as
[Native]
public enum ACEDrawingMode : nuint
{
Scale,
OriginalSize
}
I couldn't find how to handle [Native] in this case (so once again, for testing sake I removed it.) I tried using removing the nuint from the enum and using uint. Either approach seemed to fix the error.
So with those errors fixed I was able to generate the .dll from my binding project and add it to my main project.
Now, I am getting 2 more issues.
If I build and deploy to the simulator I am able to run my app up until the point that I try to create a new instance of the ACEDrawingView from the binding. I get:
Could not create an native instance of the type 'ACEDrawingView': the native class hasn't been loaded.
It is possible to ignore this condition by setting ObjCRuntime.Class.ThrowOnInitFailure to false.
If I try to build and deploy to my phone I get different errors in the build phase which prevents it from launching on the device at all:
MTOUCH: error MT5211: Native linking failed, undefined Objective-C class: ACEDrawingArrowTool. The symbol 'OBJC_CLASS$ACEDrawingArrowTool' could not be found in any of the libraries or frameworks linked with your application.
MTOUCH: error MT5211: Native linking failed, undefined Objective-C class: ACEDrawingDraggableTextTool. The symbol '_OBJC_CLASS$ACEDrawingDraggableTextTool' could not be found in any of the libraries or frameworks linked with your application.
MTOUCH: error MT5211: Native linking failed, undefined Objective-C class: ACEDrawingEllipseTool. The symbol '_OBJC_CLASS$_ACEDrawingEllipseTool' could not be found in any of the libraries or frameworks linked with your application.
...and so on.
I've tried going back, rereading and redoing steps and have tried to reuse some of the scripts and settings from my previous successful bindings with no luck.
Does anyone have suggestions for what might fix these issues?
The type or namespace name `IACEDrawingTool' could not be found.
Add a new interface, like this interface IACEDrawingTool{ }
public enum ACEDrawingMode : nuint
Change the nuint to ulong

Xcode 6 Testing Target Troubles

I've been going through many questions and answers here, and have ended up in a quandary.
Now that I'm happy with the state of my project code, I wanted to implement unit testing. So I added a testing target, can I get it to compile correctly? No.... I'm sure it's something simple, but I cannot work it out.
The project contains 2 3rd party private frameworks.
My test target bundle loader parameter is set to:
$(TEST_HOST)
My test target test host parameter is set to:
$(BUILT_PRODUCTS_DIR)/My App.app/Contents/MacOS/My App
When I try to compile, I get errors like this:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_MyArrayController", referenced from:
objc-class-ref in SMyAppLogicTests.o
I look this up, and find answers like this on:
XCTestCase: ld: symbol(s) not found for architecture x86_64
Basically saying I need to add my classes to my test target. This seems to kind of defeat the point building on top of the working app, but what the hey. So I add all .m files to the test target, and it compiles! Hooray, but, I have new problems:
Class MyClass is implemented in both /Users/georgebrown/Library/Developer/Xcode/DerivedData/My_App-akedavtbgqpujscilxqmhsvikcku/Build/Products/Debug/My App.app/Contents/MacOS/My App and /Users/georgebrown/Library/Developer/Xcode/DerivedData/My_App-akedavtbgqpujscilxqmhsvikcku/Build/Products/Debug/My App Tests.xctest/Contents/MacOS/My App Tests. One of the two will be used. Which one is undefined.
That makes sense, why do I have to put the m files in if they're already contained in my app. So I look it up, and get an answer that puts me back in the first position:
Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined
What am I missing? I've been stuck for a week just trying to implement testing in Xcode (Not to mention what Xcode server did to my server, I still have to figure that out), when I planned a 3-4 days for writing the tests foolishly thinking it would be a quick setup.
EDIT: I deleted my test target and started again. First thing that didn't work is that it couldn't find my 3rd part private frameworks. So I added $(PROJECT_DIR) to the frameworks search paths.
Next, I started getting the Undefined Symbols for architecture error again for my classes. I noticed my Bundle Loader was unset, so I set that to be $(TEST_HOST), now the tests run! Hooray.
Now I can't build for the Run Action, but build for Test works..... Progress I guess. I get the Undefined symbols for architecture x86_64: error again.
I've managed to fix that by editing the scheme, and deselecting "Run" for the test target on the build. I'm not sure I entirely understand how the scheme is supposed to work, however, it did the trick.
The XCTesting thingie works by injecting the test bundle into the app at runtime. When you add a new test target, Xcode takes care of setting up all that stuff for you: I'd suggest if you have a test target that is giving you grief, you create a new one in Xcode and transfer your test cases to that target.
Then you only have to follow three rules:
Add your class implementation files only to app targets, never to
test any test targets. This will prevent the "Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined" warning
Add your XCTestCase files only to test
targets.
'#import' the header files of each custom class that you use in a test case.

Framework errors: ".objc_class_name_XXX", referenced from

I have taken over a Cocoa project that includes some frameworks, and I can get no help from the previous developer. When building one of the frameworks, I get 8 errors like this:
".objc_class_name_XXX", referenced from
I have tried googling the problem, but I cant get my head around it. Any ideas?
I also get some warnings saying "missing required architecture ppc in file" for a linked framework, could this hint at the problem?
Any help will be greatly appreciated. Thank you
Søren
The missing .objc_class_name_XXX means you’re sending class messages to XXX, but not linking to the binary where XXX’s main implementation is. Presumably this is the framework that doesn’t have a PPC part.
There are three possible ways to fix this:
Get a version of the framework with a PowerPC part.
Stop building for PowerPC (the ARCHS build setting).
Stop using those classes in the PowerPC build of the project (either look them up dynamically using NSClassFromString or #ifdef them out).

Resources