Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added a bit more of the tutorial. Now needs editing down

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRubyWebsite/trunk@4448 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
commit 972ef02468076707228f756e53285b2b327d6e15 1 parent 903d769
Matt Aimonetti mattetti authored
121 content/realworld-dynamic-bundles.txt → content/documentation/realworld-dynamic-bundles.txt
@@ -46,32 +46,42 @@ h3. Adding our Bundle target
46 46
47 47 The first thing we need to do is add a new Target to our project. Select "New Target..." from the Project menu, and select "Loadable Bundle". Call this <code>TagLibBundle</code>.
48 48
49   -Open up the "Compile Sources" folder inside the "TagLib" Framework target. Shift-select every file in this folder, and open "Get Info" in the File menu. Open up the Targets tab at the top, and check the tickbox next to <code>TagLibBundle</code>. This ensures that every file used to compile the framework is also used to compile our new bundle target.
  49 +Open up the "Compile Sources" folder inside the "TagLib" Framework target. Shift-select every file in this folder, and open "Get Info" in the File menu. Open up the Targets tab at the top, and check the tickbox next to <code>TagLibBundle</code>. This ensures that every file used to compile the framework is also used to compile our new bundle target. This also reveals certain configuration options within the project build settings.
50 50
51   -Now open the <code>TagLibBundle</code> target info pane, and search for "Preprocessor Macros". Add <code>HAVE_CONFIG_H</code> to this entry. This is related to the way the project has been ported from the Autoconf setup of the original source code.
  51 +Open the <code>TagLibBundle</code> target info pane, and search for "Preprocessor Macros". Add <code>HAVE_CONFIG_H</code> to this entry. This is related to the way the project has been ported from the Autoconf setup of the original source code, and is required to build properly.
52 52
53   -!/images/realworld-dynamic-bundles/preprocessor_macros.png!
54 53
55   -While this info view is open, we also need to remove the entries under "Prefix Header"
  54 +!{width:600px;padding:20px 0px}/images/realworld-dynamic-bundles/preprocessor_macros.png!
  55 +
  56 +
  57 +While this view is open, we also need to remove the entries under "Prefix Header"
  58 +
  59 +
  60 +!{width:600px;padding:20px 0px}/images/realworld-dynamic-bundles/remove_prefix_header.png!
56 61
57   -!/images/realworld-dynamic-bundles/remove_prefix_header.png!
58 62
59 63 and "Other Linker Flags". These are set up by default, and are not required for our bundle.
60 64
61   -!/images/realworld-dynamic-bundles/remove_other_linker_flags.png!
62 65
  66 +!{width:600px;padding:20px 0px}/images/realworld-dynamic-bundles/remove_other_linker_flags.png!
  67 +
  68 +Lastly, we enable garbage collection support.
  69 +
  70 +!{width:600px;padding:20px 0px}/images/realworld-dynamic-bundles/enable_gc.png!
  71 +
  72 +Now swap to the "General" tab and ensure that we are linking the project against the "Foundation" framework and "libz" shared library. Check that the project builds without errors at this stage.
63 73
64   -Now swap to the "General" tab and ensure that we are linking the project against the "Foundation" framework and "libz" shared library. Check that the project builds OK at this stage.
65 74
66 75 h3. Adding our Objective-C wrapper class
67 76
68 77 Now comes the fun part. We write a very simple Objective-C class to wrap the C functionality of the <code>TagLib</code> code.
69 78
70   -We will take a very simplistic approach in buidling the wrapper, as this ensures easy memory management. Our class will have an <code>initWithFileAtPath:</code> method, which we will use to initialise our class, and perform the scan of the file for tags. The tags themselves will be placed inside an <code>NSDictionary</code> to be read at a later point. Lastly the <code>dealloc</code> method will release the <code>NSDictionary</code>. All of the potentially tricky memory management is contained only within the <code>initWithFileAtPath:</code> method, and occurs within a single method invocation.
  79 +We will take a very simplistic approach in building the wrapper, as this ensures easy memory management. Our class will have an <code>initWithFileAtPath:</code> method, which we will use to initialise our class, and perform the scan of the file for tags. The tags themselves will be placed inside an <code>NSDictionary</code> to be read at a later point. Lastly the <code>dealloc</code> method will release the <code>NSDictionary</code>. All of the potentially tricky memory management is contained only within the <code>initWithFileAtPath:</code> method, and occurs within a single method invocation.
71 80
72 81 Now we need to add an Objective-C class which will perform our wrapping duties. Add a new Objective-C file named "TagLib.m" to the project, subclassed from <code>NSObject</code>. Ensure that is is only part of the TagLibBundle target.
73 82
74   -!/images/realworld-dynamic-bundles/new_wrapper_class.png!
  83 +
  84 +!{width:600px;padding:20px 0px}/images/realworld-dynamic-bundles/new_wrapper_class.png!
75 85
76 86
77 87 Add a new Objective-C file to the <code>TagLibBundle</code> target called <code>TagLib.m</code>. Once we synthesize a simple <code>NSDictionary</code> to contain our tag, our header looks like:
@@ -89,6 +99,9 @@ Add a new Objective-C file to the <code>TagLibBundle</code> target called <code>
89 99 and our <code>TagLib.m</code> file:
90 100
91 101 <% coderay :lang => 'c' do -%>
  102 +
  103 +void Init_TagLibBundle(void) { }
  104 +
92 105 @implementation TagLib
93 106
94 107 @synthesize tags;
@@ -107,17 +120,21 @@ and our <code>TagLib.m</code> file:
107 120 }
108 121
109 122 @end
  123 +
110 124 <% end %>
111 125
  126 +The <code>Init_TagLibBundle</code> c method declaration is required to identify this as a MacRuby bundle. When loading a bundle with <code>require</code> from within MacRuby, it will automatically look for a method with the signature <code>Init_XXX</code> where XXX is the Product Name taken from the Target settings.
  127 +
  128 +Now most of the code for our <code>initWithFileAtPath:</code> has already been implemented in "taglib-src/examples/tagreader_c.c". We start off by opening the file, and initializing taglib:
  129 +
112 130 <% coderay :lang => 'c' do -%>
113 131
114 132 - (id)initWithFileAtPath:(NSString *)filePath {
115 133 if (self = [super init]) {
116 134
117   - // Our mutable dictionary for accumulation
  135 + // Our mutable dictionary in which we add the tags
118 136 NSMutableDictionary *tempDictionary = [NSMutableDictionary dictionary];
119 137
120   - // Initialisation as per the TagLib example C code
121 138 TagLib_File *file;
122 139 TagLib_Tag *tag;
123 140
@@ -128,68 +145,62 @@ and our <code>TagLib.m</code> file:
128 145
129 146 if (file != NULL) {
130 147 tag = taglib_file_tag(file);
  148 + ...
  149 +<% end %>
131 150
132   - // Collect title, artist, album, comment, genre, track and year in turn.
133   - // Sanity check them for presence, and length
  151 +After initialisation, we can try reading a single tag from the opened file with the following:
  152 +
  153 +<% coderay :lang => 'c' do -%>
  154 + ...
134 155 if (taglib_tag_title(tag) != NULL &&
135 156 strlen(taglib_tag_title(tag)) > 0) {
136 157 NSString *title = [NSString stringWithCString:taglib_tag_title(tag)
137 158 encoding:NSUTF8StringEncoding];
138 159 [tempDictionary setObject:title forKey:@"title"];
139 160 }
140   -
141   - if (taglib_tag_artist(tag) != NULL &&
142   - strlen(taglib_tag_artist(tag)) > 0) {
143   - NSString *artist = [NSString stringWithCString:taglib_tag_artist(tag)
144   - encoding:NSUTF8StringEncoding];
145   - [tempDictionary setObject:artist forKey:@"artist"];
146   - }
147   -
148   - if (taglib_tag_album(tag) != NULL &&
149   - strlen(taglib_tag_album(tag)) > 0) {
150   - NSString *album = [NSString stringWithCString:taglib_tag_album(tag)
151   - encoding:NSUTF8StringEncoding];
152   - [tempDictionary setObject:album forKey:@"album"];
153   - }
154   -
155   - if (taglib_tag_comment(tag) != NULL &&
156   - strlen(taglib_tag_comment(tag)) > 0) {
157   - NSString *comment = [NSString stringWithCString:taglib_tag_comment(tag)
158   - encoding:NSUTF8StringEncoding];
159   - [tempDictionary setObject:comment forKey:@"comment"];
160   - }
161   -
162   - if (taglib_tag_genre(tag) != NULL &&
163   - strlen(taglib_tag_genre(tag)) > 0) {
164   - NSString *genre = [NSString stringWithCString:taglib_tag_genre(tag)
165   - encoding:NSUTF8StringEncoding];
166   - [tempDictionary setObject:genre forKey:@"genre"];
167   - }
168   -
169   - // Year and track are uints
170   - if (taglib_tag_year(tag) > 0) {
171   - NSNumber *year = [NSNumber numberWithUnsignedInt:taglib_tag_year(tag)];
172   - [tempDictionary setObject:year forKey:@"year"];
173   - }
174   -
175   - if (taglib_tag_track(tag) > 0) {
176   - NSNumber *track = [NSNumber numberWithUnsignedInt:taglib_tag_track(tag)];
177   - [tempDictionary setObject:track forKey:@"track"];
178   - }
179   -
180   - // Free up our used memory so far
  161 + ...
  162 +<% end %>
  163 +
  164 +Lastly, we clean up the allocated memory, close the file, and convert the NSMutableDictionary into an NSDictionary:
  165 +
  166 +<% coderay :lang => 'c' do -%>
  167 + ...
181 168 taglib_tag_free_strings();
182 169 taglib_file_free(file);
183 170
184 171 }
185 172
186   - // Make immutable
187 173 self.tags = [NSDictionary dictionaryWithDictionary:tempDictionary];
188 174 [tempDictionary release];
189 175 }
190 176
191 177 return self;
192 178 }
  179 +
193 180 <% end %>
194 181
  182 +So now we have a simple class which should place the title tag of the file into the <code>tags</code> dictionary. The last thing we need to set up with XCode is a small build script to copy the Mach-O binary out of the standard Mac OS X bundle structure. We need direct access to the Mach-O binary bundle, as it is this we use from within MacRuby.
  183 +
  184 +We accomplish this with a small "Run Script Build Phase" added to the bundle target:
  185 +
  186 +<% coderay :lang => 'sh' do -%>
  187 + cp -v "${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
  188 + "${SOURCE_ROOT}/${FULL_PRODUCT_NAME}"
  189 +<% end %>
  190 +
  191 +Now we can build the target, and a file called "TagLib.bundle" should appear in the project root. Now from a terminal, we can quickly test out the newly built bundle using the "macirb" command-line ruby interpreter:
  192 +
  193 +<% coderay :lang => 'ruby' do -%>
  194 +$ macirb
  195 +irb(main):001:0> require 'TagLibBundle'
  196 +=> true
  197 +irb(main):002:0> test = TagLib.alloc.initWithFileAtPath("test.mp3")
  198 +=> #<TagLib:0x200090880>
  199 +irb(main):003:0> test.tags[:title]
  200 +=> "Mmm Skyscraper I Love You"
  201 +irb(main):004:0>
  202 +<% end %>
  203 +
  204 +The code for this example is available on GitHub: <a href="http://github.com/nickludlam/TagLib.framework">http://github.com/nickludlam/TagLib.framework</a>.
  205 +
195 206 </div>
BIN  content/images/realworld-dynamic-bundles/enable_gc.png

0 comments on commit 972ef02

Please sign in to comment.
Something went wrong with that request. Please try again.