[iOS] 上架App到Apple去review前會遇到的一些問題-- 使用 XCode Organizer 來遞交App到Store

聽從一些前輩的建議,決定還是把已經寫到一半卻累到半死的iOS App來上架
這裡記錄一下上架之前會遇到的一些問題: 

首先要先連到 iTune Connect 去新增一個App(當然你需要開發者帳號,也就是付每年$99的費用)
詳細流程參考: http://www.minwt.com/ios/4726.html 

關於新增資料上面有一些翻譯可以參考: http://www.csdtn.net/article/2011-01-07/289703

資料都新增完了,就會使用Application Loader 去上傳你寫好的App,不過這裡我發生幾次錯誤,分享給大家.

使用 XCode Organizer 來遞交App到Store

詳細方式如下:

  • 先到 Xcode 裏面Project Property —> [General] —> 把 Bundle Identifier 抄下來.記得把[team] 也先選擇到你的開發者帳號

    • ex: com.XXX.youAppName
  • iTune Connect 新增App申請,詳細流程可以參考這裡.注意 Bundle ID要跟XCode裡面的相同(就算寫錯了~可以之後修改)

  • Apple iOS Developer 網站的相關處理:

    • [Certificates, Identifiers & Profiles] —>[Identifiers]—> 新增一個 iOS App ID

      • 記得這裡Bundle Identifier 要跟你Xcode設定裡面一樣

        • ex: com.XXX.youAppName

    • [Certificates, Identifiers & Profiles] —> [Provisioning Profiles] —> 新增一個[Distribution]的 Provisioning

      • 這裡的App ID要使用剛剛申請的,這裡最好是一對一的mapping 比較不會有問題.
    • 下載 Provisioning 並且點兩下安裝

  • 到Xcode 準備打包上傳

    • 先到Project Property —>[Build Setting] 搜尋 [PROVISIONING PROFILE] —> 將它改成你剛剛下載的Provisioning

    • 把device 從模擬器或是手機切換到 [iOS Device]

    • [Product] —> [Archive]

      • 如還是有出現問題請參照以上得問題解答
    • 這時候就可以做App Validation 跟 Distribute 

大致流程就是這樣~接下來我的App就等著Apple 審核~~也祝福大家都成功啦…. 

[iOS] 關於時間NSDate的相關處理

昨天把NSDate的一些處理稍微研究了一下,有一些處理方式可以整理一下:

1. 比較時間 (NSDate comparison) 

<code style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;font-family:Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;white-space:inherit;"><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">if</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">([</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date1 compare</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">:</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date2</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">]</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">==</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ">NSOrderedDescending</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">)<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">{<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ"> NSLog</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">(@</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#800000;" class="str">"date1 is later than date2"</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">);<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">}<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">else</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">if</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">([</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date1 compare</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">:</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date2</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">]</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">==</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ">NSOrderedAscending</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">)<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">{<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ"> NSLog</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">(@</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#800000;" class="str">"date1 is earlier than date2"</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">);<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">}<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">else</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">{<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ"> NSLog</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">(@</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#800000;" class="str">"dates are the same"</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">);<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">}</span></code>

可以拿來比較時間的先後,在NSDate大概算是最方便的.

參考: http://stackoverflow.com/questions/5965044/how-to-compare-two-nsdates-which-is-more-recent

**
**

2. 時區的轉換

使用NSDate拿來的時間都是GMT要顯示的時候得轉成當地時區(台灣是TST)

-(NSString) transferGMT2TST: (NSDate)inGMT :(NSString*) newTZ

{

    NSDateFormatter *dateFormatter = [[NSDateFormatteralloc] init];

    dateFormatter.dateFormat = @”yyyy-MM-dd’T’HH:mm”;

    

    NSTimeZone *nTZ = [NSTimeZonetimeZoneWithAbbreviation:newTZ];

    [dateFormatter setTimeZone:nTZ];

    return [dateFormatter stringFromDate:inGMT];

}

 

  1. 也可以把NSDate當成一個時間比對或是timer,可以先設定一個未來的時間,然後做時間的比較.

 

繼續研究….

[iOS] 關於一些網路上SDK的學習整理心得

整理一下最近的關於iOS一些SDK學習心得

#import “CoreData+MagicalRecord.h”

  * 建立CoreData資料庫


  * AppDelegate 加入     [MagicalRecordsetupCoreDataStackWithStoreNamed:@"CoreData.sqlite”];


  * 搞定



* 詳細流程參考[這篇文章](http://www.cnblogs.com/mybkn/p/3328183.html)
  • Inappsettingskit (http://www.inappsettingskit.com/)

    • 設定App的設定通常都是使用Settings Bundle (參考: 這裡).如果要在app裡面更改設定就得自己都出整個頁面與設定控制,這個SDK可以快速的幫助你建制起來.

    • 流程也簡單到不行:

      • 把InAppSettingsKit資料夾加入你的專案

      • IASKAppSettingsViewController *IASKAppSettingView;

 if(IASKAppSettingView == nil)

    {

        IASKAppSettingView = [[IASKAppSettingsViewControlleralloc] init];

        IASKAppSettingView.delegate = self;

    }

    UINavigationController *aNavController = [[UINavigationControlleralloc] initWithRootViewController:IASKAppSettingView];

    //[viewController setShowCreditsFooter:NO];   // Uncomment to not display InAppSettingsKit credits for creators.

    // But we encourage you not to uncomment. Thank you!

    IASKAppSettingView.showDoneButton = YES;

    [self  presentViewController:aNavController animated:YEScompletion:nil];

    //   [self SwitchPage:PAGE_SETTING];

* 中文的介紹文章: [在這裏](http://blog.csdn.net/artwebs/article/details/8295937) 

我是參考這篇文章去找出相關的SDK,把這些SDK都玩過一次之後,又想去翻動我的程式了.一直改來改去~感覺很難上架了~~

[WindowsStore] 兩個產品共用同一份程式碼 (Shared source code with different store app with different name)

起因:
主要是因為在Windows Store 上面,需要產生兩個相同的內容的產品
但是版號與產品名稱卻不能相同.

做法:

  • 原本做法

    • 複製一個相同的目錄 (原本稱為ㄓ ProjA 複製出來為 ProjB)

    • 修改 csproj 把每個檔案的鏈結改到原本的 檔案

    • 在搬移的過程中,會出現以下的錯誤

      • Could not find **.xbf in target folder
    • 查詢過後發現,XAML 的檔案無法去link具有上一層目錄的檔案架構 ( ….XXX)

      • 但是卻可以去link “XXX “
  • 解決方法:

    • 僅僅複製把ProjB 的需要的檔案到ProjA的目錄下

      • AssemblyInfo.cs (注意要改名)

      • XXX.csproj

      • XXX_StoreKey.pfx

      • XXX_TemporaryKey.pfx

      • Package_StoreAssociation.xml (注意要改名)

      • Package.appxmanifest(注意要改名)

    • 這樣下來是有點醜,因為 bin/obj 會共用~這個之後會再仔細觀察是否有任何問題

    • 此外需要改兩個東西

      • 在 csproj 裡面
      • 在 Package.appxmanifest與Package_StoreAssociation.xml 內
    • 這樣就可以產生兩個一樣內容的app 在你的桌面

參考:

http://stackoverflow.com/questions/8162179/how-do-i-install-two-versions-of-my-metro-app

[VS2013][CI] 關於command line tool MSBuild.exe的筆記..

 

這一些主要是為了想學習架設  Jenkins,不過還沒整理完~先把這部分的整理出來….

  • [問題] 利用command line 的 MSBuild.exe 會發生錯誤 “error MSB8020: The builds tools for v120 (Platform Toolset = ‘v120’) cannot be found.”

    • 我也發現~就算你裝了 VS2013只要你有裝 VS2012 tool set你有可能會按倒 VS2012 command line tool,這時候的版本會出現  

      • msbuild.exe /version

        • 4.0.30319…
    • 解法: 首先要確認你執行是VS2013 command line tool或是執行 msbuild.exe 在  c:Program Fies(X86)MSBuild12.0Bin

    • 參考: http://public.kitware.com/Bug/view.php?id=14369

  • [問題]MSBuild去開始跑 *.sln 會出現 error MSB3779: The processor architecture of project being build “Any CPU” is not supported by referenced SDK ….

[Android] 關於相機程式的學習整理(1)

這裡整理了一些關於在Android寫相機App會產生的一些疑問,並且把自己寫好的程式整理出來放在Github上面.
這裡產生的相機功能主要如下:

  • 利用 Camera class產生相機的控制部分

  • 預覽的視窗

  • 拍照後可以讓Android系統預設的相簿觀看 

讓相機產生preview

1.將需要授權的相關部分放在”AndroidManifest.xml”

<uses-permissionandroid:name=”android.permission.CAMERA”/>

<uses-featureandroid:name=”android.hardware.camera.autofocus”/>

<uses-featureandroid:name=”android.hardware.camera”android:required=”false”/>

<uses-permissionandroid:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

  1. 把需要的元件畫出來,需要一個SurfaceView跟一個Button,修改reslayoutactivity_main.xml

    <SurfaceView

        android:id=”@+id/surfaceView1”

        android:layout_width=”fill_parent”

        android:layout_height=”fill_parent”

        android:layout_alignParentBottom=”true”

        android:layout_alignParentLeft=”true”

        android:layout_alignParentRight=”true”>

    </SurfaceView>

 

    <Button

        android:id=”@+id/btn_capture”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:layout_alignRight=”@+id/surfaceView1”

        android:layout_below=”@+id/textView1”

        android:layout_marginRight=”18dp”

        android:layout_marginTop=”144dp”

        android:text=”Capture”/>

  1. 讓preview可以動作,這裡的code比較多,儘量寫清楚點

3.1 建立 相關原件

//Camera object

Camera mCamera;

//Preview surface

SurfaceView surfaceView;

//Preview surface handle for callback

SurfaceHolder surfaceHolder;

//Camera button

Button btnCapture;

//Note if preview windows is on.

booleanpreviewing;

3.2 連接原件修改原來的 OnCreate

@Override

  protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_main);

 

  btnCapture = (Button) findViewById(R.id.btn_capture);

 

  surfaceView = (SurfaceView) findViewById(R.id.surfaceView1);

  surfaceHolder = surfaceView.getHolder();

 

  surfaceHolder.addCallback(new SurfaceViewCallback());

  surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

}

3.3 增加surface callback class讓surface自己來處理相關預覽的部分

private final class SurfaceViewCallback implements android.view.SurfaceHolder.Callback {   

public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) 

{

  if (previewing) {

    mCamera.stopPreview();

    previewing = false;

  }

 

  try {

    mCamera.setPreviewDisplay(arg0);

    mCamera.startPreview();

    previewing = true;

   } catch (Exception e) {}

}

public void surfaceCreated(SurfaceHolder holder) {

   mCamera = Camera.open();

   // get Camera parameters

   Camera.Parameters params = mCamera.getParameters();

 

   List focusModes = params.getSupportedFocusModes();

   if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {

    // Autofocus mode is supported

  }

}

 

public void surfaceDestroyed(SurfaceHolder holder) {

  mCamera.stopPreview();

  mCamera.release();

  mCamera = null;

  previewing = false;

  }

}

 

  1. 這樣就可以看到完整的camera preview,接下來就要implement 拍照後的一些處理

4.1 實作出拍照的功能

  btnCapture.setOnClickListener(new Button.OnClickListener() {

     public void onClick(View arg0) {

     if (previewing)

         mCamera.takePicture(shutterCallback, rawPictureCallback,jpegPictureCallback);

   }

  });

4.2 實作camera.takePicutre 的callback function

 先接起來button click listener

btnCapture.setOnClickListener(new Button.OnClickListener() {

  public void onClick(View arg0) {

  if (previewing)

    mCamera.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

  }

});

要把相關的 shutterCallback, rawPictureCallback加進去,最後記得要把jpegPictureCallback 弄好

ShutterCallback shutterCallback = new ShutterCallback() {

     @Override

     public void onShutter() {

   }

};

 

PictureCallback rawPictureCallback = new PictureCallback() {

     @Override

     public void onPictureTaken(byte[] arg0, Camera arg1) {

     }

};

這裡要注意,在jpegPictureCallback 中有幾件重要的事情

  • 將抓下來的部分存成檔案

    • 檔案目錄與檔名的選取,記得使用Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)

    • 在檔案的存取方面,使用的是BufferedOutputStream來寫檔案~這裡大部分網路例子會先轉成 Bitmap 然後再轉成JPG,這裡要改

  • 利用 MediaScannerConnection 去連接MediaStore 來把你拍下來的照片通知給系統,讓系統的相簿可以抓到你的相片

 這部分的code 如下

PictureCallback jpegPictureCallback = new PictureCallback() {

@Override

   public void onPictureTaken(byte[] arg0, Camera arg1) {

      String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()

                        + File.separator

                        + “PicTest_” + System.currentTimeMillis() + “.jpg”;

      File file = new File(fileName);

      if (!file.getParentFile().exists()) {

          file.getParentFile().mkdir();

       }

 

      try {

             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));

             bos.write(arg0);

             bos.flush();

             bos.close();

             scanFileToPhotoAlbum(file.getAbsolutePath());

             Toast.makeText(MainActivity.this, “[Test] Photo take and store in” + file.toString(),Toast.LENGTH_LONG).show();

        } catch (Exception e) {

                Toast.makeText(MainActivity.this, “Picture Failed” + e.toString(),Toast.LENGTH_LONG).show();

        }

    };

};

 

public void scanFileToPhotoAlbum(String path) {

 

        MediaScannerConnection.scanFile(MainActivity.this,

                new String[] { path }, null,

                new MediaScannerConnection.OnScanCompletedListener() {

 

                    public void onScanCompleted(String path, Uri uri) {

                        Log.i(“TAG”, “Finished scanning “ + path);

                    }

                });

這樣就完成了,完整的程式放在Github   

https://github.com/kkdai/Android_Camera_Sample