Exceptions in my iOS Framework Show Private Code? - ios

I'm building an iOS framework where only a single header file is exposed, and the rest of the code is private. Within Xcode I have all objective-c exception breakpoints on, so normally when there's an exception I'm brought to where it occurred in the code.
During testing in a totally new project that is using this framework I created, every now and then when an exception is raised inside the framework, I'm brought to the otherwise private framework code, which is obviously not what I want.
I think this may be because the actual raw framework code/project exists in my environment and wouldn't occur for another person using my framework without access to the actual files, but unfortunately I don't have any way to currently test this theory. Does anyone know if this is something I need to be handling in order to truly keep the project files private that I intent to, or is this just a function of having the code exist locally?

I was able to get in another dev environment with a fresh project to drop in the framework and force an internal framework crash and it appears that if the otherwise private framework files exist on the same environment and a crash occurs when you have objective-c exception breakpoints enabled it will open the private framework file in question, BUT if you don't have those private framework files (which consumers of your framework wouldn't) you will simply be taken to the normal crash/stack trace view like below:

Related

Framework using library crash

I have 2 frameworks created by me that use (both of them) a library also created by me.
The first framework initialize the library and makes all its workflow. After finishing the first framework, the second one must start.
But when the second one is going to start, after initializing the library, the app using both frameworks crashes with a exc_bad_access error.
Apparently the library is created correctly, but if i comment the line of code to initialize the library in the second framework, the workflow continues (it crashes later because it has no library initialization).
Is there anything I'm doing wrong? Should I use two separate libraries instead?
EDIT:
Imagine the situation:
Framework A has this methods: start, stop. And while it works it delegate to the methods: infoFromA,frameworkAFinished.
Framework B has this methods: start, stop. And while it works it delegate to the methods: infoFromB,frameworkBFinished.
Both start methods initialize the static library mentioned (lets call it problematicLibrary).
Both frameworks present some views to make its functionality. So lets make an example of the app workflow.
At the app view viewWillAppear method, I start the Framework A just using:
[FrameworkA start]; , this will initialize the library and present a view. With this view (using my problematicLibrary) some info will be delegated to the infoFromA delegated method. And after all the info is delegated, it will delegate to frameworkAFinished.
When the FrameworkA has delegated to the frameworkAFinished I start the next Framework: [FrameworkB start]. As the other Framework, it will initialize the library and present a view. While debugging, all the initialization of the library is done (create the instances of the needed objects and a new instance of the library is created) and while its presenting the view it goes through the viewDidLoad method and then it throws an exc_bad_access error at the problematicLibrary initialization line (which has been done before and continued to present the view!!) without going into any other method from the view.
I have checked if the initialization is doing well, and all the variables were at null value before the initialization, and a new memory address is given to the library object.
This sounds strongly like a symbol conflict to me. I'm just surprised the linker didn't catch it, but I assume that's because you're using a static library in both your frameworks instead of simply yet another framework.
Generally speaking, I'd warn that "this is a bad idea™". What you're trying to introduce into your design is basically dependency management. Like a lot of blog articles and specifically this SO answer suggest, you should avoid packaging frameworks (and by extension libraries) into frameworks.
What most likely happens in your scenario is this (I admit I'm guessing a bit here): You linked the library into Framework A. Thus, the library becomes a fixed part of it. Its symbols are in it, even if you did not expose them to the app in any header files or the like. As long as you use only that, everything works smoothly. Then comes Framework B, of which the library is also a fixed part. Even though you can't see it from your app, the very same symbols are inside it. This, however, clashes with the symbols that were already loaded by Framework A, hence a crash (as said, this would usually be caught by the linker, but I guess you "tricked" it by building the frameworks beforehand and packaged the library in them). Maybe somebody else can explain this in more detail, but that quickly becomes besides the point as to how you would go for a solution. From how I see it, it just won't work this way.
So here's a suggestion for how you can solve your problem:
If you really, really need to split like this (two frameworks used in your app using the same dependency), I'd suggest removing the library from the frameworks (i.e. make them depend on it, but not package the actual .a file in them) and documenting that properly. Then add the library to your app project, just like you added the frameworks.
If you want to make this fancy and easily installable into other apps, I'd recommend setting up a private CocoaPods repository and turn your frameworks into private pods. You can then easily define the library (or rather "new Framework C") as a dependency for Framework A and Framework B. When you then pod install in your app, cocoapods figures out the dependency and adds Framework C automatically to your project. This scenario is exactly what cocoapods (or any dependency manager for that matter) was designed for. It automates and helps in the project setup, so that the final build (the app) doesn't have to figure out dynamically what it can and can't use. The end result is the same.
Trying to duplicate that "in code" quickly becomes messy. Frameworks trying to figure out things of the surrounding app/project that uses them (like "is my dependency so-and-so already linked? if not, can I load my own version of the library?") can lead to a lot of pain.
Okay, in response to your comment I'll try my hand at a more detailed How-To for the non-cocoapods setup. As a preface, though, let me say that that's kinda hard to do on top of my head, as I have no ready sample project to toy around with. Since this is one of those "set it up once and then forget aout it for a long time" I have to admit my recollection of these things is a bit fuzzy, so consider this as a sort of "rough direction". There might be things you need to configure differently than how I recall them. Other SO user are thus hereby invited to edit and correct my answer here, too. :)
First, I have to say I am not exactly sure whether you need to convert your static library into a framework or not for this, I think you don't so I'll go from here (I have never used a static library in that way).
That means you can leave the project that builds your library as is. On second thought, you probably have to do this to be able to link against the library without making it a part of the framework that uses it. I will still refer to this code as "library" in the below points, but assume that you're able to turn it into a dynamic framework as well.
The projects building Framework A and Framework B should be changed. I don't know how you have this set up (as one project with various targets, whether you have a "development application" as part of it to test the frameworks on themselves, etc.), but the important thing is that in the target that builds a framework, the library should be linked (i.e. in the "Link Binary With Libraries" build phase), but not copied (i.e. it must not be in the "Copy Bundle Ressources" build phase). It might be tricky to set up any development/test target you use to run, depending on how you did that so far. For example you might have to manually edit Library Search paths in your build settings to be able to properly compile the framework.
Lastly, you need to change your final app's project settings, obviously. The library that was originally part of Framework A and B now needs to be linked to from its project directly, and, obviously, it needs to be copied into the bundle as well. Note that any projects that include either of your frameworks (A or B or both) in the future must do so, otherwise they won't work, because these frameworks expect the library to be present (but no longer "bring it with them").
In spite of this long-ish text wall, it shouldn't be that complicated, I think, but you may still want to check out how cocoapods can support you with something like this, perhaps it's helpful in the future. The linked article expects basic knowledge on how to use a pod and write one, but the other guides on the site explain that pretty well. I am just saying this because in the end, when using cocoapods in a project to add multiple libraries/frameworks that introduce dependencies, it basically sets up your project in the way I described above. It uses some fancy shell scripts that ensure everything is copied to the correct folders and such, but overall speaking that's how it works: A pod's podspec tells cocoapods what additional pods to include in your app for it to work (a dependecy the pod expects to be there, but doesn't "bring in" by itself).
Check if they are both compiling for the same target.
Check if they have access to the same target membership.
Check build phases to see that they are both there.
I think because the first library is not 'well' referencing the second one.
Also i think that you can't import a framework inside another one.
To make things easier, you can merge the two frameworks on a single one.
Also you can add a callback (using protocols or closures) that informs for the end of the first workflow, and you use this callback to initialize the second framework. This way the initialization will not be made automatically.

Referencing App Object Across Multiple Build Targets iOS

I need to make several versions of an iOS application each with a different target and name. Most of the differences between the applications are in the applications resources and not the source code. I am trying to share as much source code across versions as possible.
My issue arises when I try to access the application object in a version which does not have the same name as the original. For instance, I have two versions of the application named UserApp and UserApp2 and the following code which references the data store:
var user = UserApp.User
When I try to run this code in the UserApp2 target, it throws an error saying UserApp is undefined.
My question is: How can I make this code load the proper application object depending on what target is currently running? I would like a solution that can be used across all targets without the need to change code.
EDIT: The UserApp object is an XCDataModel.
The UserApp class might not be added in all of your application targets. verify the target membership for the class.

Whirlyglobe private API complain during app submission

I have a simple app which uses whirlyglobe framework (2.2). Otherwise, it is pretty basic program with Apple frameworks.
When I tried to validate the app (and during the distribution, Xcode complains that my app is using private API; specifically it calls "rootElement", and "attributeForFont:". I suspect these functions are part of WhilyGlobe component distribution.
What is the easiest way to remove those files from the framework? Do I need to have the source and compile the framework myself? Or is there a simpler way?
Solved. Here is what I did:
1) Searched in github repository of WhirlyGlobe and found the file that uses the functions which caused issue.
2) I did not need the functionality provided by the file that had the functions.
3) Removed the references (class instantiation) to the file from my project.
That's actually part of the KissXML framework and the implementation is in there. I suspect Apple's test is in error.
In any case, if you're not using the WMS functionality, you can probably drop it out just fine.

Using Late Bound Assemblies with Xamarin for iOS

I'm trying to port one of my iOS apps from the Mono versions of the
ServiceStack.Text libraries to the PCL versions for JSON
serialization/deserialization.
I have the libraries working in a regular Windows console application. When trying to port this into the iOS application, I'm getting an "Object reference not set to an instance of an object" exception from on the ServiceStack extension method classes. The error message is a red herring; the true error is getting swollowed by a try/catch.
The static constructor in ServiceStack.Text.PCL.PclExport is doing late binding for a
platform specific DLL and can't find that DLL. The build log bears out that the
Client, Interface, and Pcl libraries aren't being packaged with the .app file even though the reference are included at the project level.
I've been able to reproduce this in a basic iOS Hello World application. If I
add a line of code that references a method in the Pcl DLL, the three missing
libraries are copied. However, I'm still getting the same "Object reference not
set to an instance of an object" error.
I suspect that this is caused by the fact the Xamarin compiler doesn't translate the DLLs into iOS binaries unless they are actually used, and since they're late bound, it doesn't know they're used and so ignores them.
I didn't think late binding was legal under the licensing on at actual iPhone? doesn't the link merge everything into one exe?
See the Xamarin Linker Docs try the -nolinkaway option.

Why KeyCodeNumber exception after signing Blackberry app?

We've signed a 4.7 Blackberry application with the 4.6 code-signing tool. But when starting the application a second time it throws a 'KeyCodeNumber exception'.
Anybody some clues to why we get this exception?
Thanks!
KeyCodeNumber exceptions have todo with the verification process.
Blackberry published an article describing how to prevent verification errors:
When developing Java applications for
the BlackBerry smartphone, you may
encounter any of the following
verification errors or errors similar
to the following:
Verification Error 3141 Module
'MyMIDlet' has verification error
(<###>) at offset <###>. Error
starting MyMIDlet: Module 'MyMIDlet'
has verification error (<####>) at
offset <###>." These errors often
occur when creating MIDlets. They are
inherently hard to debug because the
same error message can apply to a
number of problems.
The following is a list of possible solutions to prevent or correct verification errors:
If you started by building a Java
Archive (JAR) file and then used the
RIM Application Program Compiler
(RAPC) to create .cod files, make
sure you turn obfuscation off when
building the JAR file. The RAPC
compiler performs its own
obfuscation and issues may occur if
the code is already obfuscated.
Remove any System.out.* calls. These
generally do nothing on the
BlackBerry smartphone, but they
might cause verification errors.
Remove unused import statements.
Explicitly specify the access for
each function or variable. For
example, make sure each one is
specified as public, private, or
protected.
If you are working with a MIDlet,
make sure the MIDlet class is
declared as public.
Verification errors may occur if the
COD file is corrupted or if it was
not signed correctly. Make sure that
you perform a clean rebuild and that
you re-sign your application.
Reinstall the application on the
BlackBerry smartphone.
Comment out any non-executable code.
Verification errors might be related
to the size of the main code file
and the library files. If you
comment out non-executable code, the
file sizes change, which may correct
the problem.
If you have created any classes that
inherit from RIM classes, change the
name of any custom methods and
members that you created in those
classes. This makes sure that you
have not named any methods or
members of the same name in the
internal RIM classes.
If your application is using
BlackBerry® Device Software 3.8 or
later, verification errors occur
when an application that implements
the
javax.microedition.rms.RecordStore
class is compiled using BlackBerry®
Java® Development Environment
(BlackBerry JDE) earlier than
version 4.0. This occurs if the
application uses either the
addRecordListener or
removeRecordListener methods of the
RecordStore class. To resolve this
issue, recompile the application
using BlackBerry JDE 4.0 or later.
There is a problem with how the
BlackBerry® Java® Virtual Machine
(BlackBerry JVM) handles the
referencing of a class directly
within the constructor of another
class. The following is an example:
Class1 class1= new
Class1(Class2.class.getName());
To work around this issue, do not make the class call within a constructor as shown in the following example:
Class1 class1;
String className = Class2.class.getName();
Class1 = new Class1(className);
Remove references to a static instance variable from an inner class. For example, the following code example could cause an error:
public class MyOuterClass {
static int var;
class MyInnerClass {
public void doSomething() {
var = 7;
}
}
}
There are a few ways you can remove these references, such as creating get/set methods for var in the outer class or modifying the logic to pull MyInnerClass out of MyOuterClass.
The build procedure normally
compiles from the java source file
with the javac command, and then
runs preverify.exe file and then
RAPC. Add the following command line
arguments to javac to help avoid
issues in earlier versions of the
RAPC:
javac.exe -source 1.3 -target 1.1
Some methods that are very long can
cause verification errors. By
breaking these methods into helper
methods, you can reduce the
likelihood of verification errors.
Although not as likely, some very
long method definitions (with 10 or
more parameters), and some very long
constant definitions (long package
structure and/or long names) can
also cause verification errors.

Resources