美文网首页iOS实用iOS Development进阶
iOS开发实战-上架AppStore 通过内购和广告获得收益

iOS开发实战-上架AppStore 通过内购和广告获得收益

作者: gwk_iOS | 来源:发表于2017-02-02 17:53 被阅读2365次

    写在前前面
    弄了下个人站...防止内容再次被锁定...所有东西都在这里面
    welcome~
    个人博客

    写在前面

    由于一些原因需要离职,准备重回大上海

    忽然发现手头上也没什么独立App,那就随便写个放到AppStore上吧,凑个数吧。哈哈哈。

    这个App是无聊找配色的时候看到的一套图

    原设计图.png

    正好春节在家没什么特别的事,编码用了半天左右吧,数据录入倒是也用了半天,于是就变成了这样。

    ios版.png 自定义.png 收藏.png

    上架的时候再做点效果图配点文字 就搞定了。
    不得不说 我是白天提交的,到晚上就Review了
    立马就通过了变 ready for sale了。。。

    e-mail

    可能是App太过于简单了。也太好过审了。。。

    广告版集成了google的Admob (需要搭梯子)不过测试发现模拟器能正常显示真机加了设备id也不能显示,经常空加载。。
    最近申请了腾讯的广告 广点通 提交了新的版本。就是注册审核需要过个一两天,关联个APP要个一两天。不过效果还是有的。
    2.6提交的 ,今天(2.7)正式过审,就有收益了,估计都是自己和apple测试的时候展示的。

    来💰了

    大家可能也看到了,这是个很简单的App,无非就是几个列表展示下分类的颜色和收藏的颜色,担心功能太单一,所以又添加了自定义色。下面我们来讲 项目 Demo吧。

    效果

    效果

    分析

    感觉都没什么好分析的了 ,就是个tableview自定义cell就行了 这里我直接用xib设置约束了

    自定义cell

    每个色块有3个btn btn的颜色都是从plist中读取,所以手工录入还是挺耗时间的。

    plist数据样板

    自定义颜色方面 直接获取Touches的值做下计算

    代码部分

    这里就贴一个自定义颜色部分。通过block回调选择颜色的RGB值

    #import <UIKit/UIKit.h>
    
    @interface WSColorImageView : UIImageView
    @property (copy, nonatomic) void(^currentColorBlock)(UIColor *color,NSString *rgbStr);
    @end
    
    #import "WSColorImageView.h"
    
    @interface WSColorImageView()
    @property (nonatomic,copy)NSString *rgbStr;
    
    @end
    @implementation WSColorImageView
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.width)];
        if (self) {
            
            self.image = [UIImage imageNamed:@"palette"];
            self.userInteractionEnabled = YES;
            self.layer.cornerRadius = frame.size.width/2;
            self.layer.masksToBounds = YES;
            
        }
        return self;
    }
    
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
        
        UITouch *touch = [touches anyObject];
        CGPoint pointL = [touch locationInView:self];
        
        if (pow(pointL.x - self.bounds.size.width/2, 2)+pow(pointL.y-self.bounds.size.width/2, 2) <= pow(self.bounds.size.width/2, 2)) {
            
            UIColor *color = [self colorAtPixel:pointL];
            if (self.currentColorBlock) {
                
                self.currentColorBlock(color,self.rgbStr);
            }
        }
    }
    
    
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
        
        UITouch *touch = [touches anyObject];
        CGPoint pointL = [touch locationInView:self];
        
        if (pow(pointL.x - self.bounds.size.width/2, 2)+pow(pointL.y-self.bounds.size.width/2, 2) <= pow(self.bounds.size.width/2, 2)) {
            
            UIColor *color = [self colorAtPixel:pointL];
            
            if (self.currentColorBlock) {
                
                self.currentColorBlock(color,self.rgbStr);
            }
        }
    }
    
    
    
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
        UITouch *touch = [touches anyObject];
        CGPoint pointL = [touch locationInView:self];
        
        if (pow(pointL.x - self.bounds.size.width/2, 2)+pow(pointL.y-self.bounds.size.width/2, 2) <= pow(self.bounds.size.width/2, 2)) {
            
            UIColor *color = [self colorAtPixel:pointL];
            
            if (self.currentColorBlock) {
                
                self.currentColorBlock(color,self.rgbStr);
            }
        }
    }
    
    
    
    //获取图片某一点的颜色
    - (UIColor *)colorAtPixel:(CGPoint)point {
        if (!CGRectContainsPoint(CGRectMake(0.0f, 0.0f, self.image.size.width, self.image.size.height), point)) {
            return nil;
        }
        
        NSInteger pointX = trunc(point.x);
        NSInteger pointY = trunc(point.y);
        CGImageRef cgImage = self.image.CGImage;
        NSUInteger width = self.image.size.width;
        NSUInteger height = self.image.size.height;
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        int bytesPerPixel = 4;
        int bytesPerRow = bytesPerPixel * 1;
        NSUInteger bitsPerComponent = 8;
        unsigned char pixelData[4] = { 0, 0, 0, 0 };
        CGContextRef context = CGBitmapContextCreate(pixelData,
                                                     1,
                                                     1,
                                                     bitsPerComponent,
                                                     bytesPerRow,
                                                     colorSpace,
                                                     kCGImageAlphaPremultipliedLast |     kCGBitmapByteOrder32Big);
        CGColorSpaceRelease(colorSpace);
        CGContextSetBlendMode(context, kCGBlendModeCopy);
        
        CGContextTranslateCTM(context, -pointX, pointY-(CGFloat)height);
        CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage);
        CGContextRelease(context);
        
        CGFloat red   = (CGFloat)pixelData[0] / 255.0f;
        CGFloat green = (CGFloat)pixelData[1] / 255.0f;
        CGFloat blue  = (CGFloat)pixelData[2] / 255.0f;
        CGFloat alpha = (CGFloat)pixelData[3] / 255.0f;
        
        NSLog(@"%f***%f***%f***%f",red,green,blue,alpha);
        NSLog(@"%d,%d,%d",pixelData[0],pixelData[1],pixelData[2]);
        self.rgbStr = [NSString stringWithFormat:@"%d,%d,%d",pixelData[0],pixelData[1],pixelData[2]];
        return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
    }
    
    - (void)setImage:(UIImage *)image {
        UIImage *temp = [self imageForResizeWithImage:image resize:CGSizeMake(self.frame.size.width, self.frame.size.width)];
        [super setImage:temp];
    }
    
    - (UIImage *)imageForResizeWithImage:(UIImage *)picture resize:(CGSize)resize {
        CGSize imageSize = resize; //CGSizeMake(25, 25)
        UIGraphicsBeginImageContextWithOptions(imageSize, NO,0.0);
        CGRect imageRect = CGRectMake(0.0, 0.0, imageSize.width, imageSize.height);
        [picture drawInRect:imageRect];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        return image;
    }
    @end
    
    

    补充

    给免费版添加了内购去广告功能。
    使用的是本地数据库,自定义tableview的footview。
    未购买标识为0,广告位的frame高设为44;
    购买成功就将标识设为1,广告位frame高设为0;
    都是tableview直接reload。

    这里再给出内购的代码。

    注意:设置成订阅类商品(非消耗)一定要添加恢复购买的代码 不然审核会被拒

    #import <StoreKit/StoreKit.h>
    @interface ColorFavTableViewController ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
    #pragma mark - 内购
    - (IBAction)clickPhures:(id)sender {
    
    //    [[CoreDataOperations sharedInstance] buyNoAds];
    //    [self.tableView reloadData];
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"花费6元购买去广告" preferredStyle:UIAlertControllerStyleActionSheet];
        [alertController addAction:[UIAlertAction actionWithTitle:@"确定购买" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
    
            NSString *product = @"123";
            _currentProId = product;
            if([SKPaymentQueue canMakePayments]){
                [self requestProductData:product];
            }else{
                NSLog(@"不允许程序内付费");
            }
        }]];
        
        [alertController addAction:[UIAlertAction actionWithTitle:@"已购买直接去广告" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
            
            [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
        }]];
        
        [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
        [self presentViewController:alertController animated:YES completion:nil];
        
    }
    
    
    //请求商品
    - (void)requestProductData:(NSString *)type{
        NSLog(@"-------------请求对应的产品信息----------------");
        
        
        NSArray *product = [[NSArray alloc] initWithObjects:type,nil];
        
        NSSet *nsset = [NSSet setWithArray:product];
        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
        request.delegate = self;
        [request start];
        
    }
    
    //收到产品返回信息
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
        
        NSLog(@"--------------收到产品反馈消息---------------------");
        NSArray *product = response.products;
        if([product count] == 0){
            NSLog(@"--------------没有商品------------------");
            return;
        }
        
        NSLog(@"productID:%@", response.invalidProductIdentifiers);
        NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
        
        SKProduct *p = nil;
        for (SKProduct *pro in product) {
            NSLog(@"%@", [pro description]);
            NSLog(@"%@", [pro localizedTitle]);
            NSLog(@"%@", [pro localizedDescription]);
            NSLog(@"%@", [pro price]);
            NSLog(@"%@", [pro productIdentifier]);
            
            if([pro.productIdentifier isEqualToString:_currentProId]){
                p = pro;
            }
        }
        
        SKPayment *payment = [SKPayment paymentWithProduct:p];
        
        NSLog(@"发送购买请求");
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    
    //请求失败
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
        NSLog(@"------------------错误-----------------:%@", error);
    }
    
    - (void)requestDidFinish:(SKRequest *)request{
        NSLog(@"------------反馈信息结束-----------------");
    }
    //沙盒测试环境验证
    #define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
    //正式环境验证
    #define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
    /**
     *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
     *
     */
    -(void)verifyPurchaseWithPaymentTransaction{
        //从沙盒中获取交易凭证并且拼接成请求体数据
        NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
        NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];
        
        NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串
        
        NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接请求数据
        NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
        
        
        //创建请求到苹果官方进行购买验证
        NSURL *url=[NSURL URLWithString:AppStore];
        NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
        requestM.HTTPBody=bodyData;
        requestM.HTTPMethod=@"POST";
        //创建连接并发送同步请求
        NSError *error=nil;
        NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
        if (error) {
            NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
            return;
        }
        NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
        NSLog(@"%@",dic);
        if([dic[@"status"] intValue]==0){
            NSLog(@"购买成功!");
            [[CoreDataOperations sharedInstance] buyNoAds];
            [self.tableView reloadData];
    
            NSDictionary *dicReceipt= dic[@"receipt"];
            NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
            NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识
            //如果是消耗品则记录购买数量,非消耗品则记录是否购买过
            NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
            if ([productIdentifier isEqualToString:@"123"]) {
                int purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量
                [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
            }else{
                [defaults setBool:YES forKey:productIdentifier];
            }
            //在此处对购买记录进行存储,可以存储到开发商的服务器端
        }else{
            NSLog(@"购买失败,未通过验证!");
        }
    }
    //监听购买结果
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
        
        
        for(SKPaymentTransaction *tran in transaction){
            
    
            switch (tran.transactionState) {
                case SKPaymentTransactionStatePurchased:{
                    NSLog(@"交易完成");
                    [self verifyPurchaseWithPaymentTransaction];
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                    [[CoreDataOperations sharedInstance] buyNoAds];
                    [self.tableView reloadData];
                    
                }
                    break;
                case SKPaymentTransactionStatePurchasing:
                    NSLog(@"商品添加进列表");
                    
                    break;
                case SKPaymentTransactionStateRestored:{
                    NSLog(@"已经购买过商品");
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                    [[CoreDataOperations sharedInstance] buyNoAds];
                    [self.tableView reloadData];
                }
                    break;
                case SKPaymentTransactionStateFailed:{
                    NSLog(@"交易失败");
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                    break;
                default:
                    break;
            }
        }
    }
    
    //交易结束
    - (void)completeTransaction:(SKPaymentTransaction *)transaction{
        NSLog(@"交易结束");
        
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
    
    
    - (void)dealloc{
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
    
    

    Demo地址

    广告版免费,收费版1元,有兴趣的可以下来玩玩
    当然,Github公开无广告版的源码,欢迎点赞加星

    相关文章

      网友评论

      • lloveyouyou:请问楼主,iOS可以用第三方的广告SDK,加载到广告数据后自己组织UI然后渲染吗
        gwk_iOS:原生广告了解下
      • JohnayXiao:请问一下楼主,你的Banner广告位为什么是多个?可以共用一个吗?如果共用一个,那跟每个界面单独一个有什么区别?
        JohnayXiao:@gwk_iOS 嗯,不过它统计点击次数的时候有点坑,我明天明明点了20多次,结果才显示点11次,我是隔两三分钟点一次的。。
        gwk_iOS:@JohnayXiao 可以用单个 多个没什么用除非单个页面有多个banner
      • JohnayXiao:你好,你在验证的时候填的是AppStore地址,那苹果在审核测试的时候还是用的是沙盒账号,这样难道不会验证失败??
        JohnayXiao:请问一下楼主,你的Banner广告位为什么是多个?可以共用一个吗?如果共用一个,那跟每个界面单独一个有什么区别?
        gwk_iOS:@JohnayXiao 没问题 但其实不推荐在本地验证,还是推荐后端验证
      • 码修:现在墙比较厉害steam社区内容都用不了 我觉得写免费vpn比较有搞头 再弄个去广告版美滋滋 哈哈:relaxed:
      • 281b0125e80a:原来如些简单的也能通过审核啊:joy: 看来我也得弄个上架去:smile: 不过个人开发帐号也挺贵的。PS:建议增加通过拍照或图片中提取颜色的功能
      • 455e186e5a80:博主点赞过30了恰好我是第30个点赞滴人··期待你的广告版源码·
        另外用xcode7.2打不开的你源码?是因为NSPersistentContainer这个类是ios10的新特性的原因吗?需要升级xcode?
        455e186e5a80:@gwk_ios 谢谢!
        gwk_iOS:@bensco 恩 是新类 一会会上传广告版代码。
      • 谈Xx:还没看源码,先问下那个选色板是用什么方案实现的
        谈Xx:@gwk_ios 我忘记了,那个圆用个图片就好了:sweat:
        gwk_iOS:@谈Xx 获取当前触摸位置直接用Quartz 2D中渲染一个点就能获取到颜色了
      • GavinKang:大神, github中的真机调试收藏会崩溃哦
        gwk_iOS:@iOS开发的毛毛虫 已经提交过了哈 你再clone 下
        GavinKang:@gwk_ios 大神,现在可以运行了吗??
        gwk_iOS:@iOS开发的毛毛虫 好的。可能是修改忘记上传了…:joy:
      • 不知风光:没看懂
      • 38ff017a6f07:一只想弄个应用上AppStore,总想得很复杂,看来也是可以从简单的做起。
        学习广告、内购逻辑 :+1:
        gwk_iOS:@happytsgf 是啊 简单的 慢慢来 没有后台我们就做单机的也是可以锻炼锻炼
      • 加基特:新世界的大门,一直感觉加上付费就必须得做的高大上,也不敢去搞。看了你的“DEMO”感觉有爱了。(PS:后半段可以单独出个教程)
        陈伯君:@晋先森 admob依然是王者……
        60343a0ad510:@gwk_ios google的admob国内已死了。 ??????????????呵呵呵 ?
        gwk_iOS:@加基特 😝 感谢支持~ 现在广告审核比较麻烦 好的平台比较重要,可惜google的admob国内已死了。

      本文标题:iOS开发实战-上架AppStore 通过内购和广告获得收益

      本文链接:https://www.haomeiwen.com/subject/vyqrittx.html