r81007 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r81006‎ | r81007 | r81008 >
Date:23:46, 25 January 2011
Author:hartman
Status:deferred
Tags:
Comment:
Rework the WikiSnaps uploader to use the xmltodict parser.
Modified paths:
  • /trunk/tools/WikiSnaps/Classes/CommonsUpload.h (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/CommonsUpload.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/ImageDetailsViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/Classes/ImageUploadViewController.m (modified) (history)
  • /trunk/tools/WikiSnaps/ImageDetailsViewController.xib (modified) (history)

Diff [purge]

Index: trunk/tools/WikiSnaps/Classes/ImageUploadViewController.m
@@ -82,6 +82,7 @@
8383 }
8484
8585 - (IBAction)onCancelUploadClicked: (id)sender {
 86+ /* FIXME cancel all api events on upload */
8687 [self.navigationController popToRootViewControllerAnimated:YES];
8788 }
8889
@@ -95,7 +96,6 @@
9697 otherButtonTitles: nil];
9798 [alert show];
9899 [alert release];
99 - [self.navigationController popToRootViewControllerAnimated:YES];
100100 }
101101
102102 - (void)uploadFailed:(NSString *)error {
Index: trunk/tools/WikiSnaps/Classes/ImageDetailsViewController.m
@@ -32,10 +32,11 @@
3333 // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
3434 - (void)viewDidLoad {
3535 [super viewDidLoad];
36 - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: NSLocalizedString( @"Upload", @"Title for upload buton on image details view" )
37 - style: UIBarButtonItemStyleDone
38 - target: self
39 - action: @selector( doUpload: ) ];
 36+ self.title = NSLocalizedString( @"Image details", "Title of the Image Details view" );
 37+ self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: NSLocalizedString( @"Upload", @"Title for upload buton on image details view" )
 38+ style: UIBarButtonItemStyleDone
 39+ target: self
 40+ action: @selector( doUpload: ) ];
4041 }
4142
4243
@@ -69,9 +70,19 @@
7071 }
7172
7273 -(IBAction)textFieldDidEnd:(id)sender {
 74+ [self textFieldDidEndEditing:sender];
 75+}
 76+
 77+-(void)textFieldDidEndEditing:(id)sender {
7378 if(sender == titleField ) {
 79+ /* Verify name */
 80+ if( ![upload verifyTitle: titleField.text] ) {
 81+ titleField.textColor = [UIColor redColor];
 82+ } else {
 83+ titleField.textColor = [UIColor blackColor];
7484 [descriptionText becomeFirstResponder];
75 - return;
 85+ }
 86+ return;
7687 }
7788 [sender resignFirstResponder];
7889 }
Index: trunk/tools/WikiSnaps/Classes/CommonsUpload.h
@@ -37,6 +37,7 @@
3838 - (NSString *)getUploadText;
3939 - (NSString *)getUploadDescription;
4040
41 -- (void) uploadImage;
 41+- (void)uploadImage;
 42+- (BOOL)verifyTitle:(NSString *)possibleTitle;
4243
4344 @end
Index: trunk/tools/WikiSnaps/Classes/CommonsUpload.m
@@ -10,207 +10,285 @@
1111 #import "CommonsUpload.h"
1212 #import "Configuration.h"
1313 #import "ASIFormDataRequest.h"
 14+#import "XMLReader.h"
1415
 16+/* Private */
 17+@interface CommonsUpload (Internal)
1518
 19+- (void)checkXML: (NSDictionary *)dict forAPIError: (NSError **)error;
 20+@end
 21+
 22+
1623 @implementation CommonsUpload
1724
1825 @synthesize imageData, title, description, delegate;
1926
2027 - (NSString *)getUploadText {
21 - return @"";
 28+ return @"";
2229 }
2330
2431 - (NSString *)getUploadDescription {
25 - NSDate *today = [NSDate date];
26 - NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
27 - [formatter setDateFormat:@"yyyy-MM-dd"];
28 - NSString *dateString = [formatter stringFromDate:today];
29 - NSLog(dateString);
30 -
31 - return [NSString stringWithFormat: @"{{Information\n|Description={{en|1=%@}}\n|Author=[[User:%@]]\n|Source={{own}}\n|Date=%@\n|Permission=\n|other_versions=\n}}\n\n== {{int:license}} ==\n%@\n\n[[Category:%@]]",
32 - description,
33 - [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY],
34 - dateString,
35 - DEFAULT_LICENSE,
36 - APPLICATION_CATEGORY,
37 - nil
38 - ];
 32+ NSDate *today = [NSDate date];
 33+ NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
 34+ [formatter setDateFormat:@"yyyy-MM-dd"];
 35+ NSString *dateString = [formatter stringFromDate:today];
 36+ NSLog(dateString);
 37+
 38+ return [NSString stringWithFormat: @"{{Information\n|Description={{en|1=%@}}\n|Author=[[User:%@]]\n|Source={{own}}\n|Date=%@\n|Permission=\n|other_versions=\n}}\n\n== {{int:license}} ==\n%@\n\n[[Category:%@]]",
 39+ description,
 40+ [[NSUserDefaults standardUserDefaults] valueForKey: COMMONS_USERNAME_KEY],
 41+ dateString,
 42+ DEFAULT_LICENSE,
 43+ APPLICATION_CATEGORY,
 44+ nil
 45+ ];
3946 }
4047
4148 - (void)uploadImage {
42 - NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
43 - ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
44 - [request setPostFormat:ASIURLEncodedPostFormat];
45 -
46 - [request addPostValue:@"login" forKey:@"action"];
47 - [request addPostValue:@"xml" forKey: @"format"];
48 - [request addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY] forKey: @"lgname"];
49 - [request addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_PASSWORD_KEY] forKey: @"lgpassword"];
50 -
51 - [request setDelegate:self];
52 - [request setDidFinishSelector:@selector(requestTokenFinished:)];
53 - [request setDidFailSelector:@selector(requestTokenFailed:)];
54 - [request startAsynchronous];
 49+ NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
 50+ ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
 51+ [request setPostFormat:ASIURLEncodedPostFormat];
 52+
 53+ [request addPostValue:@"login" forKey:@"action"];
 54+ [request addPostValue:@"xml" forKey: @"format"];
 55+ [request addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY] forKey: @"lgname"];
 56+ [request addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_PASSWORD_KEY] forKey: @"lgpassword"];
 57+
 58+ [request setDelegate:self];
 59+ [request setDidFinishSelector:@selector(requestTokenFinished:)];
 60+ [request setDidFailSelector:@selector(requestTokenFailed:)];
 61+ [request startAsynchronous];
5562 }
5663
5764 - (void)requestTokenFinished:(ASIHTTPRequest *)request
5865 {
59 - // Use when fetching text data
60 - NSString *responseString = [request responseString];
61 - NSLog(responseString );
62 - NSScanner *aScanner = [NSScanner scannerWithString:responseString];
63 -
64 - [aScanner scanUpToString:@"result=\"" intoString: nil];
65 - [aScanner scanString:@"result=\"" intoString: nil];
66 - NSString *result;
67 - [aScanner scanUpToString:@"\"" intoString:&result];
68 - if( ![result isEqualToString:@"NeedToken"] ) {
69 - [delegate uploadFailed: [NSString stringWithFormat:@"no needtoken response: %@", result]];
70 - return;
71 - }
72 -
73 - [aScanner scanUpToString:@"token=\"" intoString: nil];\
74 - [aScanner scanString:@"token=\"" intoString: nil];
75 - [aScanner scanUpToString:@"\"" intoString:&token];
76 -
77 - //New request
78 - NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
79 - ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
80 - [newRequest setPostFormat:ASIURLEncodedPostFormat];
81 -
82 - [newRequest addPostValue:@"login" forKey:@"action"];
83 - [newRequest addPostValue:@"xml" forKey: @"format"];
84 - [newRequest addPostValue:token forKey:@"lgtoken"];
85 - [newRequest addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY] forKey: @"lgname"];
86 - [newRequest addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_PASSWORD_KEY] forKey: @"lgpassword"];
87 -
88 - [newRequest setDelegate:self];
89 - [newRequest setDidFinishSelector:@selector(requestLoginFinished:)];
90 - [newRequest setDidFailSelector:@selector(requestLoginFailed:)];
91 - [newRequest startAsynchronous];
92 -
93 -
94 -
95 - // Use when fetching binary data
96 - // FIXME Use this for building xml parser
97 - //NSData *responseData = [request responseData];
 66+ NSError *error = nil;
 67+ NSDictionary *dict = [XMLReader dictionaryForXMLData: [request responseData] error: &error];
 68+
 69+ if( error ) {
 70+ /* XML parser error */
 71+ [delegate uploadFailed: [error localizedDescription]];
 72+ return;
 73+ }
 74+
 75+ [self checkXML:dict forAPIError: &error];
 76+
 77+ if( error ) {
 78+ /* API error */
 79+ [delegate uploadFailed: [error localizedDescription]];
 80+ return;
 81+ }
 82+
 83+ NSDictionary *login = [[dict objectForKey:@"api"] objectForKey: @"login"];
 84+ assert( login != nil );
 85+
 86+ if( [[login objectForKey:@"result"] caseInsensitiveCompare:@"NeedToken"] != NSOrderedSame ) {
 87+ [delegate uploadFailed: [NSString stringWithFormat:@"no needtoken response: %@", [login objectForKey:@"result"]]];
 88+ return;
 89+ }
 90+
 91+ token = [login objectForKey:@"token"];
 92+ assert( token != nil );
 93+
 94+ //New request
 95+ NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
 96+ ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
 97+ [newRequest setPostFormat:ASIURLEncodedPostFormat];
 98+
 99+ [newRequest addPostValue:@"login" forKey:@"action"];
 100+ [newRequest addPostValue:@"xml" forKey: @"format"];
 101+ [newRequest addPostValue:token forKey:@"lgtoken"];
 102+ [newRequest addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_USERNAME_KEY] forKey: @"lgname"];
 103+ [newRequest addPostValue:[[NSUserDefaults standardUserDefaults] valueForKey:COMMONS_PASSWORD_KEY] forKey: @"lgpassword"];
 104+
 105+ [newRequest setDelegate:self];
 106+ [newRequest setDidFinishSelector:@selector(requestLoginFinished:)];
 107+ [newRequest setDidFailSelector:@selector(requestLoginFailed:)];
 108+ [newRequest startAsynchronous];
98109 }
99110
100111 - (void)requestTokenFailed:(ASIHTTPRequest *)request
101112 {
102 - NSError *error = [request error];
103 - [delegate uploadFailed: [error localizedDescription]];
 113+ NSError *error = [request error];
 114+ [delegate uploadFailed: [error localizedDescription]];
104115 }
105116
106117
107118 - (void)requestLoginFinished:(ASIHTTPRequest *)request
108119 {
109 - // Use when fetching text data
110 - NSString *responseString = [request responseString];
111 - NSLog(responseString );
112 - NSScanner *aScanner = [NSScanner scannerWithString:responseString];
113 -
114 - [aScanner scanUpToString:@"result=\"" intoString: nil];
115 - [aScanner scanString:@"result=\"" intoString: nil];
116 - NSString *result;
117 - [aScanner scanUpToString:@"\"" intoString:&result];
118 - if( ![result isEqualToString:@"Success"] ) {
119 - [delegate uploadFailed: [NSString stringWithFormat:@"no success response, %@", result]];
120 - return;
121 - }
122 -
123 - [aScanner scanUpToString:@"token=\"" intoString: nil];
124 - [aScanner scanString:@"token=\"" intoString: nil];
125 - [aScanner scanUpToString:@"\"" intoString:&token];
126 -
127 - //New request
128 - NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
129 - ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
130 - [newRequest setPostFormat:ASIURLEncodedPostFormat];
131 -
132 - [newRequest addPostValue:@"query" forKey:@"action"];
133 - [newRequest addPostValue:@"xml" forKey: @"format"];
134 - [newRequest addPostValue:@"edit" forKey:@"intoken"];
135 - [newRequest addPostValue:title forKey:@"titles"];
136 - [newRequest addPostValue:@"info" forKey:@"prop"];
137 -
138 - [newRequest setDelegate:self];
139 - [newRequest setDidFinishSelector:@selector(requestEditTokenFinished:)];
140 - [newRequest setDidFailSelector:@selector(requestEditTokenFailed:)];
141 - [newRequest startAsynchronous];
142 -
143 -
144 - // Use when fetching binary data
145 - // Use this for building xml parser
146 - //NSData *responseData = [request responseData];
 120+ NSError *error = nil;
 121+ NSDictionary *dict = [XMLReader dictionaryForXMLData: [request responseData] error: &error];
 122+
 123+ if( error ) {
 124+ /* XML parser error */
 125+ [delegate uploadFailed: [error localizedDescription]];
 126+ return;
 127+ }
 128+
 129+ [self checkXML:dict forAPIError: &error];
 130+
 131+ if( error ) {
 132+ /* API error */
 133+ [delegate uploadFailed: [error localizedDescription]];
 134+ return;
 135+ }
 136+
 137+ NSDictionary *login = [[dict objectForKey:@"api"] objectForKey: @"login"];
 138+ assert( login != nil );
 139+
 140+ if( [[login objectForKey:@"result"] caseInsensitiveCompare:@"Success"] != NSOrderedSame ) {
 141+ [delegate uploadFailed: [NSString stringWithFormat:@"no success response: %@", [login objectForKey:@"result"]]];
 142+ return;
 143+ }
 144+
 145+ token = [login objectForKey:@"token"];
 146+
 147+ //New request
 148+ NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
 149+ ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
 150+ [newRequest setPostFormat:ASIURLEncodedPostFormat];
 151+
 152+ [newRequest addPostValue:@"query" forKey:@"action"];
 153+ [newRequest addPostValue:@"xml" forKey: @"format"];
 154+ [newRequest addPostValue:@"edit" forKey:@"intoken"];
 155+ [newRequest addPostValue:title forKey:@"titles"];
 156+ [newRequest addPostValue:@"info" forKey:@"prop"];
 157+
 158+ [newRequest setDelegate:self];
 159+ [newRequest setDidFinishSelector:@selector(requestEditTokenFinished:)];
 160+ [newRequest setDidFailSelector:@selector(requestEditTokenFailed:)];
 161+ [newRequest startAsynchronous];
147162 }
148163
149164 - (void)requestLoginFailed:(ASIHTTPRequest *)request
150165 {
151 - NSError *error = [request error];
152 - [delegate uploadFailed:[error localizedDescription]];
 166+ NSError *error = [request error];
 167+ [delegate uploadFailed:[error localizedDescription]];
153168 }
154169
155170 - (void)requestEditTokenFinished:(ASIHTTPRequest *)request
156171 {
157 - // Use when fetching text data
158 - NSString *responseString = [request responseString];
159 - NSLog(responseString );
160 - NSScanner *aScanner = [NSScanner scannerWithString:responseString];
161 -
162 - [aScanner scanUpToString:@"edittoken=\"" intoString: nil];
163 - [aScanner scanString:@"edittoken=\"" intoString: nil];
164 -
165 - BOOL res;
166 - res = [aScanner scanUpToString:@"\"" intoString:&editToken];
167 - if( !res ) {
168 - [delegate uploadFailed: [NSString stringWithFormat:@"could not find edittoken"]];
169 - return;
170 - }
171 -
172 - //New request
173 - NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
174 - ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
175 - [newRequest setPostFormat:ASIMultipartFormDataPostFormat];
176 -
177 - [newRequest addPostValue:@"upload" forKey:@"action"];
178 - [newRequest addPostValue:@"xml" forKey: @"format"];
179 - [newRequest addPostValue:editToken forKey:@"token"];
180 - [newRequest addPostValue:title forKey:@"filename"];
181 - [newRequest addPostValue:[self getUploadDescription] forKey:@"comment"];
182 - [newRequest addPostValue:[self getUploadDescription] forKey:@"text"];
183 - [newRequest addData:imageData forKey:@"file"];
184 -
185 -
186 - [newRequest setDelegate:self];
187 - [newRequest setDidFinishSelector:@selector(requestUploadFinished:)];
188 - [newRequest setDidFailSelector:@selector(requestUploadFailed:)];
189 - [newRequest startAsynchronous];
190 -
191 - // Use when fetching binary data
192 - // Use this for building xml parser
193 - //NSData *responseData = [request responseData];
 172+ NSError *error = nil;
 173+ NSDictionary *dict = [XMLReader dictionaryForXMLData: [request responseData] error: &error];
 174+
 175+ if( error ) {
 176+ /* XML parser error */
 177+ [delegate uploadFailed: [error localizedDescription]];
 178+ return;
 179+ }
 180+
 181+ [self checkXML:dict forAPIError: &error];
 182+
 183+ if( error ) {
 184+ /* API error */
 185+ [delegate uploadFailed: [error localizedDescription]];
 186+ return;
 187+ }
 188+
 189+ NSDictionary *query = [[dict objectForKey:@"api"] objectForKey: @"query"];
 190+ assert( query != nil );
 191+ NSLog( @"%@", query );
 192+
 193+ query = [[query objectForKey:@"pages"] objectForKey:@"page"];
 194+
 195+ /* Page can also be an array of page elements */
 196+ if( !query || ![query isKindOfClass: [NSDictionary class]] ) {
 197+ assert( 0 );
 198+ return;
 199+ }
 200+
 201+ editToken = [query objectForKey:@"edittoken"];
 202+ if( !editToken ) {
 203+ [delegate uploadFailed: [NSString stringWithFormat:@"could not find edittoken"]];
 204+ return;
 205+ }
 206+
 207+ //New request
 208+ NSURL *url = [NSURL URLWithString:COMMONS_API_URL];
 209+ ASIFormDataRequest *newRequest = [ASIFormDataRequest requestWithURL:url];
 210+ [newRequest setPostFormat:ASIMultipartFormDataPostFormat];
 211+
 212+ [newRequest addPostValue:@"upload" forKey:@"action"];
 213+ [newRequest addPostValue:@"xml" forKey: @"format"];
 214+ [newRequest addPostValue:editToken forKey:@"token"];
 215+ [newRequest addPostValue:title forKey:@"filename"];
 216+ [newRequest addPostValue:[self getUploadDescription] forKey:@"comment"];
 217+ [newRequest addPostValue:[self getUploadDescription] forKey:@"text"];
 218+ [newRequest addData:imageData forKey:@"file"];
 219+
 220+
 221+ [newRequest setDelegate:self];
 222+ [newRequest setDidFinishSelector:@selector(requestUploadFinished:)];
 223+ [newRequest setDidFailSelector:@selector(requestUploadFailed:)];
 224+ [newRequest startAsynchronous];
194225 }
195226
196227 - (void)requestEditTokenFailed:(ASIHTTPRequest *)request
197228 {
198 - NSError *error = [request error];
199 - [delegate uploadFailed:[error localizedDescription]];
 229+ NSError *error = [request error];
 230+ [delegate uploadFailed:[error localizedDescription]];
200231 }
201232
202233 - (void)requestUploadFinished:(ASIHTTPRequest *)request
203234 {
204 - // Use when fetching text data
205 - NSString *responseString = [request responseString];
206 - NSLog(responseString );
207 - [delegate uploadSucceeded];
 235+ NSError *error = nil;
 236+ NSDictionary *dict = [XMLReader dictionaryForXMLData: [request responseData] error: &error];
 237+
 238+ if( error ) {
 239+ /* XML parser error */
 240+ [delegate uploadFailed: [error localizedDescription]];
 241+ return;
 242+ }
 243+
 244+ [self checkXML:dict forAPIError: &error];
 245+
 246+ if( error ) {
 247+ /* API error */
 248+ [delegate uploadFailed: [error localizedDescription]];
 249+ return;
 250+ }
 251+NSLog( @"%@", dict );
 252+
 253+ NSDictionary *upload = [[dict objectForKey:@"api"] objectForKey: @"upload"];
 254+ assert( upload != nil );
 255+
 256+ if( [[upload objectForKey:@"result"] caseInsensitiveCompare:@"Success"] != NSOrderedSame ) {
 257+ [delegate uploadFailed: [NSString stringWithFormat:@"no success response: %@", [upload objectForKey:@"result"]]];
 258+ return;
 259+ }
 260+
 261+ /* The image will now be here: */
 262+ [[upload objectForKey:@"imageinfo"] objectForKey:@"descriptionurl"];
 263+
 264+ [delegate uploadSucceeded];
208265 }
209266
210267 - (void)requestUploadFailed:(ASIHTTPRequest *)request
211268 {
212 - NSError *error = [request error];
213 - [delegate uploadFailed:[error localizedDescription]];
 269+ NSError *error = [request error];
 270+ [delegate uploadFailed:[error localizedDescription]];
214271 }
215272
 273+/* Will verify if a proposed title is available for upload (missing) */
 274+/* When we do this, also get edittoken, so we don't have to later on ? */
 275+- (BOOL)verifyTitle: (NSString *)possibleTitle {
 276+ if( possibleTitle != nil && ![possibleTitle isEqualToString:@""] ) {
 277+ return YES;
 278+ }
 279+ return NO;
 280+}
216281
 282+- (void)checkXML: (NSDictionary *)dict forAPIError: (NSError **)error {
 283+ NSDictionary *errorDict = [[dict objectForKey:@"api"] objectForKey: @"error"];
 284+ if( errorDict ) {
 285+ NSMutableDictionary *nsErrorDict = [NSMutableDictionary dictionaryWithCapacity:5];
 286+ [nsErrorDict setObject: @"Communication failure with server" forKey: NSLocalizedDescriptionKey ];
 287+ [nsErrorDict setObject: [errorDict objectForKey:@"info"] forKey: NSLocalizedFailureReasonErrorKey ];
 288+NSLog( [errorDict objectForKey:@"info"] );
 289+
 290+ NSError *APIError = [NSError errorWithDomain: @"COMMONS_API_DOMAIN" code: 1 userInfo: nsErrorDict];
 291+ *error = APIError;
 292+ }
 293+}
 294+
217295 @end
Index: trunk/tools/WikiSnaps/ImageDetailsViewController.xib
@@ -2,10 +2,10 @@
33 <archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
44 <data>
55 <int key="IBDocument.SystemTarget">1056</int>
6 - <string key="IBDocument.SystemVersion">10H574</string>
 6+ <string key="IBDocument.SystemVersion">10J567</string>
77 <string key="IBDocument.InterfaceBuilderVersion">823</string>
88 <string key="IBDocument.AppKitVersion">1038.35</string>
9 - <string key="IBDocument.HIToolboxVersion">461.00</string>
 9+ <string key="IBDocument.HIToolboxVersion">462.00</string>
1010 <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
1111 <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
1212 <string key="NS.object.0">132</string>
@@ -193,15 +193,6 @@
194194 </object>
195195 <int key="connectionID">13</int>
196196 </object>
197 - <object class="IBConnectionRecord">
198 - <object class="IBCocoaTouchEventConnection" key="connection">
199 - <string key="label">textFieldDidEnd:</string>
200 - <reference key="source" ref="717110592"/>
201 - <reference key="destination" ref="372490531"/>
202 - <int key="IBEventType">20</int>
203 - </object>
204 - <int key="connectionID">14</int>
205 - </object>
206197 </object>
207198 <object class="IBMutableOrderedSet" key="objectRecords">
208199 <object class="NSArray" key="orderedObjects">
@@ -269,7 +260,7 @@
270261 <bool key="EncodedWithXMLCoder">YES</bool>
271262 <string>ImageDetailsViewController</string>
272263 <string>UIResponder</string>
273 - <string>{{791, 124}, {320, 480}}</string>
 264+ <string>{{577, 124}, {320, 480}}</string>
274265 <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
275266 <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
276267 <object class="NSAffineTransform">

Status & tagging log