原文:https://insert.io/frameworkios8xcode6/ 來自Oded Regev
網上充滿了關于如何構建一個iOS Framework的教程。然而,當我們開始了著手開始做這件事情時候,仍然必須克服一些不小的挑戰,才能夠得到以我們想要的方式工作的SDK。
此外,在Xcode 6中,蘋果極大地改變開發人員創建和構建Frameworks的方式,所以你會發現在互聯網上很多的frameworks制作教程都是沒有及時更新。
在這篇文章中,我們會告訴你,如何一步一步在iOS8創建和構建一個Framwork,本教程中的Framework源代碼可以在Github獲取到。
我們將針對性指出下面這些重要的挑戰:
- 如何混合Swift和Objective-C代碼結合在同一個SDK中?
- 如何構建能被所有相關的架構(armv7,armv7s,arm64,i386)使用的framework。如果你只需要這一個解決方案,只需要添加一個新的構建階段(Build Phase)到項目中,并使用“run script“, 腳本ios-build-framework-script.sh可以在這篇文章的底部找到。
在我們的例子中,我們將使用一個管理器(Manager)啟用\禁用framework,CustomView類將包含(驚喜吧)一個自定義的UIView。在這個例子中,我們要告訴你如何把xib文件和PNG文件資源整合在Framework中。
讓我們開始第1步 #1
1)從頭開始創建一個項目
因為Xcode6中有一個內置的選項來創建一個動態的Framework項目。選擇這個選項,如果你需要從頭開始創建一個框架,項目的選擇:
“靜態庫(Static Library)”和“框架(Framework)”的區別是什么?
“靜態庫”主要是將代碼編譯成.a文件的樣式,例如InsertLib.a。可以通過導出的靜態庫與他人共享,靜態庫中包含一些公共類和方法,客戶端獲取到靜態庫后可以使用這些公共類和方法。
“Cocoa Touch Framework”實質上是一個包,其中包含一個“動態庫(dynamic library)”,若干.h文件和資源文件。 “動態庫”的概念(換個詞“動態鏈接(dynamic linking)”) 就是有共享代碼的一個副本在單個設備中(CoreLocation.Framework就是一個例子)由所有鏈接到它的應用程序共享。這種動態鏈接的方式將提高系統的性能,通過最小化framework的內存使用。
2)添加Manager類文件,下面是代碼:
InsertManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface InsertManager : NSObject
+(instancetype) sharedManager;
-(void) startManager;
-(void) stopManager;
-(void) showMessageInViewController:(UIViewController *)viewController;
-(BOOL) isManagerRunning;
@end
InsertManager.m
#import "InsertManager.h"
#import "CustomView.h"
@interface InsertManager()
@property (nonatomic) BOOL isEnabled;
@end
@implementation InsertManager
+ (instancetype) sharedManager {
static InsertManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[[self class] alloc] init];
});
return sharedManager;
}
- (void) startManager {
NSLog(@"Manager is running");
_isEnabled = YES;
}
- (void) stopManager {
NSLog(@"Manager stopped..");
_isEnabled = NO;
}
-(BOOL) isManagerRunning {
return _isEnabled;
}
-(void) showMessageInViewController:(UIViewController *)viewController {
if (_isEnabled) {
NSBundle* frameworkBundle = [NSBundle bundleForClass:[self class]];
CustomView *csView = [[frameworkBundle loadNibNamed:@"CustomView" owner:self options:nil] firstObject];
csView.frame = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
[viewController.view addSubview:csView];
}
}
@end
3)添加CustomView代碼:
CustomView.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface CustomView : UIView
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
@end
CustomView.m
#import "CustomView.h"
@implementation CustomView
- (IBAction)closeButtonClicked:(id)sender {
[self removeFromSuperview];
}
@end
CustomView.xib - 下載從Github上,看看它是如何配置的。
Newsroom.png - 我們用這個文件作為背景圖片來演示,如何在一個框架中使PNG一類的資源文件并且傳給一個應用程序。
4)當你在Xcode中創建一個新的“Cocoa Touch Framework” 項目,默認的.h文件將被自動命名生成的“項目名.h” 。請確保您的所有公開的.h文件添加到該文件中,公開.h文件中包含公開方法,客戶端可以通過framework調用這些公開方法,在我們的例子中添加以下代碼:
#import <UIKit/UIKit.h>
//!InsertSampleFramework項目的版本號。
FOUNDATION_EXPORT double InsertSampleFrameworkVersionNumber;
//!InsertSampleFramework項目版本字符串。
FOUNDATION_EXPORT const unsigned char InsertSampleFrameworkVersionString [];
//在這頭,你應該導入使用類似語句的框架的所有公共頭
#import <InsertSampleFramework/InsertManager.h>
5)在Xcode中單擊Target,并轉到“Build Phase”部分,在“Headers”中添加需要公開的.h文件到“Public”中
Build Phase:
6)現在只是建立了Framework,還沒有準備好調用framework的項目。我們只能夠使用該framework在一個項目應用中。我們將在名為“Tabster”蘋果范例項目使用這個framework。該項目的完整源代碼可以從iOS Developer Library 下載,搜索“Tabster”,點擊結果。在Tabster頁面,查找按鈕“Download Sample Code”,下載代碼,并在Xcode打開項目。
來看看如何能夠讓我們的Framework在這個項目中工作起來...
在項目中集成framework
1)打開Tabster項目并運行應用程序,看到它如我們期望一樣運行起來。
2)復制“InsertSampleFramework”項目的根文件夾到Tabster的根文件夾
3)現在拖動Framework項目到Tabster項目中作為一個依賴(請注意,您必須先關閉Framework項目的Xcode的窗口,因為xcodeproj只可以在一個Xcode窗口中打開)。
4)加入該Framework作為依賴在Build Phases中
5)加入該框架為“Link Binary with Libraries”。如果框架有Swift代碼,你還需要添加框架中的““General”標簽下的“Embedded Binaries”
6)點擊Run,看看它是是否能工作,這個操作是一個完整性檢查(注意,我們還沒有用代碼集成framework)。
7)現在讓我們在Tabster項目中使用我們的神奇的Framework。Tabster這是一個相當簡單的應用程序:打開Storyboard ,并添加一個label(Insert Framework Enable\Disable),UISegmentControl和一個UIButton到ThreeViewController
8)在Tabster的ThreeViewController.m中添加:
#import<InsertSampleFramework/InsertManager.h>
9)添加下面的IBActions到ThreeViewController.m:
#pragma mark - IBAction
- (IBAction)segmentValueChanged:(id)sender {
UISegmentedControl *sc = (UISegmentedControl *)sender;
NSInteger selectedSegment = sc.selectedSegmentIndex;
if (selectedSegment == 1) {
[[InsertManager sharedManager] startManager];
}
else if (selectedSegment == 0) {
[[InsertManager sharedManager] stopManager];
}
}
- (IBAction)showCustomView:(id)sender {
[[InsertManager sharedManager] showMessageInViewController:self];
}
10)運行應用程序,點擊標簽“Three”,點擊 On\Off segment control。點擊確認按鈕“Show Custom View”將顯示view,確保manager運行時才能使用。
分發我們的framework使其能夠融合到其他外部應用程序中
大多數公司和個人開發為iOS開發框架(framework),最終希望將自己的框架能夠分發給別人使用。你必須要做的最重要的一步就是,建立對所有可能的架構(armv7,armv7s,arm64,X86等)都支持的框架。一為架構的每個家庭(iPhone模擬器,舊設備(ARMv7的,armv7s),新設備(arm64) - 我們為了做到這一點,通過增加一個“Build Phase”運行一個腳本,腳本將build framework3次。
點擊框架目標,并添加一個“New Run Script Phase”:
這一行你應該復制和粘貼到build phase:
/${PROJECT_DIR}/OTRGuideManager/ios-build-framework-script.sh
一些開發人員更喜歡直接在此框中寫腳本,我更傾向于讓腳本在一個單獨的sh文件,這樣我可以在Git的跟蹤它,當需要在未來,他們跟蹤更改。
具體的腳本是在ios-build-framework-script.sh:
set -e
set +u
#避免遞歸調用這個腳本。
if [[ $SF_MASTER_SCRIPT_RUNNING ]]
then
exit 0
fi
set -u
export SF_MASTER_SCRIPT_RUNNING=1
#常量
SF_TARGET_NAME=${PROJECT_NAME}
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
#構建Target
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 [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
then
echo "Please choose iPhone simulator as the build target."
exit 1
fi
IPHONE_DEVICE_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos
#生成其他(非虛擬機)平臺
xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/arm64" SYMROOT="${SYMROOT}" ARCHS='arm64' VALID_ARCHS='arm64' $ACTION
xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/armv7" SYMROOT="${SYMROOT}" ARCHS='armv7 armv7s' VALID_ARCHS='armv7 armv7s' $ACTION
#復制framework結構的universal folder(先清空)
rm -rf "${UNIVERSAL_OUTPUTFOLDER}"
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework"
#把這些架構(architectures)攪碎融合到一起
lipo -create "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/arm64/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/armv7/${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}"
1)請確保選擇是iPhone模擬器,當你想建立的發布版本的framework - 腳本會檢測,并自動建立的其他平臺。
2)運行“ Build”后,你需要選擇Distribution-universal目錄下的Framework。
3)整合framework到Xcode項目中,使用framework和你已經配置好的設置。