-
Notifications
You must be signed in to change notification settings - Fork 336
9.1 クラス設計 1
Concepts in Objective-C Programming
iOS アプリケーション開発は MVC フレームワークを採用しています。従って、我々が作成するカスタムクラスも MVC にのっとって開発する必要があります。
-
Model
-
View を構成する情報を保持
-
必要に応じて、APIClient でリモートから情報を取得したり、DB にアクセスして情報を取得する
-
View
-
Model のデータを表示
-
ユーザから操作を受ける
-
Controller
-
Model と View を結びつけるもの
-
Model に更新メッセージを送り、Model からその完了通知を受ける
-
View に Model の情報を送り、View を通じてユーザからの操作を受ける
iOS の MVC フレームワークは上のような形をとります。View と Model を完全に切り離している理由は、View の再利用性を高めるためです。
各レイヤーで効率的に開発するためのポイントを解説します。
複雑で大量な情報の取得、管理する必要がある Model は、Controller がそのデータにアクセスしやすいように設計する必要があります。
Model は Controller から情報取得のメッセージを受けられるようにします。そのメッセージを受けて Model はローカルやリモートから情報の取得を行います。従って Model ではローカルの情報へアクセスする処理や、HTTP クライアントを用いてリモートから情報を取得する実装をします。
取得してきた情報の各エントリは Entity クラスに定義し、そのインスタンスを Model に管理させるとよいでしょう。例えば Model クラスオブジェクトが API クライアントを用いて以下のような JSON を取得したとします。
[
{
"id" : "1FZ3P4ACUWBBC-2010061010321",
"created_at" : "Thu Jun 10 01:32:13 +0000 2010",
"text" : "つぶやきの本文",
"user" : {
"id" : "1FZ3P4ACUWBB",
"screen_name" : "Becky",
"profile_image_url" : "http://profile.img.mixi.jp/photo/user/1FZ3P4ACUWBBC_301280930.jpg",
"url" : "http://mixi.jp/show_friend.pl?uid=1FZ3P4ACUWBB"
},
"reply_count" : "3",
"favorite_count" : "5",
"favorited" : true
},
・・・
]
Model オブジェクトはレスポンスで取得したこの JSON をそのまま Array で管理するのではなく、各エントリごとに entity オブジェクトを生成して管理するします。以下は entity オブジェクトの例です。
MixiVoiceEntity.h
#import <Foundation/Foundation.h>
@interface MixiUserEntity : NSObject
@property (nonatomic, strong) NSString *profileImageURL;
@property (nonatomic, strong) NSString *URL;
@property (nonatomic, strong) NSString *userID;
@property (nonatomic, strong) NSString *screenName;
+(MixiUserEntity *)userEntitiWithDict:(NSDictionary *)dict;
@end
@interface MixiVoiceEntity : NSObject
@property (nonatomic, strong) NSString *voiceID;
@property (nonatomic, strong) NSString *createdAt;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, assign) NSInteger replyCount;
@property (nonatomic, assign) NSInteger favoriteCount;
@property (nonatomic, assign) BOOL favorited;
@property (nonatomic, strong) MixiUserEntity *userEntity;
+(MixiVoiceEntity *)voiceEntitiWithDict:(NSDictionary *)dict;
@end
Model オブジェクトはこの voiceEntity を配列で管理するようにします。そうすることで Controller 側でも参照しやすくなり、可読性も上がります。
Controller クラスファイル作成時にその xib ファイルも生成しましょう。Interface Builder を使うことで、Controller に view オブジェクト生成やレイアウトのコードを記述しなくてもよくなり可読性があがります。
また、3.* 系の研修で行った UIView またはそのサブクラスを作成し、独自の view component 作成すれば、あらゆる場面でその view の再利用が簡単に行えます。
Controller にはあらゆる処理が集中しがちです。コードが肥大化してくる多くは *ViewController.m です。
Controller で if ~~~ else ~~~ という多くのロジックが入る可能性があります。例えば以下のような仕様があったとします。
- ファーストビューので、曜日ごとに違う画像をオーバーレイしたい
- その画像をタップした際の画面遷移やアクションが画像によってことなる
ここで考えられることは、
- 今何曜日なのかを判定し、画像を生成して Controller が表示する
- 各曜日ごとのイベントハンドラを実装し、画像のイベントを Controller が受け取る
これを全て Controller で実装すると確実に肥大化していきます。このような時はビジネスロジックを別クラスに移しましょう。
NSDate クラスのカテゴリとして作成すれば、任意の NSDate オブジェクトに対して曜日を求めることが出来ます。
NSDate+Day.h
typedef NS_ENUM(NSInteger, DayOfWeekType){
DayOfWeekSunday = 1,
DayOfWeekMonday,
DayOfWeekTuseday,
DayOfWeekWednesday,
DayOfWeekThursday,
DayOfWeekFriday,
DayOfWeekSaturday
};
@interface NSDate (Day)
-(DayOfWeekType)dayOfWeekWithDate;
@end
NSDate+Day.m
@implementation NSDate (Day)
-(DayOfWeekType)dayOfWeekWithDate
{
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents = [gregorianCalendar components:(NSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:self];
NSInteger day = [weekdayComponents weekday];
return day;
}
@end
MixiDailyImageViewCreator.h
#import "NSDate+Day.h"
@interface MixiDailyImageViewCreator : NSObject
+(id)imageViewWithDayOfWeek:(DayOfWeekType)dayOfWeekType;
@end
MixiDailyImageViewCreator.m
@implementation MixiDailyImageViewCreator
+(id)imageViewWithDayOfWeek:(DayOfWeekType)dayOfWeekType
{
//do something
}
@end
イベントハンドラをすべて Controller で実装すると Controller が肥大化します。このようにイベントハンドラでいっぱいになってしまった場合、画面遷移を管理するクラスを生成し、Controller の参照を渡してそのクラスで処理を行うようにします。そうすると、Controller のコードの可読性は上がりますし、画面遷移の処理も一カ所にまとめることが可能になります。
MixiTransitionHandler.h
@interface MixiTransitionHandler : NSObject
+(void)pushSecoundViewControllerWithViewController:(UIViewController *)viewController;
+(void)pushThirdViewControllerWithViewController:(UIViewController *)viewController;
+(void)presentScountViewControllerWithViewController:(UIViewController *)viewController;
+(void)presentThirdViewControllerWithViewController:(UIViewController *)viewController;
@end
MixiTransitionHandler.m
@implementation MixiTransitionHandler
@implementation MixiTransitionHandler
+(void)pushSecoundViewControllerWithViewController:(UIViewController *)viewController
{
UIViewController *vc = [[UIViewController alloc] init];
[viewController.navigationController pushViewController:vc animated:YES];
}
+(void)pushThirdViewControllerWithViewController:(UIViewController *)viewController
{
//do something
}
...
@end
はじめに
-
導入
-
1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)
-
UIKit 1 - container, rotate-
-
UIKit 2- UIView -
-
UIKit 3 - table view -
-
UIKit 4 - image and text -
-
ネットワーク処理
-
ローカルキャッシュと通知
-
Blocks, GCD
-
設計とデザインパターン
-
開発ツール
-
テスト
-
In-App Purchase
-
付録