├── LICENSE ├── README.md └── screenshots.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 News.me, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iTC Localized Screenshot Uploader 2 | ================================= 3 | 4 | This script helps prepare localized screenshots for delivery to iTunes Connect via the Transporter. Screenshot generation, formatting, and uploading for all languages can be fully automated with these instructions. 5 | 6 | *** 7 | 8 | #### Step 1 9 | Open your app in iTunes Connect and create a new version. Ensure your app is listed in the "Prepare for Upload" status. Download your app's metadata package from Apple. 10 | 11 | ITMSUSER=YourItunesUsername 12 | ITMSPASS=YourItunesPassword 13 | ITMSSKU=YourAppSKU 14 | PATH="$PATH:/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/bin/" 15 | iTMSTransporter -m lookupMetadata -u $ITMSUSER -p $ITMSPASS -vendor_id $ITMSSKU -destination ~/Desktop 16 | 17 | This will save the .itmsp file to your desktop. 18 | 19 | #### Step 2 20 | Take screenshots and save them to `~/Desktop/screenshots` with the format like 21 | 22 | > `cmn-Hans___ios4in___portrait___screen1.png` 23 | 24 | This is comprised of sections delimited by `___`: 25 | 26 | - Device name: correct names are `Mac`, `iOS-3.5-in`, `iOS-4-in` or `iOS-iPad` 27 | - Locale: like `es-ES`, `fr-FR`, `ja-JP`, `en-US`, **HELP: NEED HELP FULLY DOCUMENTING LOCALE NAMES IN ITMS** 28 | 29 | Optional: write a UI script and automate generation of screenshots for all devices and localizations using https://github.com/jonathanpenn/ui-screen-shooter Note: if your screenshots are in the format `Locale/Devicename___other.png`, fix this with: `for a in */*; do d=$(dirname $a); f=$(basename $a); mv $a ${d}___${f}; done; rmdir */` 30 | 31 | #### Step 3 32 | Run `php screenshots.php` – this will add the XML chunks you need and make a fresh copy of your `metadata.xml` in the screenshots folder. 33 | 34 | #### Step 4 35 | Copy the contents of "screenshots" on your desktop into the itmsp file on your desktop (right-click -> show package contents to access the latter). Or use the following command: 36 | 37 | cp ~/Desktop/screenshots/* ~/Desktop/*.itmsp 38 | 39 | #### Step 5 40 | Verify your upload: 41 | 42 | iTMSTransporter -m verify -u $ITMSUSER -p $ITMSPASS -vendor_id $ITMSSKU -f ~/Desktop/*.itmsp 43 | 44 | Error "software_screenshots cannot be edited in the current state" happens if your app is currently being reviewed. 45 | 46 | #### Step 6 47 | If things went well, execute your upload: 48 | 49 | iTMSTransporter -m upload -u $ITMSUSER -p $ITMSPASS -vendor_id $ITMSSKU -f ~/Desktop/*.itmsp 50 | 51 | ----------------- 52 | 53 | ## Pro Tips 54 | You can modify Xcode schemes to automatically launch your app in a particular localization. I created a scheme for each language I have setup in iTunes Connect. To do this, duplicate your Debug scheme, edit it, and in the "Run" section under "Arguments", add your localization to "Arguments Passed on Launch" in this format: "-AppleLanguages (it)", where "it" is Italian, or whatever localization you want. 55 | 56 | On top of that, I wanted localized users for my screenshots, so I pass an Environment Variable of "USER_ID", then grab that in my code & setup the user programmatically. You can get Environment Variables via: 57 | [[NSProcessInfo processInfo] environment] 58 | 59 | 60 | ## Heads Up 61 | 62 | There's a bug in the Transporter that removes newlines from any textual metadata, so I don't recommend using it for that purpose at this time. 63 | 64 | For more info, check out the App Metadata Specs and the Transporter User Guide from https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/resources/download/TransporterQuickStartUserGuide/pdf 65 | 66 | ------------------ 67 | 68 | ## License 69 | Released under the MIT license, see the LICENSE file. 70 | -------------------------------------------------------------------------------- /screenshots.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | registerXPathNamespace("n", "http://apple.com/itunes/importer"); 32 | while ($a = $itmsp_parsed->xpath('//n:keywords')) 33 | unset($a[0][0]); 34 | } 35 | /* 36 | * STEP 3: ITERATE ON THE SCREEN SHOTS 37 | */ 38 | 39 | $tmp_file_output = ''; 40 | foreach ($screen_shots_by_locale_and_device as $locale => $screen_shots_by_device) { 41 | // Save all screen shot XML data 42 | $xml_to_insert = ''; 43 | $xml_for_screenshots .= "\n\n\nSCREEN SHOTS FOR LOCALE: $locale\n\n"; 44 | foreach ($screen_shots_by_device as $device => $screen_shots) 45 | foreach ($screen_shots as $position => $screen_shot) 46 | $xml_to_insert .= xmlChunk($device, $position+1, "$screen_shots_dir/$screen_shot", $screen_shot) . "\n"; 47 | $xml_to_insert .= ''; 48 | $tmp_file_output .= "\n\n\nSCREEN SHOTS FOR LOCALE: $locale\n\n" . $xml_to_insert; 49 | 50 | if (!isset($itmsp_parsed)) 51 | continue; 52 | 53 | // Find part of itmsp file to shove XML into 54 | $matched_itmsp_locale = NULL; 55 | foreach ($itmsp_parsed->software->software_metadata->versions->version->locales->locale as $itmsp_locale) 56 | if ($locale == (string)$itmsp_locale->attributes()->name) 57 | $matched_itmsp_locale = $itmsp_locale; 58 | 59 | // Shove it 60 | if ($matched_itmsp_locale) { 61 | echo "Splicing in screenshots for $locale\n"; 62 | unset($matched_itmsp_locale->software_screenshots); 63 | // https://stackoverflow.com/questions/3418019/simplexml-append-one-tree-to-another 64 | $dom1 = dom_import_simplexml($matched_itmsp_locale); 65 | $dom2 = dom_import_simplexml(simplexml_load_string($xml_to_insert)); 66 | $dom2 = $dom1->ownerDocument->importNode($dom2, TRUE); 67 | $dom1->appendChild($dom2); 68 | } else 69 | echo "Screenshots found for locale $locale but no matching metadata found in .itmsp, skipping\n"; 70 | } 71 | 72 | file_put_contents("$screen_shots_dir/xml_chunks_DEBUG.txt", $tmp_file_output); 73 | echo "Saved XML chunks of ".count($screen_shots_by_locale_and_device, COUNT_RECURSIVE)." screen shots to $screen_shots_dir/xml_chunks_DEBUG.txt\n"; 74 | 75 | if (isset($itmsp_parsed)) { 76 | $itmsp_parsed->asXML("$screen_shots_dir/metadata.xml"); 77 | echo "Saved updated metadata.xml file to $screen_shots_dir/metadata.xml\n"; 78 | } 79 | 80 | function xmlChunk($display_target, $position, $file_path, $file_name) 81 | { 82 | $file_size = filesize($file_path); 83 | $md5 = md5_file($file_path); 84 | 85 | return << 87 | $file_size 88 | $file_name 89 | $md5 90 | 91 | END; 92 | } 93 | 94 | 95 | function translateDevice($device) 96 | { 97 | if ($device == 'iphone4') { 98 | return 'iOS-3.5-in'; 99 | } elseif ($device == 'iphone5') { 100 | return 'iOS-4-in'; 101 | } elseif ($device == 'ipad') { 102 | return 'iOS-iPad'; 103 | } else { 104 | print "\n\n\n******\n\nWTF $device\n\n\n"; 105 | } 106 | } 107 | --------------------------------------------------------------------------------