IT_Programming/Objective-C · Swift · iOS

ios 라이브러리를 Framework 형태로 만들기 및 배포.

JJun ™ 2015. 6. 30. 12:24



 출처: http://hoiogi.tistory.com/24




1. Library를 Framework로 만들어 배포해야할 필요성.


회사에서 진행되고 있는 작업들중에 웹서버와 iOS App. 사이를 연결시켜주는 SDK(library)를 만들어 배포하는 작업이 있다.
보통은 static library로 만들어서 Header파일과 libXXX.a 파일을 배포하게 된다.
SDK 사용자는 Header와 libXXX.a
 파일을 프로젝트에 추가하여 사용하는데 파일관리가 번거로울 수 있다. 또한, 멋지지 않다. 

하지만, Framework로 만들어서 배포하게 되면 사용자입장에서는 .framework 하나만 추가하게되면 바로 개발이 가능해진다. 


아래에 설명된 내용으로 따라가기만 하면 우리도 멋진 framework를 만들어서 배포할 수 있다. 




2. Framework 뼈대 만들기


아래내용은 https://github.com/jverkoey/iOS-Framework 를 참고하여 기술하였다. 
자세한 내용은 원문을 참고.



Xcode 4.6을 기준으로 작성됨.


Step 1. "Cocoa Touch Static Library" Project 생성




여기에서 Product 이름은 .framework의 이름으로 만든다. 예를들어 "TestSDK" 라는 이름으로 만든다.




Step 2. 기본 Header 파일 생성

TestSDK.framework를 프로젝트에 추가한 개발자는 이전의 framework를 사용하는것 과 같은 경험을 제공받기를 원할 것이다. 
#import <TestSDK/TestSDK.h> 라는 기본 Header 파일만 import하게 되면 TestSDK.framework에 속한 클래스를 사용할 수 있도록 해야한다. 


1) 새롭게 추가할 클래스를 생성한다. 예를 들어 "Widget.{h,m}" 파일을 추가하도록 하겠다.

2) "TestSDK.m" 파일을 삭제한다.






3) Target 설정 -> Build Phases 에서 Copy Headers Build Phase 추가 및 Header file을 public으로 설정.




위 그림에서 "Add Build Phase" 를 누르면 "Add Copy Headers" 항목이 나온다.
Build Phases에 Copy Headers 항목이 추가 되었으면 framework에서 노출될 header 파일을 추가하고
Project에 존재하는 header파일을
 Public 으로 이동시킨다.



Step 3. Public Headers Location 값 변경하기.




static library 프로젝트를 만들게 되면 기본적으로 Build Setting Public Headers Folder Path가 /usr/local/include 로 설정되어 있다.
이 값을 "$(PROJECT_NAME)Headers" 로 변경하여 준다. 




Step 4. Disable Code Stripping

TestSDK Target -> Build Setting 에서 아래와 같이 define 값을 변경하여 준다.
변경해줘야 하는 이유는 원문을 참고.


- "Dead Code Stripping" => No (for all setting)

- "Strip Debug Symbols During Copy" => No (for all setting)

- "Strip Style" => Non-Global Symbols (for all setting)





Step 5. Build Script 추가. (prepare_framework.sh)

prepare_framework.sh


 

set -e


mkdir -p "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/Headers"


# Link the "Current" version to "${FRAMEWORK_VERSION}"

ln -sfh ${FRAMEWORK_VERSION} "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/Current"

ln -sfh Versions/Current/Headers "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Headers"

ln -sfh "Versions/Current/${PRODUCT_NAME}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"


# The -a ensures that the headers maintain the source modification date so that we don't constantly

# cause propagating rebuilds of files that import these headers.

cp -a "${BUILT_PRODUCTS_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/Headers"





위에서 추가한 script 는 framework가 되기위한 폴더구조를 만들어준다.

This will generate the following folder structure:

-- Note: "->" denotes a symbolic link --
Serenity.framework/
  Headers/ -> Versions/Current/Headers
  Serenity -> Versions/Current/Serenity
  Versions/
    A/
      Headers/
        Serenity.h
        Widget.h
    Current -> A





3. Framework Distribution Target 만들기


Step 1. Aggregate Target 생성



프로젝트 설정에서 Add Target을 한다. 위 그림과 같이 iOS -> Other -> Aggregate를 선택하고 만든다.




Product Name은 "Framework"로 한다. 




Step2. Dependent Target에 Static Library 추가.




위와 같이 Target Dependencies 에 "TestSDK" target을 추가한다. 




Step3. Build Framework Script 추가.

build_framework.sh

set -e

set +u

# Avoid recursively calling this script.

if [[ $SF_MASTER_SCRIPT_RUNNING ]]

then

exit 0

fi

set -u

export SF_MASTER_SCRIPT_RUNNING=1


SF_TARGET_NAME=${PROJECT_NAME}

SF_EXECUTABLE_PATH="lib${SF_TARGET_NAME}.a"

SF_WRAPPER_NAME="${SF_TARGET_NAME}.framework"


# The following conditionals come from

# https://github.com/kstenerud/iOS-Universal-Framework


if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]

then

SF_SDK_PLATFORM=${BASH_REMATCH[1]}

else

echo "Could not find platform name from SDK_NAME: $SDK_NAME"

exit 1

fi


if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]

then

SF_SDK_VERSION=${BASH_REMATCH[1]}

else

echo "Could not find sdk version from SDK_NAME: $SDK_NAME"

exit 1

fi


if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]

then

SF_OTHER_PLATFORM=iphonesimulator

else

SF_OTHER_PLATFORM=iphoneos

fi


if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$SF_SDK_PLATFORM$ ]]

then

SF_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${SF_OTHER_PLATFORM}"

else

echo "Could not find platform name from build products directory: $BUILT_PRODUCTS_DIR"

exit 1

fi


# Build the other platform.

xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk ${SF_OTHER_PLATFORM}${SF_SDK_VERSION} BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}" $ACTION


# Smash the two static libraries into one fat binary and store it in the .framework

lipo -create "${BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" -output "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"


# Copy the binary to the other architecture folder to have a complete framework in both.

cp -a "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}" 




build framework script는 디바이스와 시뮬레이터용 .framework 를 합쳐주는 역할을 한다. (Universal Framework)




4. Framework 배포하기

Target을 Framework로 하여 빌드 하게 되면 아래와 같이 생성되게 된다. 



framework를 배포할때에는 TestSDK.framework 만 서드파티 개발자에게 전달해 주면
기존의 framework를 import하여 사용하듯이
우리가 만든 library를 사용할 수 있다. 
(단, 배포할때에는 Release계열을 배포한다.
Univeral Framework이기 때문에 Release-iphoneos, Release-iphonesimulator 둘 중 하나의 TestSDK.framework만 배포하여도 된다.)




5. 마치며...


영어가 되는 사람은 원문을 참조하는게 자세한 설명과 함께 이해 할 수 있다. (github에서 샘플코드를 받아서 분석해 볼 수 있다.) 본 글은 본인처럼 삽질하지 않기를 바라며 작성하였다. 부디 많은 참고가 되길 바라며, 한국 개발자들도 멋지게 작성된 라이브러리를 배포할 수 있길 바란다. 다음에는 framework작성 방법을 알아보았으니 Apple style의 document를 작성할 수 있는 appledoc에 대해 알아보도록 하겠다.  끝.