TLをからの.plistファイルをロードするためにNSBundleを指定します。特定の場所をから.plist
を読み取ることができ(フレームワークの一部である)シングルトンを(作成する方法DRフレームワークのアーキテクチャ:シングルトンのinit中に
フレームワークのバンドルからではありません)。
解決策は、以下に掲載され、受け入れられた回答に基づいています。
セットアップ説明
私のiOSアプリケーションはすべての共通のコードが独自に行くUsefulKit.framework
を利用しています。
フレームワークは、(RAII)初期化中.plist
から(..例えばなどベースURL、APIキー、)いくつかの設定をロードするための責任ConfigurationManager
(シングルトン)を、持っているとアプリケーションを読んで興味を持って他のコンポーネントに+ (id)valueForKey:(NSString *)key;
APIを提供全般的な設定。
ConfigurationManager
は、EnvironmentConfiguration-Default.plist
の初期化中にロードすると予想されるデフォルトの名前(以下の質問#3を参照)を格納します。
マネージャは[NSBundle bundleForClass:[self class]]
から.plist
をロードし、それは管理者がUsefulKit.framework
の一部となっている前に、正常に動作するために使用しました。メインアプリの一部だったときは、同じバンドルにそれぞれ.plist
があり、名前で見つけることができました。以下のConfigurationManager.m
のコードを参照してください。それは一部UsefulKit.framework
とき
NSString * const kDefaultEnvironmentConfigurationFileName = @"EnvironmentConfiguration-Default";
@interface ConfigurationManager()
@property (nonatomic, strong) NSMutableDictionary *environmentInfo;
@end
@implementation ConfigurationManager
+ (instancetype)sharedInstance {
static ConfigurationManager *sharedEnvironment;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!sharedEnvironment) {
sharedEnvironment = [self new];
}
});
return sharedEnvironment;
}
- (instancetype)init {
self = [super init];
if (self) {
self.environmentInfo = [NSMutableDictionary new];
[self loadEnvironment];
}
return self;
}
- (void)loadEnvironment {
[self.environmentInfo removeAllObjects];
[self loadDefaultEnvironmentConfiguration];
}
- (void)loadDefaultEnvironmentConfiguration {
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
NSString *defaultPlistPath = [bundle pathForResource:kDefaultEnvironmentConfigurationFileName ofType:@"plist"];
assert(defaultPlistPath != nil); // <=== code crashes here
//
// processing the plist file here...
//
}
// ...
// some code omitted
// ...
@end
問題今
、アプローチは動作しません。 EnvironmentConfiguration-Default.plist
がフレームワークと一緒にバンドルされて出荷されている場合にのみ動作します。これは、フレームワークを利用するアプリケーションによって構成が異なるためです。アプリケーションにはそれぞれ.plist
が必要で、フレームワークのConfigurationManager
を使用して設定にアクセスします。
このコードは、フレームワークのXcodeプロジェクト内の単体テストターゲットでも機能しません。 (上記参照)-loadDefaultEnvironmentConfiguration
でコードがクラッシュ...
- (void)testConfigurationManagerInstantiation {
[ConfigurationManager sharedInstance];
}
:私は、テスト対象のバンドルにEnvironmentConfiguration-Default.plist
ファイルを入れて、このユニットテストを書きました。
私はこれを参照してください上記の方法でデバッグ:バンドルは間違いなく私の.plist
がで見つけることができるものではありません
- (void)loadDefaultEnvironmentConfiguration {
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
// Printing description of bundle:
// NSBundle </Users/admin/Library/Developer/Xcode/DerivedData/MyWorkspace-asazpgalibrpubbrimxpbrebqdww/Build/Products/Debug-iphonesimulator/UsefulKit.framework> (loaded)
NSString *defaultPlistPath = [[NSBundle bundleForClass:[self class]] pathForResource:kDefaultEnvironmentConfigurationFileName ofType:@"plist"];
// Printing description of defaultPlistPath:
// <nil>
を。だから、私は構造的に何か間違っていると疑い始めました。 Singletonパターンで構築されたConfigurationManager
として
質問
私はConstructor Injectionを経由して、バンドルを注入することはできません。実際、私はどんな種類の依存症注射も「うまく」演奏することはできません。私は何かが恋しいですか?たぶん
static
varクライアントのアプリはパスを割り当てますか?フレームワークは内部的に他のバンドルを検索できますか?
EnvironmentConfiguration-Default.plist
の名前はGoogleAnalytics(B/C、他の開発者がそれを知っているし、セットアップを行う、しかし、私は多くのサードパーティ製のフレームワークのためにこの種のものを見なければならない、私に匂いConfigurationManager
の内部、にハードコードされ、UrbanAirhip、Fabric)、ここでフレームワークは特定の場所(多くの場合、フレームワークバージョン間で異なる)で.plist
を見つけることを期待しています。したがって、開発者はフレームワーク統合の一環としてドキュメントを読み、環境を準備する必要があります。
アーキテクチャの変更に関するご意見は歓迎します。以下は、非常に@NSGodにより投稿の提案に基づいています
解決策は、それを感謝します!私はアプローチをある種の(静的な)依存性注入と呼んでいます。
ConfigurationManager.m:
static NSBundle * defaultConfigurationBundle = nil;
@implementation ConfigurationManager
+ (void)initialize {
if (self == [ConfigurationManager class]) {
/// Defaults to main bundle
[[ConfigurationManager class] setDefaultConfigurationBundle:[NSBundle mainBundle]];
}
}
+ (instancetype)sharedInstance {
static ConfigurationManager *sharedEnvironment;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!sharedEnvironment) {
sharedEnvironment = [self new];
}
});
return sharedEnvironment;
}
+ (void)setDefaultConfigurationBundle:(NSBundle *)bundle {
@synchronized(self) {
defaultConfigurationBundle = bundle;
}
}
// ...
@end
ConfigurationManager.h:呼び出し場所で
@interface ConfigurationManager : NSObject
// ...
/**
@brief Specify default NSBundle, other than [NSBundle mainBundle] (which is used, otherwise) where .plist configuration file is expected to be found during initialization.
@discussion For some purpose (e.g. unit-testing) there might be cases, where forcing other NSBundle usage is required. The value, assigned in this method might be [NSBundle bundleForClass:[self class]], to get the bundle for caller.
@attention This method must be called before any other method in this class for assignment to take effect, because default bundle setup happens during class instantiation.
@param An NSBundle to read Default .plist from.
*/
+ (void)setDefaultConfigurationBundle:(NSBundle *)bundle;
// ...
@end
:
@implementation ConfigurationManagerTests
- (void)setUp {
[super setUp];
/// Prepare test case with correct bundle
[ConfigurationManager setDefaultConfigurationBundle:[NSBundle bundleForClass:[self class]]];
}
- (void)testConfigurationManagerInstantiation {
// call sequence:
// 1. +initialize
// 2. +setDefaultConfigurationBundle
// 3. +sharedInstance
XCTAssertNoThrow([ConfigurationManager sharedInstance]);
}
// ...
@end
アプローチは、フレームワークを効率化できアプリケーションターゲットからの使用(mainBundle
には.plist
が存在します)、+setDefaultConfigurationBundle
はこれまでの単体テストにのみ必要です。あなたがアプリケーションと通信フレームワークを持つようにしたい場合は
'ConfigurationManager'クラスあなた' .framework'の名前は何:それを行うのに最適な場所は、呼び出される最初のいずれかの方法でアプリデリゲートの
+initialize
方法、になります住んでいる?それは 'UsefulKit.framework'ですか?もしそうなら、 'UsefulKit.framework'プロジェクトに' EnvironmentConfiguration-Default.plist'ファイルを追加し、それを 'UsefulKit.framework'ターゲットのコピーリソースビルド段階に追加しましたか?言い換えれば、 'EnvironmentConfiguration-Default.plist'ファイルが' UsefulKit.framework/Resources/EnvironmentConfiguration-Default.plist'に存在することを確認してください。 – NSGod'ConfigurationManager'の' NSBundle bundleForClass:[self class]];コードは 'ConfigurationManager'クラスがあるフレームワーク(' UsefulKit.framework'?)の 'NSBundle'を返します。なぜEnvironmentConfiguration-Default.plistファイルをテストターゲットバンドルに入れて、このユニットテストを書いたのか、私は混乱しています。 "これは、設定ファイルを必要とするフレームワークです... – NSGod
@NSGod 'UsefulKit.framework'はフレームワーク名です。 'ConfigurationManager'はフレームワークの一部です。要点は、私は 'EnvironmentConfiguration-Default.plist'をフレームワークの一部にしたくないということです。それはクライアントアプリケーションの一部でなければなりません。管理者は設定を「フィード」し、便利なAPIとともに使用する必要があります。私はこのようにする方法を知らない。 –