译言网 | iPhone OS 开发 – 了解并解决代码签名问题

了解并解决代码签名问题

(为保持跟开发环境以及APPLE开发者社区网站结构对应,一些名词未作翻译)

  绝大多数iPhone Developer/Distribution 的代码签名生成错误是因为证书和Provisioning profiles没有在开发系统上正确安装引起的。本文描述在开发 iPhone OS 应用程序时所需要的文件以及如何安装它们。这个技术说明为“Program User Guide”提供支持,Program User Guide 可以在 Portal Resources 下的 iPhone Developer Program Provisioning Portal 找到。如果你对 iPhone 开发刚入门,请先阅读 Program User Guide.

需要什么

  所有 iPhone 应用程序在iPhone OS设备上运行之前必须用合法的 signing identity 进行签名。

为了在自己的iPhone OS设备开发而做应用程序签名,需要:

私钥

iPhone 开发者证书

Development Provisioning profile

上 App Store 需要:

私钥

iPhone Distribution 证书

App Store Distribution Provisioning profile

要用在 Ad Hoc 上需要:

私钥

iPhone Distribution 证书

Ad Hoc Distribution Provisioning profile

  私钥是在生成认证签名请求(CSR)时创建的,在CSR提交和通过之后,可以在 iPhone Developer Program Provisioning Portal 里创建和下载证书以及provisioning profiles。

  注意:为了更好的区分不同的provisioning profile,在创建的时候注意命名,例如 "wild card dev profile", "MyApp Push distribution profile", “Ad Hoc Testers profile” 等等。

这些东西的作用

  Signing identity 由私有加密key和数字证书组成。在iPhone OS开发中,Xcode 用私钥来签署程序,这样程序就可以在iPhone OS开发设备上运行或者提交到app store。

  公钥包含 iPhone开发者/发布商证书,用来认证已签名的程序。

  Provisioning pfofiles 用来告知Xcode用哪个证书/私钥组合来签署程序。开发设备也通过它来决定如何认证安装在设备上的程序。

证书 – 安装/校验

  下载 iPhone Development 证书或者 iPhone Distribution 证书之后,双击下载的 .cer 文件启动 Keychain Access并安装证书。如果询问将证书安装到哪,请选择 "login" keychain。

  要确定证书是否正确安装,选择 "login" keychain 并且选择 "My Certificates" 类别。如果想要部署到你的开发设备,选择以"iPhone Developer Certificate"为标题开头的证书。如果要发布程序,选择以"iPhone Distribution Certificate"标题开头的证书。在 Information view 里应该有一个带选中标志的绿圈告诉你证书合法,意思就是 WWDR Intermediate Certificate 已经安装在系统里,并且证书已校验。要是看不见绿圈就应该从 iPhone Developer Program Provisioning Portal 下载 WWDR Intermediate Certificate 并且像安装iPhone Developer 或者 Distribution Certificate那样给它装上。选中的证书旁有个小三角,点击它会显示证书关联的私钥。要是没有,看看你是不是在“My Certificates”类别下。要对程序进行签名必须需要私钥,要是当前确实在"My Certificates"下并且还是没有小三角,需要作废旧证书并且创建并提交一个新的CSR来创建新的私钥和证书。

  注意:如果创建了新私钥或者证书,关联到旧证书的配置文件就失效了,需要重新生成新 pfofiles。最好在拷贝新证书之前把旧的从开发机器和设备上删除掉,这样可避免新旧证书/配置文件混淆。这么做不影响已经在app store里的程序。

  警告:安装 iPhone 证书后不要改动默认的 "Use System Defaults" 信任设置。如果信任设置不是’Use System Defaults’,在生成应用程序的时候会得到一个 CSSMERR_TP_NOT_TRUSTED 签名错误。

Provisioning Profiles – 安装/校验

  要把从iPhone Developer Program Provisioning Portal下载回来的Provisioning Profile安装到开发机器,首先需要启动 Xcode然后点击"Window > Organizer"来打开Organizer。把provisioning profile拖到标记为"Provisioning Profiles"的区域内即可。

  如果是为了开发而安装 Provisioning Profile,还需要把它装在开发设备上。在 iPhone OS 设备已经插到电脑上的时候,在 Devices 三角下选择设备,然后在Organizer里把开发provisioning profile拖进去。这就把 Provisioning profile装到 iPhone OS设备里了。

  注意:确保安装在开发设备和Mac里的 Development Provisioning完全相同对成功生成并安装测试程序是非常重要的。

  注意:跟 Development Provisioning Profiles不同,App Store Distribution Provisioning Profiles不能安装在iPhone OS设备里。用App Store Distribution Provisioning生成的程序是用来通过 iTunes Connect上传到App Store的。

  要告诉 Xcode 使用哪个 Provisioning profile,打开 Xcode 项目,打开 Target build settings,滚动到 Code Signing。在 Code Signing 区域有个带小三角的 Code Signing Identity。确定设置是为任何 iPhone OS 设备,之后在Value列点击弹出菜单并且选择要使用的 Provisioning Profile。为了开发,建议选择 "iPhone Developer",要发布,建议选择"iPhone Distribution"。这些都在弹出菜单的"Auto matic Profile Selector"区域里,这样就可以使 Xcode来为你找到正确的Provisioning Profile。

  注意:要是丢失了 iPhone devloper/Distribution证书的私钥,就无法选择证书关联到的 Provisioning Profile。从备份中导入 iPhone developer/Distribution 证书的私钥可以解决这个问题。如果丢了或者删了私钥,就得生成一个新的CSR并且下载新的 iPhone Developer/Distribution 证书和Provisioning Profile。

  程序的 Bundle Identifier 必须跟Provisioning profiles的Bundle Identifier APP ID匹配。要编辑程序的 Bundle Identifier,打开 target’s properties settings,在 Identifier 栏输入 Bundle Identifier。如果在 App id的bundle identifier里使用星号通配符,就可以替换为反向DNS格式的字符。

  下面有一些可以输入到 Identifier 框的例子。

  如果AppID是 A1B2C3D4E5.com.domainname.applicationname。输入com.domainname.applicationname 作为 Bundle Identifier

  AppID是 A1B2C3D4E5.*,输入反向DNS格式的字符串作为 Bundle Identifier。

关于 Profiles 和 App IDs

  创建 Provisioning Profiles 时可以关联两种类型的 App ID。第一个类型叫做通用App ID(Wildcard App ID)。推荐大多数iPhone OS开发用通用App ID的Provisioning Profiles,因为这样单个通用App ID可以用来生成和安装大多数程序,包括 iPhone Reference Libary 里的示例代码。通用App ID通过在  Identifier 里输入星号创建。星号必须是App ID字符串的最后一个字符。如果使用通用App ID,记着在Xcode项目里输入Bundle Identifier时把星号替换为反向DNS格式。

假设有 App ID ABCDE12345.*:

ABCDE12345是Bundle种子ID(由Apple生成)

*是App ID的Bundle Identifier。在xcode里输入bundle identifier时需要把星号替换为反向DNS格式。

假设有 App ID: ABCDE12345.com.yourcompany.*

ABCDE12345是Bundle种子ID(由Apple生成)

com.yourcompany.* 是App ID的Bundle Identifier。并且xcode项目里的bundle identifier必须以’com.yourcompany.’开头,星号可以替换成任意字串。

  第二类 App ID 称作固定AppID(Explicit App ID)。这类 APP ID限定了一个provisioning profile只允许一个程序安装。启用iPhone OS 3.0特性需要固定 App ID,例如 In App Purchase或者 App Push Notification 服务。固定ID通过输入指定字串创建。推荐做法是用反向DNS格式。

  重要:一个避免将来可能产生问题的提示:修改 yourcompany 为真正的名字。

Ad Hoc

为 Ad Hoc发布生成应用程序跟生成App Store程序类似,只是多了两步。第一,需要用 iPhone Developer Program Provisioning Portal 创建一个 Ad Hoc Distribution provisioning Profile并添加发布目标设备的 UDID。第二,创建一个代码签名Entitlements file。关于如何创建Entitlements file请阅读 iPhone 开发者指南的 Managing Application Entitlements部分。创建完 Entitlements file并把它添加到 Code Signing Entitlements build settings之后,打开EntitleMents File并且田间或编辑 get-task-allo把它设为false。

文档修订记录

(原文的修订记录)

2010-04-13:新建。介绍iPhone开发中安装证书和provisioning profiles的步骤。

Cocos2D and the new Retina iPad

Cocos2D and the new Retina iPad

Written by
March 19, 2012 @ 8:08 pm •
Filed under Ramblings

The introduction of the new iPad, a.k.a Retina-iPad, or iPad 3, has made developing universal applications supporting all of iOS devices definitely more complex. This is mostly due to the requirement of providing artwork for 4 (four!) different resolutions:

  • original iPhone: 320×480;
  • retina display (iPhone 4, and iPod Touch 4g): 640×960;
  • iPad 1/2: 768×1024;
  • retina iPad: 1536×2048.

Apple has done its best to support dealing with the different resolutions as transparently and effortlessly as possible, but still the problem remains of generating and managing 4 different versions for each piece of artwork you use in your app.

In fact, even before the new iPad was launched, things were not so easy and straigth-forward, since developers already had to provide artworks in 3 different sizes. For this reason, one common pattern to reduce effort and still provide a great user experience was to “reuse” on the iPad artworks tailored to the retina iPhone display (640×960 vs. 768×1024). This pattern was so a common one that the cocos2D community even provided some patch to the CCDirectorIOS class to directly support it “out-of’the-box”. Lately, I have created a CCDirectorIOS category encapsulating all that needs to be done to patch the director:

view sourceprint?

001.@implementation CCDirectorIOS (Retina)

002. 

003.CGFloat __ccPointScaleFactor = 1;

004. 

005.// new method call after creating the director in your AppDelegate

006.- (void)makeUniversal

007.{

008.if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

009.{

010.__ccPointScaleFactor = 2;

011.}

012.}

013. 

014.-(void) setContentScaleFactor:(CGFloat)scaleFactor

015.{

016.if( scaleFactor != __ccContentScaleFactor )

017.{

018.__ccContentScaleFactor = scaleFactor;

019. 

020.CGSize glSize = [openGLView_ bounds].size;

021.winSizeInPoints_ = CGSizeMake( glSize.width / __ccPointScaleFactor, glSize.height / __ccPointScaleFactor );

022.winSizeInPixels_ = CGSizeMake( winSizeInPoints_.width * scaleFactor, winSizeInPoints_.height * scaleFactor );

023. 

024.if( openGLView_ )

025.[self updateContentScaleFactor];

026. 

027.// update projection

028.[self setProjection:projection_];

029.}

030.}

031. 

032.-(void) updateContentScaleFactor

033.{

034.// Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html

035.if([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])

036.{

037.CGFloat scaleFactor = (__ccPointScaleFactor == 1) ? __ccContentScaleFactor : (__ccContentScaleFactor / __ccPointScaleFactor);

038.[openGLView_ setContentScaleFactor: scaleFactor];

039. 

040.isContentScaleSupported_ = YES;

041.}

042.else

043.{

044.CCLOG(@"cocos2d: WARNING: calling setContentScaleFactor on iOS < 4. Using fallback mechanism");

045.isContentScaleSupported_ = NO;

046.}

047.}

048. 

049.-(BOOL) enableRetinaDisplay:(BOOL)enabled

050.{

051.// Already enabled ?

052.if( enabled && __ccContentScaleFactor == 2 )

053.returnYES;

054. 

055.// Already disabled

056.if( ! enabled && __ccContentScaleFactor == 1 )

057.returnYES;

058. 

059.// setContentScaleFactor is not supported

060.if(! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])

061.returnNO;

062. 

063.// SD device

064.if([[UIScreen mainScreen] scale] == 1.0 && __ccPointScaleFactor == 1.0)

065.returnNO;

066. 

067.float newScale = enabled ? 2 : 1;

068.[self setContentScaleFactor:newScale];

069. 

070.returnYES;

071.}

072. 

073.-(void) reshapeProjection:(CGSize)size

074.{

075.CGSize glSize = [openGLView_ bounds].size;

076.winSizeInPoints_ = CGSizeMake(glSize.width/__ccPointScaleFactor, glSize.height/__ccPointScaleFactor);

077.winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor);

078. 

079.[self setProjection:projection_];

080.}

081. 

082.-(CGPoint)convertToGL:(CGPoint)uiPoint

083.{

084.CGSize s = winSizeInPoints_;

085.float newY = s.height - (uiPoint.y / __ccPointScaleFactor);

086.float newX = uiPoint.x / __ccPointScaleFactor;

087. 

088.returnccp( newX, newY );

089.}

090. 

091.-(CGPoint)convertToUI:(CGPoint)glPoint

092.{

093.CGSize winSize = winSizeInPoints_;

094.int newX = glPoint.x * __ccPointScaleFactor;

095.int newY = (winSize.height - glPoint.y) * __ccPointScaleFactor;

096. 

097.returnccp(newX, newY);

098.}

099. 

100.@end

Just copy/paste the above category in a file of its own and add it to your project, and magics will happen: all iPad (1/2) artwork will be shown in the higher resolution version you provided for the retina iPhone. You’ll have to bear with a couple of warnings due to calling some CCDirectorIOS private methods, but the methods are there.

Welcome Retina iPad!

The new iPad has made things better, indeed exciting, on the one hand, but worse on the other, meaning: more work to create and manage your artworks. The trick above will not be of much help, since as a side effect of having to support the new 1536x1024p resolution, what makes sense is natively supporting the 768x1024p resolution with scaled-down artworks.

One important point to understand is that existing apps that do use that trick will still work correctly on the new iPad, but they will break if you try to recompile them with the newer iOS SDK (have a look at this discussion on the cocos2d forum) without adapting them to the new retina iPad. This is something that cannot be circumvented in the end, but if you are in a hurry and need to quickly publish a new version of your app, still cannot afford to update all of its artworks now, well, there is a patch to the patch…

(The code that I am going to display here is a slight adaptation of the original one coming from Taco Graveyard blog.)

So, the <code>CCDirectorIOS</code> category becomes:

 

view sourceprint?

001.@implementation CCDirectorIOS (My)

002. 

003.&nbsp;

004. 

005.CGFloat __ccPointScaleFactor = 1;

006. 

007.&nbsp;

008. 

009.// new method call after creating the director in your AppDelegate

010. 

011.- (void)makeUniversal

012. 

013.{

014. 

015.if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

016. 

017.{

018. 

019.__ccPointScaleFactor = 2;

020. 

021.}

022. 

023.}

024. 

025.&nbsp;

026. 

027.-(void) setContentScaleFactor:(CGFloat)scaleFactor

028. 

029.{

030. 

031.if( scaleFactor != __ccContentScaleFactor )

032. 

033.{

034. 

035.__ccContentScaleFactor = scaleFactor;

036. 

037.CGSize glSize = [openGLView_ bounds].size;

038. 

039.winSizeInPoints_ = CGSizeMake( glSize.width / __ccPointScaleFactor, glSize.height / __ccPointScaleFactor );

040. 

041.winSizeInPixels_ = CGSizeMake( winSizeInPoints_.width * scaleFactor, winSizeInPoints_.height * scaleFactor );

042. 

043.if( openGLView_ )

044. 

045.[self updateContentScaleFactor];

046. 

047.// update projection

048. 

049.[self setProjection:projection_];

050. 

051.}

052. 

053.}

054. 

055.&nbsp;

056. 

057.-(void) updateContentScaleFactor

058. 

059.{

060. 

061.// Based on code snippet from: <a href="http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html">http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html<;/a>

062. 

063.if([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])

064. 

065.{

066. 

067.CGFloat scaleFactor = (__ccPointScaleFactor == 1) ? __ccContentScaleFactor : (__ccContentScaleFactor / __ccPointScaleFactor);

068. 

069.[openGLView_ setContentScaleFactor: scaleFactor];

070. 

071.isContentScaleSupported_ = YES;

072. 

073.}

074. 

075.else

076. 

077.{

078. 

079.CCLOG(@"cocos2d: WARNING: calling setContentScaleFactor on iOS &lt; 4. Using fallback mechanism");

080. 

081.isContentScaleSupported_ = NO;

082. 

083.}

084. 

085.}

086. 

087.&nbsp;

088. 

089.#ifdef CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX

090. 

091.&nbsp;

092. 

093.-(BOOL) enableRetinaDisplay:(BOOL)enabled

094. 

095.{

096. 

097.return[self enableRetinaDisplay:enabled onPad:FALSE];

098. 

099.}

100. 

101.&nbsp;

102. 

103.-(BOOL) enableRetinaDisplay:(BOOL)enabled onPad:(BOOL)onPad

104. 

105.{

106. 

107.// Already enabled ?

108. 

109.if( enabled &amp;&amp; __ccContentScaleFactor == 2 )

110. 

111.returnYES;

112. 

113.// Already disabled

114. 

115.if( ! enabled &amp;&amp; __ccContentScaleFactor == 1 )

116. 

117.returnYES;

118. 

119.// setContentScaleFactor is not supported

120. 

121.if(! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])

122. 

123.returnNO;

124. 

125.// SD device

126. 

127.CGFloat scale = [[UIScreen mainScreen] scale];

128. 

129.if(scale == 1.0 &amp;&amp; __ccPointScaleFactor == 1.0)

130. 

131.returnNO;

132. 

133.float newScale = 1;

134. 

135.if(onPad) {

136. 

137.newScale = enabled ? (scale * __ccPointScaleFactor) : 1;

138. 

139.} else{

140. 

141.newScale = enabled ? 2 : 1;

142. 

143.}

144. 

145.[self setContentScaleFactor:newScale];

146. 

147.returnYES;

148. 

149.}

150. 

151.&nbsp;

152. 

153.#else

154. 

155.&nbsp;

156. 

157.-(BOOL) enableRetinaDisplay:(BOOL)enabled

158. 

159.{

160. 

161.// Already enabled ?

162. 

163.if( enabled &amp;&amp; __ccContentScaleFactor == 2 )

164. 

165.returnYES;

166. 

167.// Already disabled

168. 

169.if( ! enabled &amp;&amp; __ccContentScaleFactor == 1 )

170. 

171.returnYES;

172. 

173.// setContentScaleFactor is not supported

174. 

175.if(! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])

176. 

177.returnNO;

178. 

179.// SD device

180. 

181.if([[UIScreen mainScreen] scale] == 1.0 &amp;&amp; __ccPointScaleFactor == 1.0)

182. 

183.returnNO;

184. 

185.float newScale = enabled ? 2 : 1;

186. 

187.[self setContentScaleFactor:newScale];

188. 

189.returnYES;

190. 

191.}

192. 

193.#endif

194. 

195.&nbsp;

196. 

197.&nbsp;

198. 

199.-(void) reshapeProjection:(CGSize)size

200. 

201.{

202. 

203.CGSize glSize = [openGLView_ bounds].size;

204. 

205.winSizeInPoints_ = CGSizeMake(glSize.width/__ccPointScaleFactor, glSize.height/__ccPointScaleFactor);

206. 

207.winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor);

208. 

209.[self setProjection:projection_];

210. 

211.}

212. 

213.&nbsp;

214. 

215.-(CGPoint)convertToGL:(CGPoint)uiPoint

216. 

217.{

218. 

219.CGSize s = winSizeInPoints_;

220. 

221.float newY = s.height - (uiPoint.y / __ccPointScaleFactor);

222. 

223.float newX = uiPoint.x / __ccPointScaleFactor;

224. 

225.returnccp( newX, newY );

226. 

227.}

228. 

229.&nbsp;

230. 

231.-(CGPoint)convertToUI:(CGPoint)glPoint

232. 

233.{

234. 

235.CGSize winSize = winSizeInPoints_;

236. 

237.int newX = glPoint.x * __ccPointScaleFactor;

238. 

239.int newY = (winSize.height - glPoint.y) * __ccPointScaleFactor;

240. 

241.returnccp(newX, newY);

242. 

243.}

244. 

245.@end

246. 

247.As you can see, there is now a conditional section that can be compiled in ifyou need it. To doso, you need to define somewhere in your header files the <code> CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX</code> symbol, e.g.:

248. 

249.<code>#define CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX @"-hdpad"</code>

250. 

251.There is one more change that you need to make. In this case, it cannot be easily factorized in a category, but you can directly patch the code in the <code>CCFileUtils.m</code> file. Here it goes:

252. 

253.[sourcecode]

254. 

255.+(NSString*) getDoubleResolutionImage:(NSString*)path

256.{

257.#ifCC_IS_RETINA_DISPLAY_SUPPORTED

258. 

259.NSString * retinaPath;

260.if( CC_CONTENT_SCALE_FACTOR() == 4 )

261.{

262.if( (retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX]) ) {

263.returnretinaPath;

264.}

265.}

266. 

267.if( CC_CONTENT_SCALE_FACTOR() == 2 )

268.{

269.if( (retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_DISPLAY_FILENAME_SUFFIX]) ) {

270.returnretinaPath;

271.}

272.}

273. 

274.#endif// CC_IS_RETINA_DISPLAY_SUPPORTED

275. 

276.returnpath;

277.}

278. 

279.+(NSString*) getPathForSuffix:(NSString*)path suffix:(NSString*)suffix {

280.NSString *pathWithoutExtension = [path stringByDeletingPathExtension];

281.NSString *name = [pathWithoutExtension lastPathComponent];

282. 

283.// check if path already has the suffix.

284.if( [name rangeOfString:suffix].location != NSNotFound ) {

285. 

286.CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, suffix);

287.returnpath;

288.}

289. 

290.NSString *extension = [path pathExtension];

291. 

292.if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] )

293.{

294.// All ccz / gz files should be in the format filename.xxx.ccz

295.// so we need to pull off the .xxx part of the extension as well

296.extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension];

297.pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension];

298.}

299. 

300.NSString *retinaName = [pathWithoutExtension stringByAppendingString:suffix];

301.retinaName = [retinaName stringByAppendingPathExtension:extension];

302. 

303.if( [__localFileManager fileExistsAtPath:retinaName] )

304.returnretinaName;

305. 

306.CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found (%@): %@", suffix, [retinaName lastPathComponent] );

307. 

308.returnnil;

309.}

In order to factorize this code in a category, we need a way to access __localFileManager. But this variable is unfortunately a static global, so it cannot be accessed from outside the compilation unit where it is defined. One workaround is reimplementing it in the category itself:

view sourceprint?

001.#ifdef CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX

002. 

003.@implementation CCFileUtils (Retina_iPad)

004. 

005.NSString *ccRemoveHDSuffixFromFile( NSString *path )

006.{

007.#ifCC_IS_RETINA_DISPLAY_SUPPORTED

008. 

009.if( CC_CONTENT_SCALE_FACTOR() == 2 ) {

010. 

011.NSString *name = [path lastPathComponent];

012. 

013.// check if path already has the suffix.

014.if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) {

015. 

016.CCLOG(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, CC_RETINA_DISPLAY_FILENAME_SUFFIX);

017. 

018.NSString *newLastname = [name stringByReplacingOccurrencesOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX withString:@""];

019. 

020.NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent];

021.return[pathWithoutLastname stringByAppendingPathComponent:newLastname];

022.} elseif( [name rangeOfString:CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) {

023. 

024.CCLOG(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX);

025. 

026.NSString *newLastname = [name stringByReplacingOccurrencesOfString:CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX withString:@""];

027. 

028.NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent];

029.return[pathWithoutLastname stringByAppendingPathComponent:newLastname];

030.}

031.}

032. 

033.#endif// CC_IS_RETINA_DISPLAY_SUPPORTED

034. 

035.returnpath;

036. 

037.}

038. 

039.+(NSString*) getDoubleResolutionImage:(NSString*)path

040.{

041.#ifCC_IS_RETINA_DISPLAY_SUPPORTED

042. 

043.NSString * retinaPath;

044.if( CC_CONTENT_SCALE_FACTOR() == 4 )

045.{

046.if( (retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX]) ) {

047.returnretinaPath;

048.}

049.}

050. 

051.if( CC_CONTENT_SCALE_FACTOR() == 2 )

052.{

053.if( (retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_DISPLAY_FILENAME_SUFFIX]) ) {

054.returnretinaPath;

055.}

056.}

057. 

058.#endif// CC_IS_RETINA_DISPLAY_SUPPORTED

059. 

060.returnpath;

061.}

062. 

063.+(NSString*) getPathForSuffix:(NSString*)path suffix:(NSString*)suffix {

064.NSString *pathWithoutExtension = [path stringByDeletingPathExtension];

065.NSString *name = [pathWithoutExtension lastPathComponent];

066. 

067.staticNSFileManager *__localFileManager = [[NSFileManager alloc] init];

068. 

069.// check if path already has the suffix.

070.if( [name rangeOfString:suffix].location != NSNotFound ) {

071. 

072.CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, suffix);

073.returnpath;

074.}

075. 

076.NSString *extension = [path pathExtension];

077. 

078.if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] )

079.{

080.// All ccz / gz files should be in the format filename.xxx.ccz

081.// so we need to pull off the .xxx part of the extension as well

082.extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension];

083.pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension];

084.}

085. 

086.NSString *retinaName = [pathWithoutExtension stringByAppendingString:suffix];

087.retinaName = [retinaName stringByAppendingPathExtension:extension];

088. 

089.if( [__localFileManager fileExistsAtPath:retinaName] )

090.returnretinaName;

091. 

092.CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found (%@): %@", suffix, [retinaName lastPathComponent] );

093. 

094.returnnil;

095.}

096. 

097.@end

098. 

099.#endif

亲爱的商界精英们,开发一个iOS应用没有那么容易 – 博客 – 伯乐在线

导读:这是来自新加坡的 iOS 开发者 Kent Nguyen 发表在1月底的一篇博文。这篇吐槽文在 iOS 开发圈子里流传甚广,从原文150多个评论就可见一斑,现翻译如下。

让我们开门见山吧:做一个iPhone应用需要花多少钱?

就是这个最常见的问题,我的很多朋友(大多是些西装革履的商务人士),还有我那些个对技术一知半解的客户们,他们都问过我这个的问题。通常,我会先给出一个大致的报价,这个报价并没有细致到需要签合同确认每一个功能点的地步。即便是这样,每当的我报价一出口,对方都毫无例外的给惊着了(当然不是因为便宜)。

说实话,我没有狮子大开口。看看StackOverflow上这个著名的帖子吧,讨论的是开发Twitterific这样一款应用需要多少钱,后来讨论范围扩展到开发一个iOS应用的合理费用范围。虽然这个帖子是在2008年发布的,而帖子的最佳答案是由一名来自Twitteriffic的开发人员于2010年回答的,但是时至今日,帖子里面讨论的数字仍然是很靠谱的,而且我预计到2012年底依然有效。而我的报价和这个帖子里面的数字比起来,简直是小巫见大巫了。

现在的趋势是,什么公司什么业务都想搞个iOS客户端,并且这种趋势在2012年看似依然火爆。所以我想起来写这篇博文,我想说一下开发一个iOS应用会碰到的各种细节问题和横生的变数,借此解释为什么iOS应用开发成本这么贵。如果你在考虑搞一个iOS应用,而你本身是搞业务而不是做技术的,如果你目前正在招标或者仅仅是想了解一下,那我这篇博会对你有帮助。当然,我说的东西并不局限于iOS应用开发,对Android、Windows Phone或者是Blackberry(如果RIM还能活的话)等移动应用平台基本上也是适用的。

伯乐在线配图)

 

开发之前需要仔细考虑的

别做拍脑瓜的决策,在开工之前你需要考虑的比你想象的要多。我通常会帮助或者指导客户把以下几个要素都过一遍:

一:和客户谈他们的移动应用,最让我吃惊的是他们从来没有想过支撑一个iPhone应用运行,背后需要涉及到的方方面面。他们想象中的iPhone是独立存在于这个宇宙的,是如此的简单,以至于他们要我很快就给出一个项目预算报价,而不用讨论诸多细节。我问他们:“你们是否考虑过后台服务器的事情?你们的应用需要和后端服务器做数据通讯?” 什么,听不懂?好吧,我用地球人的语言再把这个问题讲 一遍:“你们的应用不是需要用户注册嘛,你们考虑过把用户的数据存放在哪里了吗?我们需要一个地方去保存这些以后会用到的数据。” 第一次碰到这样的客户时,哥简直就怒了。后来我发现这不是客户的错:我是搞编程的,CS架构对我来说就像吃饭睡觉一样是不假思索的东西,而我的客户尽是些高富帅,他们懂个毛CS架构!

所以,如果你不大懂技术,那请仔细听我说:如果你想做的移动应用需要用户注册和登录,或者你想随时控制移动应用的一些输出,甚至是你仅仅是需要一个用户反馈意见调查表这么简单的功能,那么,你得搞一台后端服务器。

二:好了,现在你知道你需要一台后端服务器。同时你还需要想办法让你的iOS应用和你的服务器能够对话,就是相互间接收数据什么的。不,这个问题不是简答靠什么标准的即插即用的东东就能解决的,不是你们想象的那样!所有的东西都需要定制化开发,这就好比发明一门语言:你希望你的服务器和你的应用之间能够通过一种语言沟通,但是你不希望其他人听得懂这门语言。

用行话说这就是制定服务器端API接口,或简称API。这些API应该在开发iPhone客户端之前就到位了。为什么?因为你必须先规定好一门语言的单词和语法,然后才能用这门语言说话吧!?好了,这就带出了第三点—如何开发这些API。

三:API的成功定制是项目成功的一半(反之亦然),所以千万不要掉以轻心。你要考虑你的业务数据模型、业务流程、调用业务需要提供的参数、特定事件发生时数据间该如何互动等等。简单来说,我们要做的就是开发一个网站,上门跑着你的业务流程,只不过这个网站的所有运行结果都不是通过网页形式展现出来,而是呈现在一行行的文本和数字中。举个例子:一个登录成功的反馈页面仅仅包含YES一个单词。

iPhone应用需要访问这些预先定义好的接口,并且按预定义格式提供必要的输入(比如用户名和密码),然后要对服务器端的反馈(YES或者NO)做出解析处理。所以,没有什么移动应用能够自动的含有用户注册和登录功能。

服务器端开发需要考虑的问题太多了:选择服务器,选择用什么语言开发,主机放在哪里才能增加访问速度,等等,这里我就不展开了。如果这一切对你来说很陌生,那么你最好去问问团队里的技术负责人,或者干脆让开发人员做决策。

四: 所以,关于服务器端API,你或者让自己的技术团队把它开发好,再将完善的API文档交给iPhone应用开发人员;或者你支付iPhone应用开发人员额外的报酬来搞定这些。你找的iPhone应用开发人员可能会服务器端开发也可能不会。如果他会的话,我建议最好让他也同时负责服务器端开发,因为他最清楚iPhone应用中需要哪些服务器端API。

如果你的服务器端API已经存在了,那么除了向iPhone应用开发人员提供相关文档之外,你还要考虑让他能够便捷的同服务器开发团队沟通,因为大多数情况下,iPhone应用需要在已有API基础上增加一些新的接口。

 

现在我们来看看iPhone应用开发本身

扯了大半天,我们终于开始谈iPhone应用开发本身了。一般来说,iOS平台上做所有事情都不能随心所欲。你最好在开发人员写代码之前把所有的需求都确认好好。这和开发网站不一样,按照实现签订的合同开发iOS应用,开发过程中对需求变更的容纳度可能很低:

用户界面:无论你打算采用iOS标准界面还是自定义元素,在开发开始前一定要确认清楚,因为应用的程序架构是根据界面和用户使用流程来设计的。一个很好的例子就是在界面底部使用了iOS标准的标签栏(Tab Bar),此后如果你想让标签栏里面的图标变成彩色的,这个代码改动量可没你想象的那么小!

代码之间的耦合:如果是开发网站,你可以随意的添加一个页面或者一处链接。做iOS应用就没有那么简单了,很多东西一开始都要设计好,后期的一处改动会牵连很多东西,具体原因是你无法理解的。iOS应用的代码写好之后,再改动行不行?行!但必须小心。 这就像设计电路板一样, 如果你不小心把那根线搭错了,整块电路板就会不工作。有人说架构优良的程序可以有很高的延展性,那纯属纸上谈兵。在About屏幕上添加一个电子邮件按钮可能只需要几行代码的工作量,而添加一个转发到新浪微薄的按钮(译者注:原文是添加一个Facebook Like)就完全不是那么简单的事儿了!

让一个iPhone应用同时也支持iPad:如果要评选最坑爹“需求变更”,那么这个绝对是当之无愧的。理由很简单:支持iPad根本不是TMD什么附加功能!iPad应用基本上都比iPhone应用来得要复杂,界面设计和用户体验也大不一样。我问你,制造一辆电动自行车,然后把它改装成一部烧汽油的摩托车,这能是一回事儿吗!?电动自行车跟摩托车看起来是很像,但是制造它们完全是两码事。

拿广受欢迎的Facebook官方应用来说,它的iPhone和iPad版本看似相似,实际用户操作流程完全不同。不仅仅是界面上的不同会带来额外的工作,对后台服务器API的需求也可能不一样。拿我熟悉的一个应用Denso来说(我熟悉它因为这是我开发的),它的iPad版本比iPhone多了几个功能,这些都需要额外的服务器端API来支持。记住,iPhone和iPad应用的用户体验需求是完全不一样的。

准备好开始了吗?

希望此文能够帮助你和你的团队了解移动应用开发幕后的方方面面。除非你们要做一个像计算器那么简单的单机应用,否则你们很难用极低的成本搞定。综上所述,如果你觉得外包成本太高,那你只好招人自己开发。

当然,如果你决定了要外包移动应用开发,那么我还要提醒一点:公司政治。如果你是在一家大公司或者有着严格制度的机构里面干活,那么帮助合同开发者搞定那些个规章制度上的繁文缛节,对你来说是非常重要的一项工作,必要的时候甚至可以做一些政策上的变通。 我同几个大型企业客户接触过,当我要求看他们的服务器端数据接口的时候,他们流露出很不安的表情。我想这或许是因为他们受制于公司规定而不能透露信息,这无可厚非;或者他们还没有想好这种情况下该如何操作;或者他们的品牌制度蛋疼到需要在移动应用的每个屏幕上都摆着公司logo!最终我没有和这样的企业客户合作,因为我无法想象如果有一天我需要增加一些服务器端API接口的话,和他们的规章和流程折腾,那将会是多么悲剧的事情。

PS:开发移动应用很耗费时间,你最好有耐心。

 

英文原文:Kent Nguyen   编译:伯乐在线 – 陈远

【如需转载,请标注并保留原文链接、译文链接和译者等信息,谢谢合作!】

 

iphone开发–真机测试 – 阿P的博客 – ITeye技术网站

 

如何进行真机测试?

需要得到apple官方的development授权,过程如下

1、访问https://developer.apple.com/devcenter/ios/index.action 并登录;

2、登录后点击页面右侧的“iOS Provisioning Portal ”,如图

alt

3、进入“iOS Provisioning Portal ”后,点击左侧的“Certificates”,如图

alt

4、点击页面中的“click here to download now ”下载(这个只用下载一次即可),如图

alt

alt

5、双击下载的文件“AppleWWDRCA.cer”,就会打开“钥匙串访问”,点击左上侧的“登录”,左下侧的“证书”,如图

alt

alt

右侧就会出现下图

alt

6、焦点窗口切换为“钥匙串访问”,选择菜单“钥匙串访问”<span–<》“证书助理”<span–<》“从证书颁发机构请求证书”,如图

alt

再打开的窗口中,输入邮件地址(apple ID邮箱)及常用名称(貌似没有什么要求,应该是随便输入的),选择“存储到磁盘”及“让我指定钥匙串信息”,如图

alt

点击“继续”,弹出保存框,选择保存在桌面,在新的窗口中点击“继续”,会生成“CertificateSigningRequest.certSigningRequest”文件。

7、切回浏览器,在“Development ”选项卡中点击“Request Certificate”按钮,如图

alt

8、在新的页面中点击“选择文件”,选择刚刚生成的“CertificateSigningRequest.certSigningRequest”文件并点击“submit”,如图

alt

9、进入页面

alt

F5刷新,直到出现页面

alt

点击“Download”按钮,下载到“ios_development.cer”文件,双击该文件会在“钥匙串访问”中出现如图

alt

注意,若没有出现图中的钥匙

alt

则可能是中间操作有问题,后面应该会不成功。

10、切回浏览器,选择“Devices”,如图

alt

11、在页面中点击右侧的alt按钮,在出现的页面中输入设备名称(随便起个名字)及手机唯一码

获取手机唯一码方式:当手机插入到mac上时,打开xcodeorganizer,选中devices,就会出现***’s iPhone4S或者其他名称,选中机器就会在右侧出现,如下图,红框内的就是唯一码

alt

12、在浏览器中,选择“App IDs”,如图

alt

13、点击右侧的alt按钮,Description中输入一个名称(随便,但最好特定),在Bundle Identifier (App ID Suffix)中输入appBundle identifier,如:com.abc.iphone

14、在左侧选择“Provisioning”,如图

alt

15、在右侧点击alt按钮

16、页面进入“Create iOS Development Provisioning Profile”,Profile Name是名称(随便,但最好特定)Certificates选择一个证书即可,App ID选择你刚刚创建的app idDevices选择你添加的Device。点击submit你创建的provisioning profilestatus会是pending 刷新页面,知道statusActive,点击右侧的alt按钮 下载*****.mobileprovision,并双击 添加到xcodeProvisioning Profilesorganizer中选中你的手机dervice展开,选择Provisioning Profiles 点击右侧窗口下的alt按钮,选择到刚才下载的“*****.mobileprovision”文件,就会对该手机授权

17、配置app,使之可以在手机上调试,如下图

alt

如果右侧红框内的授权信息不能选择,则说明中途操作有问题。重新操作吧,我弄了5次才搞好。如下图

alt选择查到mac上的手机(写文档时,手机不在,故没有iphone真机的名称),就可以在手机上调试了。

 

 

PS:可能有些细节没有写清楚,自己做的时候细心些,之后再做的话就简单多了。

再PS:在iteye上发图文并茂的文章很难!!

Cocos2D from iPhone to iPad

Q:

I’m developing a game using cocos2d. It’s running now on iPhone and also supports Retina display. With retina i’m using images with "-hd" postfix. Now i want my application to be able to work on iPad using this images. How can i do that ?

PS: I don’t have a real iPad and can only use a simulator. I will test the game on my friend’s iPad after it will work well on a simulator. Is it possible to use hi-res images with simulator and how to do it?

I’m using cocos2D 0.99.5

A:

inside CCFileUtils.m

inside method name +(NSString*) getDoubleResolutionImage:(NSString*)path

edit if( CC_CONTENT_SCALE_FACTOR() == 2) to if( CC_CONTENT_SCALE_FACTOR() == 2 || (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad))

this will make the app to load the -hd image when running on ipad, remember to change your app targeted device family to iPhone/iPad.

this will solve the loading of -hd problem for ipad.

but after loading -hd file for ipad resolution, you will also notice that most of your image position should be off alignment, now either you go directly into the cocos2d folder to edit how CCNode,CCSprite,CClabel handle the positioning of thing or you code it in your code checking if its ipad or not.

if you are using CCSpriteBatchNote. this will also cause another problem because you will be reading from the -hd SpriteSheet. so the texture Rect u set to cut need to be double too.

another method is position everything according to winsize. i haven try this method.

iPhone & iPad apps with special url shortcuts – App Lookup

A feature of the iPhone SDK allows you to bind your application to a custom url scheme. This allows others to trigger the opening of your app and opens the possibilities of passing data between apps.

Here’s a quick tutorial on how to update your Info.plist to register your own url.

List of iPhone apps with custom url shortcuts along with instructions on how to use them.

Safari

Phone Call

SMS / Text Message

  • sms:123456789There is no // in the SMS scheme.
  • Note: Currently no method to specify a body for the message. Update: IOS4 allows you to present the SMS composition screen and pre fill the body.

Send an Email

  • mailto:foo@example.com?cc=bar@example.com&subject=Greetings&body=Wish%20you%20were%20here!

AppStore

Like YouTube and Google Maps, certain http urls get hijacked by the build in Apple apps. This will open the Appstore app.

  • http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=291586600&mt=8

Maps

  • Launch the mapping app – maps://
  • See this document from Apple on linking to specific locations

Feed Reader

  • feed:// launches http://reader.mac.com in Safari

Twitteriffic / Twitteriffic Premium

A Twitter client by The Icon Factory

  • Launch – twitterrific://
  • Start a tweet – twitterrific:///post?message="message body"

Twinkle

Twitter client by Tapulous

  • Launch – twinkle://

Twitter for iPhone

Twitter client from Twitter

  • Launch – twitter://
  • Start tweet with shortened url – twitter://http://your.url.com

Twitterfon

Twitter client by

Credit Card Terminal

Online CC processing by InnerFence

This is one of the first iphone apps to provide a return path when it is invoked. See this post on their blog for more details and some security caveats.

Yummy

by Stephen Darlington

Appigo Todo

Todo list management by Appigo

Appigo Notebook

Notepad by Appigo

Darkslide (formerly Exposure)

Flickr viewer by Connected Flow

AirSharing

by Avatron

Portscan

Portscanner by Digital Sirup

  • Launch Portscanner – portscan://
  • Launch a scan – portscan://127.0.0.1
  • Launch a scan – portscan://host.name

ChatCo

English/Japanese IRC client

  • irc://irc.example.domain/roomName
  • irc://irc.example.domain/Nickname@roomName
  • irc://irc.example.domain/Nickname!Realname@roomName

Eureka

Wikipedia Viewer by Keishi Hattori

Wikiamo

Wikipedia viewer by Satoshi Nakagawa
First one seen with 2 url schemes registered

Ships² Viewer

Game by Universalis

Geopher Lite

Geocaching app by DadGuy

Google Earth

by Google

Alocola

by Dan Grigsby / Mobile Orchard

An iPhone location web-helper application

Kindle

by Amazon

Amazon Kindle book reader

UniDict Engine & SpellChecker

by Enfour

Enfour has a number of apps that support round-trip URLs -add the sender app’s URL to the ‘Back’ button.

“UniDict Engine” (used for dictionaries like “American Heritage” & “Oxford Deluxe“) allows lookups from other applications as well as links between dictionaries.

SpellChecker” also has round trip URLs. This lets apps send text to be checked with the results sent back to the app that made the call.

Skype

by Skype

VOIP Calls and Chat

Ebay

by Ebay

Auctions

Facebook

by Facebook

Social Networking

EirText Pro

by Vincent Coyne –

SMS Client

iVersion

by Ben Reeves

Fully featured SVN Client for iPhone and iPod Touch

Round Tuit

by Leigh McCulloch

Use Round Tuit to keep a quick and easy list of things you need to get done. Add new items, delete items and order your items according to what you need to do next and make a productive difference to your day.

Gowalla

by Alamofire, Inc

Share your experiences through Gowalla with your Facebook & Twitter friends

Print Magic

by Wellala Inc

“Print Magic is the first application for the Apple iPhone & iTouch products, which allows for wireless printing through IPP on the latest OS system”

  • Launch with print:// Data in the pasteboard will be printed.

Birdfeed

by BirdFeed

Twitter Client

MyAppSales

by Dr. Touch

Download your iTunes Connect reports. (Not available from the AppStore, sold as source fo you to build and run via your developer account)

Sounds real (sound bank)

by Série Blanche

Sounds real (sound bank)

Simply Tweet

by Hwee Boon Yar

Twitter client with push notifications

Echofon

by Naan Studio

Twitter client

Good Reader

by Good.iWare Ltd

Download files for use offline

  • Launch from another app gropen:// (help)
  • Download files ghttp://, ghttps://, grhttp://, grhttps://, giwhttp:// or giwhttps:// (help)

Pocket Universe

by John Kennedy

Pocket Universe supports the following options (more to come), which makes it possible to link to planets or points in the sky from a web page and then ’see’ them in the app’s planetarium mode. Motion tracking mode must be off for these to function as expected.

Years

by Zicron Software

Years replaces the year calendar in your paper diary with an elegant digital year calendar. The Years custom URL scheme allows other apps or web pages to launch Years and optionally select a date.

Add more in the comments and I’ll integrate them asap.