prettyprint

2015年10月13日 星期二

iPhone App 設計概念: MVC 設計模式

2015.10.12

iPhone App 設計遵循 MVC 設計模式 Design Pattern。此 MVC 即 Model - View - Control 的縮寫。當 Ap 
變得複雜時,會將問題切割為較小單位來解決,此為 Divide and Conquer 方法。所以,MVC 模式也以 Divide 
and Conquer 的思維,將問題簡化,各司其職。 Model 負責 Data 資料的存取,View 負責資料的 Presentation
 呈現,而 Control 則是此模式的神經、管家,負責運算邏輯 Logic,作為處理 Model 與 View 之間的橋樑。
MVC 模式已被廣泛運用在 AP 的開發。 Apple 提供一個整合免費的開發環境,即 Xcode。 App 設計時,需考慮 
User 如何與裝置 device 互動,這互動是藉由 App 所提供的 UI 介面達成。 Xcode 內含 UI 開發工具 
Interface Builder,讓開發人員用“畫”的方式,將所需要呈現的 UI 元件拖曳到"畫面“ Screen 上,這 
“What You See Is What You Get 所見即所得”的直覺式設計 View 的方式,大大降低開發人員過去以文字 
Code 的輸入方式所耗的時間。

Apple 的 UI 設計,一般我們設計的 App 只會在一個 device 呈現,當我們要將 UI 元件"畫“上時, Apple 
已為我們準備一個唯一的 UI 容器 Container,這容器即是 Window。這 Window 就暫且把它看成 Storyboard。
由於這 Window 只是容器,沒有我們平常看到的像是按鈕等 UI 元件,所以只是個 UI 框架。但,UI 元件不能
"畫“在這 Window 框架中,只能畫在"視圖 View"上,所以在畫 UI 元件前,我們必須先準備好 View 這視圖。
既然 Window 是個大容器,它可以依據需要容納更多的視圖 View,而每個 View 與 View 之間並不是單獨存在
的,而是有關係的,這關係在 Xcode 工具中是用 Seque 的方式來連結。當所有的 View 都連結好後,這 App 
要提供給 User 什麼樣的功能就此完整呈現說明。所以 Apple 在 UI 設計上以 Storybord 故事分鏡的概念來
完成。當然,完成 Storyboard 之前,我們已經將每個 View 所需要的 UI 元件都“畫”到各自的 View 中。若
我們設計的 App 會在 2 個以上的裝置 device 顯現,此時 App 的 UI 就需要 2 個以上的 Window 容器,
每個 Window 容器各自對應到特定的顯示裝置。

當 UI 介面定稿,接下來我們要讓 App 能“動”。UI 視圖 View 上面這些元件大致上分為 2 類。一類只是顯示,
不需要任何動作。另一類 UI 元件則需要後續處理。對於後續需要處理的 UI 元件,我們就必須能識別,因此會
設定"變數“名稱命名。當 UI 元件與程式裡與其有關的識別名稱都準備好,此 2 者各自獨立,誰也不認識誰。
此時,Apple 提供一個 connect 的機制,將 UI 元件與其相關的識別名稱 variable 關聯起來, 而在此識別
名稱前加入一個特別的前綴詞 @outlet。另外,若這些元件是與 event 事件有關的元件,例如 Button 按鈕,
這時 Apple 提供另外一個 connect 機制,講此類 UI 元件與其相關的“動作 Action"關聯起來,而在此事件
名稱前加入一個特別的前綴詞 @action。 由於 iPhone 的 App 遵循 event driven 此 windows 模式架構,
所以當 User 按下 Button 安扭時,會觸發一個事件 event。當事件被觸發後,接下來程式就要決定接下來該
怎麼處理。若還需要進一步讀取資料庫,則讀取完畢後再處理資料,而將最後處理的結果回傳給 View,由 View 
將結果顯示出來。因此我們說 Control 這模組是負責運算邏輯 Logic,作為處理 Model 與 View 之間的橋樑。
要注意:每個 View 視圖的背後都會有一個,且是唯一的 ViewController 來控制這視圖 View 的 UI 元件,
以及其之間的 Logic 處理。

剛提到,若 Control 模組處理時,需要額外的資料,它會透過機制向 Model 模組要資料。這些資料可能是有關
 App 設定的 plist 檔案。若更複雜則使用小而美、有五臟俱全資料庫能能的 SQLite,或是使用 Apple 貼心的
設計 Core Data 機制。 Core Data 是將 data base 的複雜性封裝,而提供等同的資料庫存取介面,方便開發
者使用。有時 App 的質料必須連結至網路例如 iCloud 讀取。基本上 Apple 對資料的安全維護控管採用 
Sandbox 的概念,即每個 App 各自有屬於自己的資料庫,各自井水不犯河水。當然,這也有例外。Apple 也提供
讓不同的 App 共用存取資料的方式,例如 iPhone 的聯絡人資料。所以剛接觸 iPhone App 設計時,書本會要求
你不勾選 Core Data,就是說這測試學習用的 App 並不需要存取任何資料。

學習 iPhone App 的設計,熟習了解 MVC 設計模式是很重要的,如此我們才能將 App 的設計的實作部分由繁入
簡。 MVC 設計模式好比是森林,接下來我們要入林探訪,了解整個 App 設計概念的細節部分。這其中較常被忽略
的是 UIViewController 的 Life Cycle。 各 View 中的 UI 元件何時被建立,又何時被釋放,
這些觀念要釐清,我們才知道程式碼該放在那個函數 function 中。


後記:

對 iPhone App 入門者而言,這 MVC 設計模式,以及與 Xcode 工具之間的關係與運用,總要摸索一段時間。若
僅是跟著書本敲打,而只知其然,不知所以然,在往後開發一個 App,遇到困難點就會不知道如何除錯 debug。
在此將學習過程紀錄,有助於其他人學習。

參考:

1. File:MVC Diagram (Model-View-Controller).svg, https://commons.wikimedia.org/wiki/File:MVC_Diagram_(Model-View-Controller).svg



2015年10月2日 星期五

Apple Watch Test


1. Add new project:

First, you have to create an iOS Application.

Apple Watch App is an add-on to an existing iOS App. You first have to create an iOS application and then add a WatchKit application to it.

Select Universal for Devices.

2. Select Editor Function for Adding Apple Watch Target to Project: Then, you add a WatchKit application to the iOS Application.
3. Add Apple Watch Target to Project
4. Uncheck or Check Include Notification Scene. It is an option. Now we will implement nothing. This project is only for testing.
5. Activate this App's scheme.
6. Afterwards, this project contains 3 main folders: AppleWatchTest, AppleWatchTest WatchKit 1 Extension, and AppleWatchTest WatchKit 1 App A WatchKit application runs in the WatchKit app extension on the iPhone, not on the Apple Watch itself.
7. Review target property settings: General
8. Review target property settings: Build Setting
Test: 1. For the first time testing the setting shown below is optional.
2. Run the test:
3. For Apple Watch' app, you will see that this project will generate 2 running processes: Watch App and WatchKit Extension
Resources: 1. http://www.apress.com/9781484210260

2015年10月1日 星期四

如何學寫 iOS App

寫 iPhone App 相信應該是大多數程式師躍躍欲試的領域,而如果有天能寫出像 Angry Bird 那樣的軟體,那名利雙收不在話下。但,問題是:該從何開始?

首先你要有個開發環境。過去坊間有教人用黑蘋果來開發。黑蘋果顧名思義就是紅透發黑的蘋果,而這樣的蘋果能吃嗎?也就是在一般 Intel i5/i7 的 PC 上,先安裝 Mac OS,然後在安裝開發環境 Xcode。這工程非常浩大,縱然安裝好了,但 Apple 的 OS,Xcode 改新版本的數度超快。例如:今年才剛拿到的新書 Xcode 版本是 6.x,且以 Swift 開發,而目前 2015/9 月 Xcode 已進化到 7.0,且OS 更新為 iOS 9。以這樣的速度,紙本的書籍根本無法跟上 Apple 的野心。我就碰過照著書一字不漏敲打,就是無法編譯。後來追根究底才發現 Swift 演化到 Swift 2.0,在呼叫函數 function 時,新增加類似 Java 的 throws,這屬於 Exception 報錯誤訊息的功能。

既然書籍還沒上架就註定會滯銷 (?),那有心成為 iPhone App 的開發者不得不養成耐性,將 Apple Dedeveloper 的開發者網站當成尋寶迷宮。這網站要什麼有什麼 (廢話),但由於是五臟俱全,所以開發者頭髮還沒有變白,那寶物依然安然躺在雲深不知處。這種焦慮對從未開發程式的初學者更明顯。初學者必須藉由書本,良師的指引才能登堂入室。然而,既使 Swift 與 Objective-C 相較之下,已降低相當的學習門檻,但不可諱言 Swift 的彈性與豐富簡約的語法的功能,常會令開發老手歎為觀止,更別提新手了。最著名的範例就是 Closure 閉包。有誰會想到以 { $0 > $1 } 來表示兩個變數的比較?這概念非要有歷經 Unix / Dos 使用命令式 command line 處理資料,才能有感覺與想像。

學習好 Swift 這 Apple 去年才發表的新程式語言之後,你可能會沾沾自喜,認為自己可以拜別師父,自立門戶了。然而,等下了山才發現即將面對的是成堆的 類別 Class 組成的程式庫 Library。這 Library 從物件導向的觀念來看,就把它們當成工具箱,等需要時再拿出適當工具。我們不會拿蓋房子的磚塊去煮飯吧?!煮飯自己有自己的工具。這工具 Library 若以 Apple 的術語,就叫做 Framework。這 Framework 換湯不換藥,就是很貼心早已為我們準備套裝的工具,然後為我們建立一個粗毛胚,好讓我們不必從頭由無到有建立一個 App。它好比量販店賣的微波食品,有快炒,有湯點,各有各的配料。接下來,開發員整日在這堆 Framework 鑽來鑽去,而這是不得不然啊!剛才說過,Apple 更新開發程式的環境如此快速,新的功能只會在 Apple 的開發者網站找得到,其他免談。乍聽之下,有野心的開發者更是見獵心喜,但苦的是,在鑽入這大黑洞後,開發者除了了解 Frameworks 所提供的功能外,也必須像是做實驗般,不斷地 try and error,有時不僅 App 要能動,若力求美感,還要講求程式碼 Code 的可讀性,效率,預留方便除錯等。

拉雜寫了一堆,會不會玻璃心就如此碎了?有時更糟糕的是你得面對殘酷的現實,就是寫程式的人並非人人是口袋滿滿,尤其在台灣這個只會講究做麵包,不注重食譜的短視近利企業心態。所以適合寫程式的人仔細觀察真有那人格特質,即能夠做得住,捱得住,既使荷包不比業務,但能從寫程式的除錯過程中“享受”那唯我獨一的快樂。這樣的人很適合寫程式,也足夠強壯去接受 Apple 層出不窮翻新的挑戰。但,繞了一大圈,我們又該如何買入門票呢? 我個人認為先要有寫程式的整體概念,再來寫程式就相對容易些。例如:若不知道物件導向 Object-Oriented,能把 App 寫的精妙,那我也很佩服。問題是:可能嗎?有了程式的概念,再去猜猜怎麼寫,要找什麼樣的工具,這樣建構一個 App 就容易些,不會像是大海撈針。對毫無經驗的初學者而言,雄心壯志固然重要,但好的師傅,好的武功祕笈更能讓你早點練好九陽神功,早日下山除害啊!

PS. 以上這些對想入門 iOS App 的人有幫助嗎?個人學淺,僅就個人的經驗提出看法,希望能拋“柱”引玉,獲得更多的迴響,好讓百家爭鳴,那有福的是苦思無法入寶山的慕道者啊!一笑!


2015年9月28日 星期一

新增檔案 Create a New File

Purpose: 

探討如何以 Swift 建立新的檔案。

Code:


//
//  ViewController.swift
//  FileBuildUpDemo
//
//  Created by Elvis Meng on 2015/9/28.
//  Copyright © 2015年 Elvis Meng. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let fileManager = NSFileManager.defaultManager()
        let directory = NSHomeDirectory().stringByAppendingString("Documents/data")
        do {
            try fileManager.createDirectoryAtPath(directory, 
                                                  withIntermediateDirectories: true, 
                                                  attributes: nil)
            print("directory build up success")
        }
        catch {
            print("directory build up fail")
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
Test:
當 fileManager.createDirectoryAtPath(:directory, withIntermediateDirectories:, attributes:) 的 withIntermediateDirectories 設為 false 時,若此檔案已建立,則不覆蓋原有檔案:
後記: 新版本 Xcode 7.0 只支援 fileManager.createDirectoryAtPath(:directory, withIntermediateDirectories:, attributes:),不支援 fileManager.createDirectoryAtPath(:directory, withIntermediateDirectories:, attributes:, error:),且呼叫改採用 throws 方式來呼叫,即程式要用 do { try func XXX } catch { ... } 的語法。 參考: 1.NSFileManager, https://developer.apple.com/library/prerelease/watchos/documentation/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/

2015年9月25日 星期五

專案中的 Bundle ID


開發 App 時會啟動一個專案 Project,而除了專案的名稱之外,我們還要給這專案一個很特別的 Bundle ID。此 Bundle ID 是由 Product Name 與 Domain Name 組成。為何需要此 Bundle ID 呢? 因上架到 App Store 的應用 App 相當多,而 Apple 設計要求每個 App 要有自己的 Bundle ID,而此 Bundle ID 在 App Store 是唯一的,就像是我們的身分證一樣。

當 User 下載安裝你的 App,安裝後會在其 iPhone / iPad 上建立一個專屬的目錄,用來存放你的 App 執行檔,圖片,音樂等,同時存放資料。別人的 App 無法存取你專屬目錄下的資料,這是 Apple 以 Sandbox 概念,來管理資料的安全性。

App 安裝後,每個 App 也會有自己的目錄結構,每個目錄各自放不同的資料方便管理。 

Apple 在資料的管理上採用 Container 概念,即每一個 Container 各司其既定的功能。 例如: Bundle Container。在這 Container 下,存放我們在 Xcode 所開發的 Code,Resource,與相關 Supporting Files。
參考: 1. File System Basics, https://developer.apple.com/library/prerelease/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW2

2015年9月23日 星期三

取得目前定位的位置

 
CLLocationManager 直接繼承 NSObject,在程式中要用 import CoreLocation 取得其相關工具 (元件)

CLLocationManager 的使用方式如下:
生成 CLLocationManager 這物件,並指定其代理 delegate:

locationManager = CLLocationManager()
locationManager.delegate = self
與 Core Location 這類別相關的常數 Constant:
所以,我們在此只設定取到位置需要的精準度:

locationManager.desireAccuracy = kCLLocatinAccruacyBest
取得使用 Core Location 授權:
在此選用 requestAlwaysAuthorization() 方法:

locationManager.requestAlwaysAuthorization()
如何取得目前的座標位置?
新增專案:
程式碼:

//
//  ViewController.swift
//  FindLocationDemo
//
//  Created by Elvis Meng on 2015/9/23.
//  Copyright © 2015年 Elvis Meng. All rights reserved.
//

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate{
    
    var locationManager: CLLocationManager!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()
        //locationManager.startUpdatingHeading()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func locationManager(manager: CLLocationManager,
    didUpdateLocations locations: [CLLocation]) {
        let curLocation:CLLocation = locations[0]
        print("test")
        print("latitude = \(curLocation.coordinate.latitude)")
        print("longitude = \(curLocation.coordinate.longitude)")
    
    }

    override func viewDidDisappear(animated: Bool) {
        locationManager.stopUpdatingLocation()
    }
}
測試環境設定:
新增 2 個環境變數:
開始測試: 允許定位:
此時新增 Privacy
檢查 Location Service 是否開啟 ON:
設定 Apple 已預設城市座標,選擇 Tokyo, Japan:
測試結果:
參考: 1. CLLocationManager, https://developer.apple.com/library/prerelease/watchos/documentation/CoreLocation/Reference/CLLocationManager_Class/index.html#//apple_ref/occ/cl/CLLocationManager 2. Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone 2010], http://www.slideshare.net/invalidname/adamson-bringingyourownmaps 3. Reduce Location Accuracy and Duration, https://developer.apple.com/library/prerelease/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html 4. CLHeading, https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLHeading_Class/ 5. About Location Services and Maps, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/Introduction/Introduction.html 6. Getting the User’s Location, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW1 7. Core Location Constants Reference, https://developer.apple.com/library/prerelease/tvos/documentation/CoreLocation/Reference/CoreLocationConstantsRef/ 8. CLLocationManagerDelegate, https://developer.apple.com/library/prerelease/watchos/documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/index.html#//apple_ref/occ/intf/CLLocationManagerDelegate

2015年9月21日 星期一

Random Generator 亂數產生器


在 Apple 的官方網站 [1]上描述 arc4random_uniform() 這亂數產生器:

u_int32_t arc4random_uniform(u_int32_t upper_bound);

arc4random_uniform() 
will return a uniformly distributed random number less than upper_bound.

arc4random_uniform() 
is recommended over constructions like ``arc4random() % upper_bound'' as it avoids
"modulo bias" when the upper bound is not a power of two.
程式:

//
//  ViewController.swift
//  RandomGeneratorDemo
//
//  Created by Elvis Meng on 2015/9/21.
//  Copyright © 2015年 Elvis Meng. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let n:UInt32 = UInt32(arc4random_uniform(100)+1)
        print("n = \(n)")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
測試:
參考: 1. ARC4RANDOM(3), https://developer.apple.com/library/prerelease/mac/documentation/Darwin/Reference/ManPages/man3/arc4random_uniform.3.html
prettyPrint();