Asked  7 Months ago    Answers:  5   Viewed   55 times

I have an existing iOS app and want to add a large chunk of code that I've been developing as another project just for ease of testing. The new chunk basically deals with saving an image to various sharing services, etc.. Because that sharing code needs a lot of testing and future updating, I was wondering what the best way to incorporate that code chunk into my existing app.

I don't know if it should be a static library, dynamic library or a framework, and honestly, I'm not really sure what the difference is, or how I should go about it and get it set up in Xcode.

All I know is that I need/want to keep a separate testing and updating app for the sharing code and have the main app use it.

 Answers

27

First, some general definitions (specific to iOS):

Static library - a unit of code linked at compile time, which does not change.

However, iOS static libraries are not allowed to contain images/assets (only code). You can get around this challenge by using a media bundle though.

A better, more formal definition can be found on Wikipedia here.

Dynamic library - a unit of code and/or assets linked at runtime that may change.

However, only Apple is allowed to create dynamic libraries for iOS . You're not allowed to create these, as this will get your app rejected. (See this other SO post for confirmation and reasoning on such).

Software Framework - a compiled set of code that accomplishes a task... hence, you can actually have a static framework or a dynamic framework, which are typically just the compiled versions of the above.

See the Wiki on Software Framework for more details.

Hence on iOS, your only option is basically to use a static library or static framework (the main difference being that a static framework is distributed as a compiled .a file most often, whereas a static library may simply be included as a subproject - you can see all of the code - which is compiled first and its resulting .a file used as a dependency by the project).

Now that we're clear(er) on these terms, setting up a static library and supporting media bundle for iOS isn't too difficult, and there are many tutorials on how to do such. I personally would recommend this one:

https://github.com/jverkoey/iOS-Framework

This is a pretty straight-forward guide and doesn't have the disadvantage of dealing with "fake static libraries"... check it out for more info...

Once you've created your static library, it's as easy as including it as a submodule within Git for use across different projects.

Good Luck.

EDIT

Regarding a subproject within a project, as far as I know, to get this to work/compile correctly, you essentially have to set up a compile chain where the subproject is compiled first, which creates a static framework .a file that is used as a dependency by the project.

Here's another useful tutorial which talks about this:

http://www.cocoanetics.com/2011/12/sub-projects-in-xcode/

EDIT 2

As of iOS 8, Apple now permits developers to create dynamic frameworks! (Note: your app must have a minimum target of iOS 8 to include a dynamic framework... back porting isn't allowed.)

This has been added as a new project template. In Xcode 6.1, this can be found at:

New Project -> iOS -> Framework & Library -> Cocoa Touch Framework
Tuesday, June 1, 2021
 
Trott
answered 7 Months ago
82

Here's what i did:
$(SRCROOT)/"subfolder"/"subfolder"/"framework"
This is what we're using before in previous Xcode versions. The reason why it wasn't working with Xcode5 is because; for some reason, whenever you add another framework to your project, it automatically adds a "" so you need to remove those to make it work. I don't know why it does that.

Monday, August 2, 2021
 
Zach
answered 4 Months ago
95

To convert the static/dynamic linked framework from static linked library,

  1. Add a new cocoa touch framework as a TARGET in your existing static linked library project.
  2. In the Build Phases, adding all the .m, .mm, .c, .cpp, .metal, etc. into "Build PhasesCompile Sources" phase of your static linked framework target.
  3. Put the headers that you want to exposed in to "Build PhasesHeaders".
  4. For dynamic linked framework, remember to check the Mach-O Type setting in your Build Settings. If you are going to use swift, you need to make sure the Mach-O type is set as dynamic library so that it will become a dynamic linked framework. For static linked framework, you'll need to set the Mach-O type as static library, but you cannot use swift in the converted static linked framework (only objective-c, objective-c++, C++, C, etc. are allowed).

Then for the app that wants to use this framework just need to include the headers as #import and add the framework into "Build PhasesLink Binary With Libraries" of your App Target. If the converted framework is dynamic linked framework, you will need to put it into "Embedded Binaries".

Monday, August 9, 2021
 
Heisenberg
answered 4 Months ago
27

Static libraries are just a bundle of .o files. They're not "linked" in any meaningful way; just concatenated together. It's not until you perform a real link step that symbols are resolved.

There is basically no difference between linking a .a with your executable and copying the equivalent source code into your executable's project. So there's no need to link with any additional frameworks or libraries until that time.


The following exercise may be educational:

Create the following comptest.c:

#include <stdio.h>

int main() {
   printf("Hello world.n");
   return 0;
}

See what the pre-processor does:

gcc -E comptest.c > comptest-cpp.c

This removes the #include and replaces it with the contents of the referenced file. This file is what the compiler actually sees.

Now see what the compiler does (I'm using the > syntax here and below so that things are parallel with -E):

gcc -S comptest.c > comptest.s

This is the generated assembly language after pre-processing and compilation. Now we turn that into a .o:

gcc -c comptest.c > comptest.o

Now let's see what's in that .o:

$ nm comptest.o
0000000000000040 s EH_frame0
000000000000002d s L_.str
0000000000000000 T _main
0000000000000058 S _main.eh
                 U _puts

The important things here are _main and _puts. _main is defined in this file at address 0. _puts is undefined. So something we link with had better provide it. Let's try linking without anything:

$ gcc -nodefaultlibs comptest.o
Undefined symbols for architecture x86_64:
  "_exit", referenced from:
      start in crt1.10.6.o
  "_puts", referenced from:
      _main in comptest.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status

(_exit is implicit from the C runtime; it's not directly referenced in the .o)

OK, so now we're ready to put it all together. We'll be explicit:

gcc -nodefaultlibs comptest.o /usr/lib/libc.dylib -o comptest

This says to link together comptest.o and the dynamic library libc. It promises that every symbol referenced will be provided by one of these files. It makes a note in the resulting binary that it should dynamically load symbols from /usr/lib/libc.dylib (this is a symlink to libSystem.B.dylib, which is itself an "umbrella framework" rather than a proper library, but that goes a little past what you need to know in most cases; you can pretend that puts() is in libSystem):

$ otool -L comptest
comptest:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)

If you link with a static library, it's identical to listing all the .o files included in it on the command-line.

Notice that at the link step, we just have .o and .dylib files (.a is just a package of .o). There are no .c files, no .h files, no .s files, no source code. Just object files that need symbols resolved. This is why header files don't matter here, but do matter when you're compiling.

Tuesday, August 10, 2021
 
Floris
answered 4 Months ago
10

You have to generate an expression tree, but a simple one, so it's not so hard...

void Main()
{
    var source = new[]
    {
        new SourceModelType { A = "hello", B = "world", C = "foo", D = "bar", E = "Baz" },
        new SourceModelType { A = "The", B = "answer", C = "is", D = "42", E = "!" }
    };

    var dest = ProjectionMap<SourceModelType, DestModelType>(source.AsQueryable());
    dest.Dump();
}

public static IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
    where TDest : new()
{
    var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
    var destProperties =   typeof(TDest).GetProperties().Where(p => p.CanWrite);
    var propertyMap = from d in destProperties
                      join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
                      select new { Source = s, Dest = d };
    var itemParam = Expression.Parameter(typeof(TSource), "item");
    var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source)));
    var newExpression = Expression.New(typeof(TDest));
    var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
    var projection = Expression.Lambda<Func<TSource, TDest>>(memberInitExpression, itemParam);
    projection.Dump();
    return sourceModel.Select(projection);
}

(tested in LinqPad, hence the Dumps)

The generated projection expression looks like that :

item => new DestModelType() {A = item.A, C = item.C, E = item.E}
Thursday, October 21, 2021
 
Sheakspear Zitouni
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share