Jun 20

自动分发,一键安装v2版,部署更简单,多版本同时显示

建议先阅读第一版本,了解一下流程。本文只讲解主要配置流程。

这次版本需要安装python,mac os自带,所以用本机做分发的不需要担心。

runserver.py
这次服务器部置只要一个py文件就可以完全搞定。

py文件中只需要修改这几行

host = "http://192.168.1.188:8080/"#服务器ip
ipaname = "InstaSoccer"#与xcode打包时代码的ipa名相同
bundleid = "com.minroad.appid"#bundle id
appname = "一键安装" #app name

在xcode里run script里面加入如下代码,scp是将ipa上传到与py文件同一目录下

# Date: 2013-06-20
# Author: Seamus
# Sina Weibo: @qdvictory

# compress application.
if [ "${CONFIGURATION}" = "ad_hoc" ]; then

#.app名,默认与target name一致
ipaname="InstaSoccer"
#工程所在目录为根目录
pathtoartwork="isoccer/icon/iTunesArtwork"
#scp上传用户名及ip
sshhost="sshuser@192.168.1.188"


/bin/mkdir $CONFIGURATION_BUILD_DIR/Payload

/bin/cp -R $CONFIGURATION_BUILD_DIR/${ipaname}.app $CONFIGURATION_BUILD_DIR/Payload

/bin/cp ${pathtoartwork} $CONFIGURATION_BUILD_DIR/iTunesArtwork

cd $CONFIGURATION_BUILD_DIR

# zip up the Instasoccer directory

/usr/bin/zip -r ${ipaname}.ipa Payload iTunesArtwork

rm -R $CONFIGURATION_BUILD_DIR/Payload

/usr/bin/scp ${ipaname}.ipa ${sshhost}:~/ipa_publish/${ipaname}_`date +%Y%m%d%H%M`_$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion $REV" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}").ipa

rm ${ipaname}.ipa

fi

exit 0

如果想要下载时有图标的话,在py文件同目录下添加Icon.png文件就可以

接下来,启动Server

nohup python runserver.py 8080 > /dev/null 2>&1 &

手机访问服务器ip,一切ok
Screenshot 2013.06.20 11.38.21

Jun 19

iOS测试包自动分发,一键安装,效率提高百分百

一个可以让你快速、方便、一次配置,终生受益的测试包分发教程。你还在傻傻的用airdrop,qq么?

使用环境:

适合iOS开发者,常需要发布测试包给各类人员,那么以后再也无需多余操作,一键搞定。公司有内网服务器,或用Mac os的同学都可以使用。非越狱手机可以使用,只要正常绑定过证书就没有问题。

以下是教程,相当简单。
服务器ip以192.168.1.188为例,端口8080

第一步,配置run script打包ipa并完成ipa上传部署过程,Xcode中打开target->build phases->add build phase->add run script如图添加如下代码,并根据自己使用环境做一下调整。
屏幕快照 2013-06-19 10.25.49 AM

# shell script goes here

# compress application.
if [ "${CONFIGURATION}" = "ad_hoc" ]; then #判断发布版本

/bin/mkdir $CONFIGURATION_BUILD_DIR/Payload

/bin/cp -R $CONFIGURATION_BUILD_DIR/InstaSoccer.app $CONFIGURATION_BUILD_DIR/Payload

/bin/cp isoccer/icon/iTunesArtwork $CONFIGURATION_BUILD_DIR/iTunesArtwork

cd $CONFIGURATION_BUILD_DIR

# zip up the Instasoccer directory

/usr/bin/zip -r InstaSoccer.ipa Payload iTunesArtwork

/usr/bin/scp InstaSoccer.ipa sshuser@192.168.1.188:~/ipa_publish/ #scp到服务器路径,如果用Mac本机开启服务器,可以用cp到webserver路径
fi
exit 0

第二步,部署服务器。可以用Mac os的Web共享,也可以自己用python开一个,当然也可以用内网服务器、外网服务器,要求极低,扔几个静态文件就可以。
关于Mac os 10.8在偏好设置里面已经没有了Web共享,要开启的话需要手动写一下配置文件,方法请自搜。
样例下载
样例中有2个文件,index.html和Info.plist
index.html修改一处,“http://192.168.1.188:8080/Info.plist” 改为你相应的路径

<html>
<head>
<meta charset="utf-8" />
<title>Minroad一键安装</title>
</head>
<a style="font-size: 5em;" href="itms-services://?action=download-manifest&url=http://192.168.1.188:8080/Info.plist">install</a>
<html>

Info.plist,修改ipa路径(如果你用scp的话请查看你scp后的路径是否与之相同),icon,版本号,bundle id,程序名

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>items</key>
	<array>
		<dict>
			<key>assets</key>
			<array>
				<dict>
					<key>kind</key>
					<string>software-package</string>
					<key>url</key>
					<string>http://192.168.1.188:8080/InstaSoccer.ipa</string>
				</dict>
				<dict>
					<key>kind</key>
					<string>display-image</string>
					<key>needs-shine</key>
					<true/>
					<key>url</key>
					<string>http://192.168.1.188:8080/Icon.png</string>
				</dict>
				<dict>
					<key>kind</key>
					<string>full-size-image</string>
					<key>needs-shine</key>
					<true/>
					<key>url</key>
					<string>http://192.168.1.188:8080/Icon.png</string>
				</dict>
			</array>
			<key>metadata</key>
			<dict>
				<key>bundle-identifier</key>
				<string>com.minroad.appid</string>
				<key>bundle-version</key>
				<string>2.8.2</string>
				<key>kind</key>
				<string>software</string>
				<key>subtitle</key>
				<string>一键安装副标题</string>
				<key>title</key>
				<string>一键安装程序名</string>
			</dict>
		</dict>
	</array>
</dict>
</plist>

然后在启动webserver, 方法多了去了,提供一个python的,Mac os也可以用

cd 到当前目录
nohup python -m SimpleHTTPServer 8080 > /dev/null 2>&1 &

此时,用你的ios设置访问网址,本例中是http://192.168.1.188:8080,会出现如下内容
Screenshot 2013.06.19 10.55.23

Screenshot 2013.06.19 10.55.34

点击安装就自动安装了。省心省力!只要将网址收藏,以后分发的事与开发人员就无关咯

转贴请注明出处:http://www.minroad.com/?p=688

Jun 03

app store版本比较算法

介于app store的算法不是单纯的使用数值数值比较,下面是我自己写的方法
原来系统有api的,惭愧,多亏@TraWor 提醒

- (BOOL)version:(NSString *)_oldver lessthan:(NSString *)_newver //系统api
{
    if ([_oldver compare:_newver option:NSNumbericSearch] == NSOrderedAscending)
    {
         return YES;
    }
    return NO;
}
- (BOOL)version:(NSString *)_oldver lessthan:(NSString *)_newver
{
    NSArray *a1 = [_oldver componentsSeparatedByString:@"."];
    NSArray *a2 = [_newver componentsSeparatedByString:@"."];
    
    for (int i = 0; i < [a1 count]; i++) {
        if ([a2 count] > i) {
            if ([[a1 objectAtIndex:i] floatValue] < [[a2 objectAtIndex:i] floatValue]) {
                return YES;
            }
            else if ([[a1 objectAtIndex:i] floatValue] > [[a2 objectAtIndex:i] floatValue])
            {
                return NO;
            }
        }
        else
        {
            return NO;
        }
    }
    return [a1 count] < [a2 count];
}
May 30

xcode更新出现不能编译release包的解决办法

之前打包都没问题,突然间出现了不能打包的问题。
先来几个常规方法,如果还是不行再尝试后面的方法
1.clean build (command+shift+k)
2.delete derived data (windows->organizer->projects)
3.重启xcode
4.重启电脑

以上方法不行之后再试如下方法:

我报的是dsymutil error,大概意思就是生成dsym文件的命令行丢了。解决办法有2个:

1.如图所示,直接关掉。(不推荐使用)
2.菜单->xcode->preferences->downloads->components->command line tools 安装命令行工具

然后清空derived date 和 缓存,重新编译应该就OK了。

Apr 15

帮女友推一下她做的App,一个从事HR工作的人,一样可以设计App。(当然代码是我搞定的)——呼噜熊睡前故事

这个App可以说是汇集了我女友几个月的心血,她只是想为孩子们做点事,讲点故事。所以我自然大力支持她了。从她开始做到成型到上线,给我上了好几课。

讲讲呼噜熊故事的开发历程吧。

1.怎么会想到做这个:因为她晚上就喜欢听故事,也许大多数人做app的初衷都是自己需要。
2.从哪动工:开始的时候,我根本都没有当回事,以为她是说着玩,同时程序员拖延症,基本没动弹过,直到东西摆在眼前,不得不动工。此时,她已经用iPhone录好了好几十首。我们也是从现在开始。
3.不会真的要学:在我心目中,她简直是一个文盲,意思就是只会玩玩QQ和office,从来就没见过她干过别的,安装游戏都不会。而现在可以用Audition编辑声音,学习没有门槛(当然少不了我在旁指点)。
4.精益求精的心:说实话,做这个程序我没被少挨骂,总是能偷懒就偷懒,结果最后还是得完成她的需求(苦逼啊,需求总在变)。
5.坚持和任何都没有关系:以前从来没做过app的人,把app做出来了,即使我有帮忙,这句话我也彻底相信了。
6.你的好坏与别人无关,就像别人发财亏损你漠不关心一样:自己努力做了,走在路上,就好。

附上呼噜熊故事的App Store下载地址,欢迎试用:
https://itunes.apple.com/cn/app/hu-lu-xiong-shui-qian-gu-shi/id627421573?mt=8

Mar 01

制作.webloc文件的方法

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>URL</key>
    <string>http://minroad.com</string>
</dict>
</plist>

保存成.webloc文件,打开时自动会打开网页~

Mar 01

Xcode已经不再集成Icon Composer,不用Icon Composer一样制作Icon

要想再用,只能去Xcode->Open Develop Tool->More Develop Tools->下载GraphicsTools,里面会包含Icon Composer。
但是我们不能让下载阻挡我们使用的脚本,虽然下载是必要的,但是在急用的时候,下面的方法才是正路!

1.新建一个文件夹,里面添加如下的PNG文件,尺寸看名字。
icon_16x16.png
icon_16x16@2x.png
icon_32x32.png
icon_32x32@2x.png
icon_128x128.png
icon_128x128@2x.png
icon_256x256.png
icon_256x256@2x.png
icon_512x512.png
icon_512x512@2x.png

2.给文件夹添加扩展名 .iconset

3.打开终端,运行命令

iconutil -c icon.icns <path to .iconset file>

输出的icon.icns就是Icon Composer输出的图标文件。

Nov 20

iOS上日期格式转换

- (NSString *)convertDateFromFormat:(NSString *)_f1 toFormat:(NSString *)_f2
{
    NSLocale * enUSPOSIXLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease];
	NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
	[inputFormatter setDateFormat:_f1];
    [inputFormatter setLocale:enUSPOSIXLocale];
	NSDate *formatterDate = [inputFormatter dateFromString:self];//因为是用category实现的,self就是源时间string
	
	NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
    [outputFormatter setDateStyle:NSDateFormatterShortStyle];
    [outputFormatter setTimeStyle:NSDateFormatterShortStyle];
	[outputFormatter setDateFormat:_f2];
	NSString *result = [outputFormatter stringFromDate:formatterDate];
	
	[inputFormatter release];
	[outputFormatter release];
	
	return result;
}
Nov 20

iOS上将时间转换成当前时区

- (NSString *)convertDateToCurrLocalWithFormat:(NSString *)_format
{
	NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
	[inputFormatter setDateFormat:_format];
    [inputFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"Europe/Copenhagen"]];//设置源时间时区
	NSDate *formatterDate = [inputFormatter dateFromString:self];//因为是用category实现,self就是源时间string
    

	
	NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
    [outputFormatter setTimeZone:[NSTimeZone localTimeZone]];
    [outputFormatter setDateStyle:NSDateFormatterShortStyle];
    [outputFormatter setTimeStyle:NSDateFormatterShortStyle];
	[outputFormatter setDateFormat:_format];
	NSString *result = [outputFormatter stringFromDate:formatterDate];
	
	[inputFormatter release];
	[outputFormatter release];
    
    return result;
}
Nov 20

iOS上缓存文件有效期的实现

@implementation NSString (NSString_PathValid)
- (int)Interval
{
	NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self error:nil];
	//NSLog(@"create date:%@",[attributes fileModificationDate]);
	NSString *dateString = [NSString stringWithFormat:@"%@",[attributes fileModificationDate]];
	
	
	NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
	[inputFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
	
	NSDate *formatterDate = [inputFormatter dateFromString:dateString];
	
	
	unsigned int unitFlags = NSDayCalendarUnit;
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *d = [cal components:unitFlags fromDate:formatterDate toDate:[NSDate date] options:0];
	
	[inputFormatter release];
	
	//NSLog(@"%d,%d,%d,%d",[d year],[d day],[d hour],[d minute]);
	
	int result = [d day];
	
    //	return 0;
	return result;
}

- (BOOL)isValid
{
	if ([self Interval] < VALIDDAYS) { //VALIDDAYS = 有效时间天数
		return YES;
	}
	return NO;
}
@end
NSString *path = <filePath>;
if ([path isValid]){
// 有效
}
else{
// 无效
}