Wednesday, September 26, 2012

Create Framework of an existing xcode project (Custom Framework)

I have my xcode project named as "SocketTest", and I want to use it in my another application.
For this I have to create it's framework, because if I directly drag and drop it will increase the application size. So I have been searching for a way to create a static library. I figured out how to do so but then had the issue of creating a static library that was only one file and worked with both iOS devices (arm6 and arm7 architectures  and the iOS Simulator (i386 architecture). I finally got that to work but then ran into another problem. I was using the MapKit library which is unavailable in the i386 version. I could not figure out a way to make all of these work together. Finally, I figured out how to create a framework instead of a library. 

Apple also approve apps with custom frameworks. 

Let's proceed further how to build our own custom framework that will be re-usable, can use any other framework, and will work with both iOS devices and the iOS Simulator.

Step 1: Create a New Bundle Target




A bundle is used here because it will allow us to store xib files, as well as source code and any other resources we need. My new bundle target will be name "iSocket".

Step 2: Configure Bundle Target





Here is a list of the changes that need to be made:
  1. Base SDK = Latest iOS
  2. Architectures = Standard
  3. Build Active Architecture = No
  4. Valid Architectures = $(ARCHS_STANDARD_32_BIT)
    • It is important to copy this string exactly as is
  5. Dead Code Stripping = No
    • This is because Apple does not want you to have a dynamic library. This enables that "static" part of the framework
  6. Link With Standard Libraries = No
    • Any libraries required by your framework will have to be imported into the project that uses your framework
    • ie- If your framework requires MapKit (like mine did) then the app that uses your framework will need to add MapKit to the imported frameworks
  7. Match-O Type = Relocatable Object File
  8. Wrapper Extension = framework
 Step 3: Complete Bundle Target Setup



  • Now that we have the source code complete, go back to the target bundle we created and navigate to the "Build Phases" tab.
  • Expand the "Compile Sources" section. Here we will add all *.m files that need to be compiled. Basically, any of the code that was written in the previous step (not including header files) should be added here. In my case, publicClass.m and privateClass.m are being added.
  • If your framework is using any other frameworks (such as MapKit) add it to "Link Binary with Libraries"
  • If you have any additional resources, add them to the "Copy Bundle Resources" section.
  • Add "Copy Headers" as a new Build Phase
  • Add header files to the "Copy Headers" section
    • Any header files that should be public should be added and placed under "Public"
    • Any header files that should be private should be added and placed under "Private"
*Note: I also created a header file named "exampleHeader.h". This header file simply imports all of the public header files in my framework. Now, when another developer uses this framework he/she can just use
#import <exampleBundle/exampleHeader.h>
instead of importing every header file. This may be overkill for this example framework but it will be much appreciated by the developer if you build a complex framework that has several header files. 
    Step 4: Fix <bundle name>-Prefix.pch

     This file should have a line "#import <Cocoa/Cocoa.h>" and that line must be removed

    Step 5: Create Aggregate Target


    I have named my aggregate target "exampleAggregate". The aggregrate is the final target and when it is built it will turn the code into a framework.

    Step 6: Configure Aggregate Target

    1. Go to the "Build Phases" tab of the newly created aggregate target.
    2. Add a new "Run Script" as a build phase.
    3. Set the script to be the following where FMK_NAME is the name of your new framework:
    # Sets the target folders and the final framework product.
    FMK_NAME=SocketTest
    FMK_VERSION=A

    # Install dir will be the final output to the framework.
    # The following line create it in the root folder of the current project.
    INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework

    # Working dir will be deleted after the framework creation.
    WRK_DIR=build
    DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
    SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework

    # Building both architectures.
    xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos
    xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator

    # Cleaning the oldest.
    if [ -d "${INSTALL_DIR}" ]
    then
    rm -rf "${INSTALL_DIR}"
    fi

    # Creates and renews the final product folder.
    mkdir -p "${INSTALL_DIR}"
    mkdir -p "${INSTALL_DIR}/Versions"
    mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}"
    mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources"
    mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers"

    # Creates the internal links.
    # It MUST uses relative path, otherwise will not work when the folder is copied/moved.
    ln -s "${FMK_VERSION}" "${INSTALL_DIR}/Versions/Current"
    ln -s "Versions/Current/Headers" "${INSTALL_DIR}/Headers"
    ln -s "Versions/Current/Resources" "${INSTALL_DIR}/Resources"
    ln -s "Versions/Current/${FMK_NAME}" "${INSTALL_DIR}/${FMK_NAME}"

    # Copies the headers and resources files to the final product folder.
    cp -R "${DEVICE_DIR}/Headers/" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/"
    cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/"

    # Removes the binary and header from the resources folder.
    rm -r "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/Headers" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/${FMK_NAME}"

    # Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
    lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}"

    rm -r "${WRK_DIR}"




    Step 7: Build the Aggregate Target

    We have completed all necessary steps and your framework is ready to build. Now build the aggregate target and bundle target, you will find a new folder under <project root directory>\Products and it will be given the name <bundle name>.framework. This is your new framework! It contains the header files that developers will need and the compiled source code.

    Your next step will be to create a project that uses this framework. When you do so you will want to import the framework. Do this by navigating to the "Build Settings" tab under your app target then click the "+" under "Link Binaries with Libraries" just as you would with any standard framework. The only difference is that your framework will not be in the list. You will have to click "Add Other..." then navigate to the <bundle name>.framework folder. Select that folder and click "Open". 


    Your new framework has been added and you or the other developer can begin developing! 

    Happy Coding :) 

    Thursday, June 28, 2012

    Handle NSLog before release

    Go to your Build settings and under the Debug configuration add a value to "Preprocessor Macros" value like:      DEBUG_MODE=1
    Make sure you only do this for the Debug configuration and not for Beta or Release versions. Then in a common header file you can do something like:


    
    
    #ifdef DEBUG_MODE
    #define DLog( s, ... ) NSLog( @"<%@:(%d)> %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
    #else
    #define DLog( s, ... )
    #endif
    
    


    Now instead of NSLog use DLog everywhere. When testing and debugging, you'll get debug messages. When you're ready to release a beta or final release, all those DLog lines automatically become empty and nothing gets emitted. This way there's no manual setting of variables or commenting of NSLogs required. Picking your build target takes care of it.

    Wednesday, June 20, 2012

    iPhone/iPad Technical Interview Question(FAQ) with answers(Part 3)


    Prev….(Part 2) 

    Q14. What are the ways to store data localy on device ? 
    A.

    We store data localy in device through:
    1. Plist.
    2. NSUserDefaults.
    3. SQLite.
    4. CoreData.

    Q15. What is the way to store value as default in application without using nsuserdefault for the application run firts time, because first time there will be no any data in nsuserdefault.
    A.  
    Through Plist.


    Q16.  What is the significant of passing (NSDictionary *)launchOptions in application didFinishLaunchingWithOptions ? What are the options in application didFinishLaunchingWithOptions ?
    A.

     Launching an application by tapping its icon, an application can be launched in order to respond to a specific type of event.

    For example, it could be launched in response to an incoming push notification, it could be asked to open a file, or it could be launched to handle some background event that it had requested. In all of these cases, the options dictionary passed to theapplication:didFinishLaunchingWithOptions: method provides information about the reason for the launch.
    In situations where the application is already running, the methods of the application delegate are called in response to key changes.
    There are 7 launch options key:
    UIApplicationLaunchOptionsURLKey;
    UIApplicationLaunchOptionsSourceApplicationKey;
    UIApplicationLaunchOptionsRemoteNotificationKey;
    UIApplicationLaunchOptionsAnnotationKey;
    UIApplicationLaunchOptionsLocalNotificationKey;
    UIApplicationLaunchOptionsLocationKey;
    UIApplicationLaunchOptionsNewsstandDownloadsKey;
    For example:
    When you actually receive a notification, there are 2 way it can be handel in our application:
    1.    When application is not running, and user click on view button on the notification. To interpret this notification we have to implement application:didFinishLaunchingWithOptions:  instead of  applicationDidFinishLaunching: like this:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
      NSDictionary* userInfo = [launchOptions valueForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"];
      NSDictionary *payLoad= [userInfo objectForKey:@"aps"];
      NSDictionary *alertMsg = [userInfo objectForKey:@"alert"];
      NSDictionary * badgeCount = [userInfo objectForKey:@"badge"];
    //....do something with this.
    }

    2.    And when application is already in running state, in this case application delegate receives a call to application:didReceiveRemoteNotification:. This method also receives userInfo like this:
    -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    {
    NSDictionary *payLoad= [userInfo objectForKey:@"aps"];
      NSDictionary *alertMsg = [userInfo objectForKey:@"alert"];
      NSDictionary * badgeCount = [userInfo objectForKey:@"badge"];
    //....do something with this.

    }

    Q17.  What are the design patterns in iOS ?
    A.

    Q18.  Difference between Cocoa and Cocoa touch ?

    A. 

    Q19.  What is KVC and KVO ?

    A. 
    Its a design pattern follow by iOS. Its a.......

    Q20.  What architecture iOS follows ?

    A. 


    Q21.  What are collection methods ?

    A. 
    For objects that manage a collection of objects (each called an element of that collection).
    For example: - (void)addElement:(elementType)anObj;
               - (void)removeElement:(elementType)anObj;
               - (NSArray *)elements;



    Q22.  How to handle NSLog before release   ?

    A. 


    All The Best :)

    WILL BE CONTINUE… :
    )