南京邮电大学移动互联网俱乐部

关于某App外包开发心得-iOS篇(一)

  • 作者 : 林翔宇

前不久和朋友一起合作了一个外包App项目,我负责iOS和后台Server,另外两名同学合作开发Android端。现在项目已经进入收尾上架工作,我将就开发过程中的一些感想与收获进行总结。

使用CocoaPods做依赖管理以及开发用到的第三方开源项目

CocoaPods真是依赖管理神器,比Andorid的Maven或者Gradle方便多了。具体试用可以看看这篇文章唐巧-用CocoaPods做iOS程序的依赖管理

而且最近CocoaPods的Spec有了第三方国内镜像,速度快多了,为什么之前开发的时候木有,加一个库要等半天(ノДT) 。 所以我之前修改Podfile后是这样更新的(不更新Spec库并且有提示):

1
pod install --verbose --no-repo-update   

我项目里面的第三方依赖有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

    #import <AVOSCloud/AVOSCloud.h> // AVOSCloud SDK
    #import <MAMapKit/MAMapKit.h>   // 高德地图
    #import <AFNetworking/AFNetworking.h> // AFNetworking 网络通信   

    #import <UIColor+ChineseColor.h> // 自己的项目复用模块
    #import <MMCommon/MMCommon.h>  

    #import <SVProgressHUD/SVProgressHUD.h> // 加载提示的HUD

    #import <Underscore.m/Underscore.h> // Objective-C的类似JavaScript的Underscore.js的辅助库
    
    #import <UMengAnalytics/MobClick.h>  // 友盟统计和SNS集成
    #import <TencentOpenAPI/TencentApiInterface.h>
    #import <TencentOpenAPI/QQApiInterface.h>
    #import <TencentOpenAPI/TencentOAuth.h>
    #import <UMengSocial/UMSocialData.h>
    #import <UMengSocial/UMSocialSnsService.h>
    
    #import <SDWebImage/UIButton+WebCache.h> // 异步图片加载
    #import <SDWebImage/UIImage+GIF.h>
    #import <SDWebImage/UIImageView+WebCache.h>
    #import <SDWebImage/UIImageView+WebCache.h>

不得不提的是使用中。。友盟以及SNS集成过程中,不够清楚的文档以及那些坑的地方,还有调试了几天才发现是SDK悄悄升级,之前的SDK不能用的情况。

在使用的时候,为了更优雅的进行依赖管理,我也为高德地图和友盟SNS创建了Podfile并且提交到了CocoaPods/Spec库里面,需要的童鞋可以看看https://github.com/oa414/Specs/commits/master

我们同时用了AVOSCloud与友盟,这两个第三方开发者平台功能有很多重复,但是当前各有长处。未来可能迁移到其中一个。

iOS MVC与界面布局的一些实践

最初的时候,为了快速开发,全部使用了Storyboard,这让我在一个小时就把几十个页面跳转关系全部做好了。。。而且可以作为原型演示给UI童鞋和Android童鞋看。到后来,发现Storyboard管理几十个页面完全是坑,很多东西代码写更方便,并且即使是23寸的显示器在Interface Building里面拖动各个View也好麻烦。。。于是把这个Tab布局的应用按照五个Tab拆成五个StoryBoard了。主要用代码做布局和跳转,同时取巧用了StoryBoard一些方便的地方。

把各个Tab对应内容分离到不同的StoryBoard的方法:(代码好丑)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

+(UIViewController *)initViewController :(NSString *)storyBoardName
                            identifier:(NSString *)identifier
                              iconName:(NSString *)iconName
                                 withTag:(int) tag{
    
    UIViewController *vc = [SXAppDelegate viewControllerWithStoryBoard:storyBoardName identifier:identifier];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
  
    [nav.tabBarItem setImage:[SXAppDelegate getIconImage:iconName]];
    
    nav.tabBarItem.image = [[SXAppDelegate getIconImage:iconName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

    
    UIImage *selectImage = [SXAppDelegate getIconPressedImage:iconName];
    selectImage = [selectImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    DebugLog(@"select Image %@", selectImage);
    [nav.tabBarItem setSelectedImage:selectImage];
    nav.tabBarItem.selectedImage = selectImage;
    [[UITabBar appearance] setTintColor:[SXAppDelegate getNavColor:iconName]];
    
    nav.tabBarItem.tag = tag;
    
    [nav.tabBarItem setTitleTextAttributes:@{
                                            NSForegroundColorAttributeName : [SXAppDelegate getNavColor:iconName] }     forState:UIControlStateSelected];

    
    
    NSDictionary *titles = @{
        @"home" : @"首页",
        @"product":@"产品",
        @"news": @"新闻",
        @"my": @"我的",
        @"more": @"更多",
    };
    [nav.tabBarItem setTitle:titles[iconName]];
    return nav;
}


    self.tabBarController = [[UITabBarController alloc] init];
    
    self.regVC = [SXAppDelegate initViewController:@"My" identifier:@"login" iconName:@"my" withTag:3];
    self.userVC = [SXAppDelegate initViewController:@"My" identifier:@"index" iconName:@"my" withTag:3];
    self.indexVC = [SXAppDelegate initViewController:@"Main" identifier:@"index" iconName:@"home" withTag:0];
    self.productVC = [SXAppDelegate initViewController:@"Product" identifier:@"index" iconName:@"product" withTag:1];
    self.newsVC = [SXAppDelegate initViewController:@"News" identifier:@"index" iconName:@"news" withTag:2];
    self.myVC = self.regVC;
    self.moreVC = [SXAppDelegate initViewController:@"More" identifier:@"index" iconName:@"more" withTag:4];
    
    DebugLog(@"Splash  %@", [SXSplashHelper splashImage]);
    
    if ([SXUser currentUser] == nil){
        [self setRegView];
    }else {
        [self setUserView];
    }
    
    self.window.rootViewController = self.tabBarController;

有几个常用的宏可以和大家分享下:

Debug环境下输出Log与行号

1
2
3
4
#ifdef DEBUG
#define DebugLog( s, ... ) NSLog( @"[FILE]%@ %*s [LINE]%-*d [METHOD]%@ %*s [MESSAGE]%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent],30 - ([[[NSString stringWithUTF8String:__FILE__] lastPathComponent] length]),"", 5,__LINE__, NSStringFromSelector(_cmd), 75 - ([NSStringFromSelector(_cmd) length]),"", [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )

用RGB生成UIColor

1
2
3
4
#define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

获取App版本号

1
#define APP_VERSION ([[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"])

判断是否是4寸Retina屏幕

1
IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )

让你事倍功半的开发工具

我觉得有一个外接液晶屏幕很重要。。。

此外是一把比较好的椅子。。

此外,推荐大家试试http://revealapp.com/, 一款帮你轻松调试界面的App,收费软件,有试用版本,可以免去改参数-编译-点击查看对应View-改参数-编译的麻烦流程,直接在Reveal里面修改参数就能看到对应效果

iOS Developer账号的那些坑 与 提交审核

iOS Developer 账号

如果希望认真学习开发iOS App,加入iOS开发者计划是必不可少的。当初为了真机调试,又是找破解Xcode的教程又是在淘宝上买开发者证书,最后还是靠亲爱的妈妈特地办了一张Visa信用卡乖乖付钱交了99美元年费。

但是并不是乖乖叫了钱就一番风顺了。创建一个应用,你需要在开发面板上分别配置开发者证书,应用ID,调试设备,推送证书。。。并且绑定对应调试设备和应用ID。。。推送证书还分开发设备的Sandbox环境和Production环境。。。

相关具体设置可以参见苹果开发者账号那些事儿

测试分发

据说TestFlight很不错。。。最近被Apple收购了,我在使用过程中也感到各种不便。不过一些在应用内反馈给测试人员的特性还不错。

最后我发测试包用的是Fir.im,感觉还不错。另外AVOS也提供了相应的功能,值得一试。

同时我也用一个晚上时间做了一个Android APK分发的Demo,放在 Github上,希望有空能完善。

Comments