1
0

More than 1 year has passed since last update.

「やっとわかったCoreData」のモノマネ

Posted at

概要

@pe-taさんの「やっとわかったSwift/CoreData入門」をObjective-Cに移植したものです。(逐語訳ではありません。自己流のコードだらけです。)マックブック用ですから、よかったらアップルのDeveloperアカウントを持ってない方も使ってみてください。

バージョンはmacOSが12.3、Xcodeが13.3です。

プロジェクトの作成

Xcode→File→NewProjectとし、最上段をmacOS(iOSではない)とし、APPを指定します。次のページに移って、プロダクト名は原作と同じTestCoreData、言語はObjective-Cにします。UseCoreDataのチェックをお忘れなく。
チーム名などはNoneのままでOKです。

Entityの設定

原作と全く同じで、新しいEntityを作り、名前をMonsterとします。そのAttributeは一つだけで、NameはmonsterName、TypeはStringとします。

EntityのMonsterを選択し、右側のCodegenというところがClass Definitionになっているのを確認して下さい。多分デフォールトでそうなっている筈です。

ViewController.m

ちょっと乱暴ですが、元のコードをすべて削り、代わりに次のコードをこのままペーストして下さい。これをやらないと、次の作業の間ずっとXcodeが文句をいってきます。
//
//  ViewController.m
//  TestCoreData
//
//  Created by Myself on 2022/03/16.
//

#import "ViewController.h"

  ViewController *viewctrl_ptr;		/* (referenced by 'AppDelegate') */





@implementation ViewController

{
 
#pragma mark global variables within ViewController

  AppDelegate *parent_ptr;

  NSArray<NSManagedObject *> *monstary;		/* (handled by Core Data) */

  NSManagedObjectModel *my_mom;
  NSPersistentStoreCoordinator *my_psc;
  NSManagedObjectContext *my_moc;
  NSFetchRequest *my_fetch;
}





#pragma mark initializers

- (void)viewDidLoad

{ [super viewDidLoad];
  viewctrl_ptr = self;		/* set public variable */

  tbl_view_ptr.delegate = self;
  tbl_view_ptr.dataSource = self;

  monstary = nil;		/* (against some accident) */
} // viewDidLoad





- (void)setRepresentedObject:(id)representedObject

{ [super setRepresentedObject:representedObject];

	// Update the view, if already loaded.
} // setRepresentedObject





#pragma mark public functions

- (void)setParent_ptr:(AppDelegate *)myparent

	/* called by 'AppDelegate' */

{ parent_ptr = myparent;
  NSLog(@"parent ready");
} // setParent_ptr





-(void)set_pc

	/* set persistent-container variables */
	/* called by 'AppDelegate' */
	/* called after AppDelegate ready */

{ [self initcore];		/* initialize core data */
  [tbl_view_ptr reloadData];	/* display registered Monsters */
} // set_pc





#pragma mark event handler

- (IBAction)editdone:(NSTextField *)sender

{ NSString *mystring;

  NSLog(@"notification from text-field");
  [txtfield_ptr resignFirstResponder];

  mystring = txtfield_ptr.stringValue;
  if ( mystring == nil )
    NSLog(@"textfield not yet ready");
  else if ( mystring.length == 0 )
    NSLog(@"textfield empty");
  else
  { [self createOneWithName: mystring];	/* create one Monster */
    [tbl_view_ptr reloadData];
    txtfield_ptr.stringValue = @"";	/* clear textfield */
  }
} // editdone





#pragma mark core data functions

- (void)initcore

	/* initialize core data */
	/* note: 'my_mom' and 'my_psc' not used */

{ NSArray *temp_ary;
  NSError *errorptr;

  NSAssert(parent_ptr.persistentContainer != nil, @"persistentContainer failed");

  my_mom = parent_ptr.persistentContainer.managedObjectModel;
  NSAssert(my_mom != nil, @"managed object managedobjectmode failed");
  my_psc = parent_ptr.persistentContainer.persistentStoreCoordinator;
  NSAssert(my_psc != nil, @"persistent store coordinator failed");
  my_moc = parent_ptr.persistentContainer.viewContext;
  NSAssert(my_moc != nil, @"managed object context failed");

  my_fetch = [[NSFetchRequest alloc] initWithEntityName: @"Monster"];

  errorptr = nil;	/* (unnecessary ... result not used) */
  temp_ary = [my_moc executeFetchRequest: my_fetch error: &errorptr];
  if ( temp_ary == nil )
    NSLog(@"monster array not yet ready");
  else			/* (usually array ready) */
    monstary = temp_ary;
} // initcore





- (void)createOneWithName: (NSString *)the_name

	/* create and save one entity */

{ NSManagedObject *one_monster;
  NSEntityDescription *myentity;
  NSError *errorptr;
  BOOL myresult;

	/* create one Monster(entity) */

  NSLog(@"start creating Monster");
  myentity = [NSEntityDescription entityForName:@"Monster"
	      inManagedObjectContext: my_moc];
  one_monster = [[NSManagedObject alloc]
    		 initWithEntity: myentity
    		 insertIntoManagedObjectContext: my_moc];
  NSAssert(one_monster != nil, @"managed object creation failed");

	/* set attribute (Monster name) */

  [one_monster setValue: the_name forKey: @"monsterName"];

	/* save the Monster */
	/* update monster-array */

  NSLog(@"start saving monster");
  errorptr = nil;	/* (&errorptr = '\0') */
  myresult = [one_monster.managedObjectContext save: &errorptr];
  if ( ( myresult == NO ) || ( errorptr != nil ) )
    NSLog(@"monster '%@' save error", the_name);
  else			/* (here errorptr == nil) */
  { NSLog(@"new monster with name '%@' saved", the_name);
    monstary = [my_moc executeFetchRequest: my_fetch error: &errorptr];
    NSAssert(monstary != nil, @"monster-array fetch failed");
  }
} // createOne





#pragma mark delegate and datasource

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView

{ NSInteger nbr_rows;		/* number of rows */

  if ( monstary == nil )	/* monster array not ready */
    nbr_rows = 0;
  else
    nbr_rows = monstary.count;

  return nbr_rows;
} // numberOfRows





- (id)tableView:(NSTableView *)aTableView
	objectValueForTableColumn:(NSTableColumn *)aTableColumn
	row:(NSInteger)rowIndex

{ NSString *mystring;
  NSManagedObject *one_monster;

  if ( monstary == nil )	/* array not ready (never occurs?) */
    mystring = @"";
  else
  { one_monster = [monstary objectAtIndex: rowIndex];
    mystring = [one_monster valueForKey: @"monsterName"];
  }

  return mystring;
} // objectValue

@end

Xcodeはまだ何か文句をいっていますが、先に進みましょう。

ViewController.h

変更は少しだけです。

#import <CoreData/CoreData.h>	// ← これを追加する

#import "AppDelegate.h"		// ← これを追加する

@interface ViewController : NSViewController
	<NSTableViewDelegate, NSTableViewDataSource> // ← これを追加する

	/* -- public functions (called by 'AppDelegate') -- */

	// AppDelegateから呼ばれるfunctionを2行追加
	
- (void)setParent_ptr:(AppDelegate *)myparent;
- (void)set_pc;

AppDelegate.m

変更は次のとおりです。

#import "AppDelegate.h"
#import "ViewController.h"		// ← これを追加する

  extern ViewController *viewctrl_ptr;	// ← これを追加する

@interface AppDelegate ()


- (IBAction)saveAction:(id)sender;

@end

@implementation AppDelegate

	// この関数の内容を次のようにする
	// この関数はViewDidLoadよりも後で呼ばれる
	// だからviewctrl_ptrはすでにセットされている筈

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

{ NSAssert(viewctrl_ptr != nil, @"ViewController not ready");
  [viewctrl_ptr setParent_ptr: self];
  [viewctrl_ptr set_pc];
	// Insert code here to initialize your application
}

AppDelegate.hの変更はありません。

(投稿直前になって気が付いたのですが、viewctrl_ptrの初期値をnilにしておくべきでした。このままではNSAssertの意味がありませんよね:sweat_smile: 。)

ストーリーボードの設定

Viewに部品TextFieldをセット(ドラッグ・ドロップ)します。

TextFieldのActionがSent On End Editingになっているのを確認します。

TextFieldの下に部品TableViewをセットします。

TableViewのContent ModeをCell Based、列数を1にします。

TableViewにはTextCellと表示されていますが、ここにTextFieldCellをセットします。似たような部品が多いのでご注意を。



ここから3本、コネクションをやります。アシスタントエディタを開き、ViewController.mを表示させます。

TextFieldからコントロール・ドラッグして、ViewController.mの@implementationのすぐ下のブレースの中にIBOutletを作り、名前をtxtfield_ptrにします。

TableViewからコントロール・ドラッグして、さっきの下にIBOutletを作り、名前をtbl_view_ptrにします。

最後にTextFieldからコントロール・ドラッグして、ViewController.mの中程にある(IBAction)editdoneにコネクトします。

なおdelegateとdatasourceはViewController.mで手当てしているので、処理は不要です。

試運転

ビルドさせて実行し、テキストフィールドにモンスター名を入力してエンターキーを押すと、テーブルに名前が表示されます。デバッグウィンドウで作業内容がわかりますね。

試運転が終わったらストップボタンを押して下さい。

アプリケーションを作成

Xcode→Product→Archiveに進むと、アーカイブが作成されます。作成後の表示を見失ったらXcode→Window→Organizerで同じ画面が表示されます。

Distribute Appからcopy Appとし、お好きなフォルダーを選んで下さい。Finderツールを使って見ると堂々たる(?)自作アプリケーションの完成です。拡張子もちゃんとappになっていますね。

Xcodeを終了し、Finderツールでアプリケーションをダブルクリックすると、Xcodeなしでアプリが起動し、メニューバーにはアプリ名が表示されます。

モンスター名をいくつか追加し、メニューバーからアプリを終了させ、再び起動させると、追加分も正しく表示されているのがわかります。

終わりに

最初に申し上げたとおり、コードが自己流ですみません。変数名や関数名にいわゆるキャメルをほとんど使っていませんが、お見逃し下さい。 viewctrl_ptrのようなバグがほかにもありそうです。いろいろご指摘をお待ちしております。
1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0