顯示具有 筆記 標籤的文章。 顯示所有文章
顯示具有 筆記 標籤的文章。 顯示所有文章

2021年2月3日 星期三

Flutter 開發文件之 iOS 與 Android 實作筆記 - 使用 Admob 以 Interstitial ads 為例


關於 flutter 使用 Admob 的架構,可參考網路上數篇文章:
此例筆記 iOS & Android 的設定方式,並且記錄碰到的錯誤訊息。

Android app 設定方式:

1. 更新 flutter_app/pubspec.yaml 添加 dependencies (記得要運行 flutter pub get)

firebase: ^8.0.0
firebase_admob: ^0.11.0+1

2. 安置從 Firebase 建立專案時得到的 google-services.json 檔案,擺在 flutter_app/android/app/ 中

3. 更新 flutter_app/android/build.gradle 的 buildscript -> dependencies 設定

//classpath 'com.android.tools.build:gradle:3.5.0'
// 為了修正錯誤訊息:error: unexpected element <queries> found in <manifest>.
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.google.gms:google-services:4.3.3'

4. 更新 flutter_app/android/gradle/wrapper/gradle-wrapper.properties 的 gradle 版本至 6.1.1

#為了修正錯誤訊息:Minimum supported Gradle version is 6.1.1. Current version is 5.6.2. If using the gradle wrapper
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

5. 增加 flutter_app/app/build.gradle 的 apply 項目

apply plugin: 'com.google.gms.google-services'

6. 更新 flutter_app/android/app/src/main/AndroidManifest.xml 添加 meta data (僅範例程式數值)

<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/>

7. 更新 flutter_app/lib/main.dart (此改自於範例程式,讓 add Button 可以產生一個插頁廣告)

iOS app 設定方式:

1. 更新 flutter_app/pubspec.yaml 添加 dependencies(記得要運行 flutter pub get)

firebase: ^8.0.0
firebase_admob: ^0.11.0+1

2. 安置從 Firebase 建立專案時得到的 GoogleService-Info.plist ,用 Xcode 打開專案,把 GoogleService-Info.plist 拖拉擺入跟 Info.plist 的同階目錄位置。

% open flutter_app/ios/Runner.xcworkspace
% ls flutter_app/ios/Runner/GoogleService-Info.plist

3. 更新 Info.plist 內容,添加資訊 GADApplicationIdentifier 等資訊(僅範例程式數值)

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
<key>SKAdNetworkItems</key>
  <array>
    <dict>
      <key>SKAdNetworkIdentifier</key>
      <string>cstr6suwn9.skadnetwork</string>
    </dict>
  </array>

4. 更新 Podfile ,上方添加 platform :ios, '10.0'

為了修正錯誤訊息: 
Automatically assigning platform `iOS` with version `9.0` on target `Runner` because no platform was specified. Please specify a platform for this target in your Podfile.

5. 更新 flutter_app/lib/main.dart (此改自於範例程式,讓 add Button 可以產生一個插頁廣告)

如此就收工啦!而 flutter_app/lib/main.dart 內容如下:

import 'package:flutter/material.dart';

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_admob/firebase_admob.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  MobileAdTargetingInfo targetingInfo = MobileAdTargetingInfo(
    keywords: <String>['flutterio', 'beautiful apps'],
    contentUrl: 'https://flutter.io',
    childDirected: true,
    nonPersonalizedAds: true,
  );

  InterstitialAd _interstitialAd;

  InterstitialAd createInterstitialAd() {
    return InterstitialAd(
      adUnitId: InterstitialAd.testAdUnitId,
      targetingInfo: targetingInfo,
      listener: (MobileAdEvent event) {
        print("InterstitialAd event $event");
      },
    );
  }

  @override
  void initState() {
    super.initState();
    FirebaseAdMob.instance.initialize(appId: FirebaseAdMob.testAppId);
    //_interstitialAd = createInterstitialAd()..load();
  }

  @override
  void dispose() {
    _interstitialAd?.dispose();
    super.dispose();
  }

  void _incrementCounter() {
    _interstitialAd?.dispose();
    _interstitialAd = createInterstitialAd()..load()..show();
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

 

2019年12月2日 星期一

FB 廣告投放筆記

FB廣告投放

2018 年夏天跑去聽了一門泰國老闆的經商之術,2019年秋天尾則是看到FB廣告大神的臨時加開課程就衝了一發 XD 本身不算這個領域的,但每次衝去接觸就有新的感觸,真的不錯!

這算是我第二次廣告投放聽課,上一次是在新創內部課程,距今大概 5 年了吧。聽這課程還滿不錯的,花個短短的四小時複習一下有哪些新東西,跟哪些容易遺忘的東西。剛好近兩年在搞流量變現,現在把思維反過來用。

這門課採用實務教學方式,過程沒有投影片,而是一頁頁瀏覽器直接操作 FB 廣告投放的後台,有許多一言難以道盡的心得,條列式如下:
  • FB User 興趣、地理位置、社群互動資訊,依舊是強項,這是 Google Ads 難以對抗的項目。消費產品的電商就是靠 FB Ads 啦
  • Online-Merge-Offline (OMO) 廣告投放早已很成熟,店家透過 Point of sale (POS) 店頭機系統可以將線下活動資料匯入到 FB 廣告後台,即可做到上線線下整合,例如對到店用餐的客戶投放線上購物的資訊,這兩者是互補而非互斥,例如實體餐飲店的體驗轉換成線上購買新式甜點等。而線下為了收集到客戶的 Email 和手機幾乎無所不用,辦個優惠/贈品活動都是(想起來那些送化妝贈品要留姓名電話的過程嗎?)
  • 不要期待自流量帶來的交易,且在 3C 產業不要再對已購買的再行銷。對既有客戶再行銷屬於耗材類的商品才有意義。
  • 不需太強調 A/B Test 的策略,實際上運作就會不斷追求更佳文案,自然會演化到最佳的路線(就像寫程式不見得一定不用靠 framework ,久了為了好維護就會自成 framework 的)
  • FB 為了用戶體驗也會評分你的文宣,依照評分機制評估該讓你的文宣給多少受眾觀看。鼓勵多和用戶互動,別人分享或留言都可以去按讚,大概可以參考蝦皮/全聯/政治/眾籌等其他小編文章,一樣有滿滿的互動。
  • 粗略引流成交的成本約 300-800 台幣,商品利潤有這等水準就該試試 FB Ads 買流量導購電商
  • 電商領域上,不要過度陷入到曝光價格/點擊價格的競局,重點還是訂單成交跟獲利
其中,我覺得最重要習得的策略是評估廣告文案的狀態。評估一則廣告文案是靠受眾佔比數量,若在條件中受眾的數量是 10萬,那實務上大概也只能接觸到 50% 而已,在接觸到 50% 之前,大概 5-10% 時就可以評估文案跟受眾的互動成績,以此決定是否要換文案或受眾。

這些評估策略的經驗還滿寶貴的,著重的不在廣告投放時間,而是更精準的受眾互動數據。

講師也提到一些概念,也驗證過去自己猜測的方向:
不要以為廣告一直打,用戶可能哪天想起會買
也就是廣告不是一直擺在那邊花錢,接著催眠自己哪天用戶會想起的。

實務上,有效的廣告投放可能一天內(最遲七天內)就會看到成效。而重複對一個用戶打廣告的方式也是用不同文案,永遠要避免同一個文案被一個用戶重複觀看,重複觀看可是傷了自己的荷包。

跟這類似的本業領域大概就是數據收集,不認真思考數據收集的目的/情境,就全部說收,殊不知網路儲存要費用、數據運算的算力也是要費用,搞到最後所有的收集就跟沒收集一樣,隨著時間而逝。當然也有反過來的,就是拼命想規劃情境收集,沒有明確的評鑑用途就淪為八卦資訊,數據價值又隨著時間衰減而無意義。

其中 A/B Test 就有那麼一點點味道,因為所有文案/策略都有時空背景,不見得訓練出某個經驗就可以一直沿用,搞到最後就是不需 A/B Test,永遠真槍實彈上場,心中自成一把尺可以評估該文案到底能不能再進步,因為久了就該略知哪些 CTR / CPC 是合理的、是可以再進步的。

這行業也是令人小抖,例如好不容易找到了高價值受眾,結果這款商品銷售完後,就得再打起精神再找下個受眾,這種沒完沒了的生活... XD 其實跟新創差不多,在時間限制下看誰先搶下市場。搶下市場或得資源後,就開始進入資本主義的保衛戰,建立更高的市場跨入門檻,一日復一日。這時就覺得當個投資人是多麼幸福的事,靠分散風險錢砸下去就好,不用浪費自己的青春年華啊。

2014年3月11日 星期二

iOS 開發筆記 - 使用 AssetsLibrary (assets-library://) 取得相簿內照片的原圖、縮圖

一直以來都是自己檔案管理,最近開始想要用內建相簿來管理儲存。

用法:

#import <AssetsLibrary/ALAssetsLibrary.h>
#import <AssetsLibrary/AssetsLibrary.h>

typedef void (^DoneHandler)(NSDictionary *ret);
- (void)get:(NSString *)key callback:(DoneHandler)handler {
 
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
 
[assetslibrary assetForURL:[NSURL URLWithString:key] resultBlock:^(ALAsset *asset) {
//NSLog(@"asset:%@",asset);
UIImage *image = nil;
image = [UIImage imageWithCGImage:[asset aspectRatioThumbnail]];
image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]];
        handler(@{@"image":image});
} failureBlock:^(NSError *error) {
        handler(@{});
}];
}


其中 key 是類似這樣:assets-library://asset/asset.JPG?id=xxx&ext=JPG。

2014年1月1日 星期三

iOS 開發筆記 - 設定 UIButton 發動 Storyboard Segue 事件(performSegueWithIdentifier)

使用 Storyboard 處理多個 UIViewController 進行切換時,可以很簡單地透過按住 ctrl + 元件(如 UIButton),拖拉到指定的 UIViewController ,完成 UIViewController 切換工作。然而,有時希望按了元件後,做完一堆事才切換到指定的 UIViewController ,這時候就要稍微更改 Segue 發動的流程。

例如,目前共有 AUIViewController 跟 BUIViewController ,其中 AUIViewController 上頭有一個 AUIButton ,當點選完 AUIButton 時,做完一些要事,再切換到 BUIViewController。

開發上,在 Storyboard 上,先在 AUIViewController 點一下,並按住 AUIViewController + Ctrl 後(並非點 AUIButton),拉條線到 BUIViewController 上,以此建立一個 Storyboard Segue 叫做 "ChangeViewController",接著把 AUIButton 拉條線到 AUIViewController.m ,做個  IBAction ,代表點選 AUIButton 將發動的動作,剩下的程式碼:

AUIViewController:

- (IBAction)AUIButtonEvent:(id)sender {
// Step 1 : doing …

// Step N : fire a segue event
[self performSegueWithIdentifier:@"ChangeViewController" sender:sender];
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"ChangeViewController"]) {
        UIViewController *target = segue.destinationViewController;
        if ([target isKindOfClass:[UINavigationController class]]) {
            target = [[(UINavigationController *)target viewControllers] lastObject];
        }
        if([target isKindOfClass:[BUIViewController class]]) {
            BUIViewController *v = (BUIViewController *)target;
            // … other setting …
        }
    }
}


如此一來,當點選 UIButton 時,可以先做完事再切過去指定的 UIViewController 了,當然,也可以讓一個 UIButton 依照事件條件,切換到多個 UIViewController 。