原文:https://insert.io/frameworkios8xcode6/ 來自Oded Regev
網(wǎng)上充滿了關于如何構建一個iOS Framework的教程趾娃。然而摔桦,當我們開始了著手開始做這件事情時候,仍然必須克服一些不小的挑戰(zhàn)手负,才能夠得到以我們想要的方式工作的SDK。
此外臣镣,在Xcode 6中谣膳,蘋果極大地改變開發(fā)人員創(chuàng)建和構建Frameworks的方式,所以你會發(fā)現(xiàn)在互聯(lián)網(wǎng)上很多的frameworks制作教程都是沒有及時更新何吝。
在這篇文章中溉委,我們會告訴你,如何一步一步在iOS8創(chuàng)建和構建一個Framwork爱榕,本教程中的Framework源代碼可以在Github獲取到瓣喊。
我們將針對性指出下面這些重要的挑戰(zhàn):
- 如何混合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)從頭開始創(chuàng)建一個項目
因為Xcode6中有一個內(nèi)置的選項來創(chuàng)建一個動態(tài)的Framework項目淑履。選擇這個選項隶垮,如果你需要從頭開始創(chuàng)建一個框架,項目的選擇:
“靜態(tài)庫(Static Library)”和“框架(Framework)”的區(qū)別是什么秘噪?
“靜態(tài)庫”主要是將代碼編譯成.a文件的樣式狸吞,例如InsertLib.a≈讣澹可以通過導出的靜態(tài)庫與他人共享蹋偏,靜態(tài)庫中包含一些公共類和方法,客戶端獲取到靜態(tài)庫后可以使用這些公共類和方法至壤。
“Cocoa Touch Framework”實質(zhì)上是一個包威始,其中包含一個“動態(tài)庫(dynamic library)”,若干.h文件和資源文件。 “動態(tài)庫”的概念(換個詞“動態(tài)鏈接(dynamic linking)”) 就是有共享代碼的一個副本在單個設備中(CoreLocation.Framework就是一個例子)由所有鏈接到它的應用程序共享像街。這種動態(tài)鏈接的方式將提高系統(tǒng)的性能黎棠,通過最小化framework的內(nèi)存使用晋渺。
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中創(chuàng)建一個新的“Cocoa Touch Framework” 項目户魏,默認的.h文件將被自動命名生成的“項目名.h” 。請確保您的所有公開的.h文件添加到該文件中挪挤,公開.h文件中包含公開方法叼丑,客戶端可以通過framework調(diào)用這些公開方法,在我們的例子中添加以下代碼:
#import <UIKit/UIKit.h>
//扛门!InsertSampleFramework項目的版本號鸠信。
FOUNDATION_EXPORT double InsertSampleFrameworkVersionNumber;
//!InsertSampleFramework項目版本字符串论寨。
FOUNDATION_EXPORT const unsigned char InsertSampleFrameworkVersionString [];
//在這頭星立,你應該導入使用類似語句的框架的所有公共頭
#import <InsertSampleFramework/InsertManager.h>
5)在Xcode中單擊Target,并轉(zhuǎn)到“Build Phase”部分葬凳,在“Headers”中添加需要公開的.h文件到“Public”中
Build Phase:
6)現(xiàn)在只是建立了Framework绰垂,還沒有準備好調(diào)用framework的項目。我們只能夠使用該framework在一個項目應用中火焰。我們將在名為“Tabster”蘋果范例項目使用這個framework劲装。該項目的完整源代碼可以從iOS Developer Library 下載,搜索“Tabster”昌简,點擊結果占业。在Tabster頁面,查找按鈕“Download Sample Code”纯赎,下載代碼谦疾,并在Xcode打開項目。
來看看如何能夠讓我們的Framework在這個項目中工作起來...
在項目中集成framework
1)打開Tabster項目并運行應用程序犬金,看到它如我們期望一樣運行起來念恍。
2)復制“InsertSampleFramework”項目的根文件夾到Tabster的根文件夾
3)現(xiàn)在拖動Framework項目到Tabster項目中作為一個依賴(請注意,您必須先關閉Framework項目的Xcode的窗口晚顷,因為xcodeproj只可以在一個Xcode窗口中打開)峰伙。
4)加入該Framework作為依賴在Build Phases中
5)加入該框架為“Link Binary with Libraries”。如果框架有Swift代碼音同,你還需要添加框架中的““General”標簽下的“Embedded Binaries”
6)點擊Run词爬,看看它是是否能工作秃嗜,這個操作是一個完整性檢查(注意权均,我們還沒有用代碼集成framework)顿膨。
7)現(xiàn)在讓我們在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運行時才能使用塔橡。
分發(fā)我們的framework使其能夠融合到其他外部應用程序中
大多數(shù)公司和個人開發(fā)為iOS開發(fā)框架(framework)梅割,最終希望將自己的框架能夠分發(fā)給別人使用。你必須要做的最重要的一步就是葛家,建立對所有可能的架構(armv7户辞,armv7s,arm64癞谒,X86等)都支持的框架底燎。一為架構的每個家庭(iPhone模擬器,舊設備(ARMv7的弹砚,armv7s)双仍,新設備(arm64) - 我們?yōu)榱俗龅竭@一點,通過增加一個“Build Phase”運行一個腳本桌吃,腳本將build framework3次朱沃。
點擊框架目標,并添加一個“New Run Script Phase”:
這一行你應該復制和粘貼到build phase:
/${PROJECT_DIR}/OTRGuideManager/ios-build-framework-script.sh
一些開發(fā)人員更喜歡直接在此框中寫腳本茅诱,我更傾向于讓腳本在一個單獨的sh文件为流,這樣我可以在Git的跟蹤它,當需要在未來让簿,他們跟蹤更改敬察。
具體的腳本是在ios-build-framework-script.sh:
set -e
set +u
#避免遞歸調(diào)用這個腳本。
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模擬器尔当,當你想建立的發(fā)布版本的framework - 腳本會檢測莲祸,并自動建立的其他平臺。
2)運行“ Build”后椭迎,你需要選擇Distribution-universal目錄下的Framework锐帜。
3)整合framework到Xcode項目中,使用framework和你已經(jīng)配置好的設置畜号。