2015年3月22日日曜日

Hướng dẫn làm application cho Apple Watch

Đây là sản phẩm được yêu chuộng của Apple  dự định  sẽ được bán  ra vào cuối tháng 4 này. Nhìn tổng quan đây là sản phẩm đẹp, tiện lợi và hứa hẹn 1 thị trường béo bở mới cho các lập trình viên về application cho các sản phẩm của Apple.

Dưới đây mình xin giới thiệu với các bạn các bước lập trình cơ bản với sản phẩm của Apple Watch.

Điều kiện cần thiêt:
 1. Xcode 6.2 beta trở về sau
 2.  Cập nhật mới library do Apple  cung cấp (WatchKit)
 3.  Có kiên thức cơ bản về ngôn ngữ Swift

Các bước lập trình:

Bước 1:Mở Xcode và lập 1 project mới với target là Iphone 6 
  ※File\New\Target…
  iOS\Apple Watch\Watch App

Bước 2: Kiểm tra các option cho application cần lập trình.

Cần xác định những tiêu chí cần thiết sau đây cho application.
  ※Language: Swift
  ※Không chọn Include Notification Scene và Include Glance  Scene 
Chọn Finish để đi đến được project có cấu trúc như bên dưới.
■BitWatch  Watch App bao gồm cả storyboard và image assets . Trong  trường hợp này chúng ta không sử nó mà chỉ xem như là View của application
■BitWatch WatchKit Extension sẽ bao gồm code xử lí các sự kiện khi application được khởi động, thay đổi giá trị của các switch hay các  button ở bên trong  application. Chúng ta có thể tưởng tượng cấu trúc của 1 Watch application như hình ảnh bên dưới.
Bước 3:
Ta đi tiếp vào lập trình cụ thể chi tiết. Trước hết lập trình thiết kế cho Watch application mà chúng ta  sẽ cho chạy trên Apple Watch. Chọn Bitwatch Watch App project , chon Interface.storyboard để thiết kế.

Drag và drop từ object library bên trái Xcode Tool các object sau:
  ※ 1 UILabel
  ※ 1 UIButton
Lưu ý bạn chỉ có thể di chuyên các Object theo chiều dọc 1 cách mặc định như hiệu chỉnh 1 Auto Layout.
Positioning: Bạn có thể thay đổi các hiểu chỉnh bằng cách chỉnh ở thanh Attributes Inspector . Như setting  bên dưới UIButton sẽ nằm dưới màn hinh, UILabel sẽ nằm trên đỉnh màn hình.
※UILabel : 
  Horizontal position : Center
  Vertical position     : Top  
※UIButton : 
   Horizontal position : Center
 Vertical position     : Bottom

Sau đấy các bạn chọn thanh cung cụ Attributes inspector bên phía tay trái , hiệu chịnh UILabel theo tiêu chuẩn sau:
   * Để biểu thị giá trị Bitcoin price bạn cài đặt Text dưới dạng sau : $0.00
  * Alignment : center
  * Font :  Custom , Avenir Next, Demi Bold , Size 30
Chọn Button và cài đặt title là  Refresh. Đồng thời chọn Font là CustomAvenir NextMedium, Size16.
Bước 4:  kết nối các button va label với code  xử lí
Các bạn chú ý Apple  đã cung cấp cac hổ trợ cho bạn bằng bluetooth để  kết nối Apple Watch của bạn và Iphone. Bạn không cần phải lo lắng nhiều.

Quay lại với lập trình , các bạn chọn Assistant Editor chọn InterfaceController.swift để biểu thị màn hình chia đôi như bên dưới .

Chọn UILabel bên phái trái màn hình , nhấn control+ drag vào bên phía trái  của  InterfaceController đặt tên là priceLabel và chọn Connect.

Bước 5 : Sử dụng library do  BitWatchKit cung cấp
Mở Project dưới  dạng  Navigator và chọn BitWatch WatchKit Extension. Tiếp đến chọn General-> Linked Frameworks and Libraries. Đến đây bạn chọn + và tìm gắn thêm BitWatchKit.framework vào để sử dụng.

Bước 6: Lập trình
Chọn file tên là InterfaceController.swift  và  gắn vào phần import như sau.
import BitWatchKit
Library này cho phép bạn dùng được class Tracker.
Tiếp theo ta khai báo các biến sau đây.
let tracker = Tracker()
var updating = false 
Để có thể cập nhật nhanh các giá trị của Bitcoin ta thiết lập 1 method
private func updatePrice(price: NSNumber) {     priceLabel.setText(Tracker.priceFormatter.stringFromNumber(price))  
}
Đây là method chuyển giá trị từ NSNumber sang String để hiện lên trên màn hình của Apple Watch.

private func update() {
  // 1
  if !updating {
    updating = true
    // 2
    let originalPrice = tracker.cachedPrice()
    // 3
    tracker.requestPrice { (price, error) -> () in
      // 4
      if error == nil {
        self.updatePrice(price!)
      }
      self.updating = false
    }
  }
}
Ta xây dựng thêm 1 method để lấy cache của dữ liệu vừa lây được , đồng thời request Bitcoin Price thành công ta sẽ  gọi method  là updatePrice().

Bước 7 : Thiết lập  LifeCycle cho xử lí chính ở  InterfaceController.swift
■Đối với method là  override func awakeWithContext :
 ở method này cac UI Interface, cac action va outlet  sẽ được cập nhật tức thời cho xử lí
updatePrice(tracker.cachedPrice())
■  Đối với method là override func willActivate:
Cũng giống như viewWillActive trong iOS, đây là method để cập nhật và biểu thị dữ liệu trên màn hình Apple Watch.
Chúng ta gọi method la update() để lấy dữ liệu mới nhất cần được biểu thị.
update()
■ Gắn thêm button để refresh dữ liệu như bên dưới
  @IBAction func refreshTapped() {
    update()
  }
Bước 8 :  Chạy kiểm tra  Application thử 
Để chạy được màn hình mô phỏng chạy trên Apple Watch , các bạn mở iOS Simulator lên trước. Chọn Hardware => External Display và chọn 1 trong những loại Apple Watch cần dùng để kiểm tra. Như vậy bạn sẽ phải có 2  Simulator để kiểm tra , 1 màn hình để chạy Iphone application và 1 màn hình để chạy Apple Watch application.
Trở lại với cung cụ lập trình Xcode. Ở trên Xcode Toolbar bạn chọn BitWatch Watch App  và iphone 6 Simulator.
Tiếp theo bạn Build và Run  application , chờ đợi kết quả được biểu thị trên man hình của Apple Watch Simulator như ở bên dưới.

Bước 9 :  Bổ sung thêm chức năng biểu thị thời gian cập nhật và biến đổi tăng giảm của Bitcoin 
Như vậy bạn đã hoàn thành được những bước cơ bản để biểu thị giá trị Bitcoint lên  Apple  Watch. Tiếp theo , chúng ta sẽ bổ sung thêm thời gian cập nhật và ảnh biểu thị giá trị tăng giảm của  giá trị Bitcoin.

Chọn  Images.xcassets ở bên  BitWatch Watch App 1 ảnh biểu thi tăng giảm của Bitcoin vào trong thư viên ảnh của Xcode.Ở đây là ảnh tương ứng với màn hình Retina nên bạn chọn ảnh có tên ***@2x.png . Ở đây ta chọn thêm vào 2 ảnh  Down@2x.png và Up@2x.png bằng cách rê chuột và thả vào Images.xcassets tương ứng.
Tiếp theo bạn chọn Interface.storyboard và hiểu chỉnh thêm như bên dưới .

       ■UIButton hiên tại sửa phần Title là Refresh.
   ■Chọn và thêm UILabel được cài đặt như sau
  •  Text : Last Updated 
  •  Font : CustomAvenir NextRegularvà Size 13
  •  Text alignment : center 
  •  Horizontal position : Center 
  •  Vertical position : Bottom
Bước 10 :  Tạo Group bao gồm 1 UILabel va  1 image  để biểu thị sự tăng giảm của giá trị Bitcoin như bên dưới
Tạo và rê 1 group từ object library. Chú ý cài đặt Layout setting là Horizontal ở trong mục Attributes inspector.
Tiếp theo tạo và rê 1 image và 1 UILabel vào bên trong group vừa mới cài đặt. Ta được kết quả như bên dưới.
Hiệu chỉnh cho UILabel va Image này như sau.
  • Đối với UILabel
    • Text title : 1 BTC
    • Horizontal position : Center
    • Vertical position     : Center
    •  Font : CustomAvenir NextRegular, and Size 13 
  • Đối với Image
    • Horizontal position : Center
    • Vertical position     : Center
    • Size ở thanh Attributes inspector chọn Fixed Width và Fixed Height và cài đặt cả 2 giá trị là 32 
Bước tiếp theo ta cần phần khai báo outlet cua Image , click vào Image và rê vào file  tương ứng như bước đầu hướng dẫn. Ta đặt tên là image và chọn Connect.
Tương tự như trên ta chọn UILabel và đặt tên la lastUpdateLabel và chọn Connect.
Bước 11 :  Tính toán biểu thị image và  thời gian cập nhật.
Mở file có tên là InterfaceController.swift và thêm 2 method sau vào
  • Cài đặt cập nhật mới nhất về thời gian 
private func updateDate(date: NSDate) {
  self.lastUpdatedLabel.setText("Last updated \(Tracker.dateFormatter.stringFromDate(date))")
}
  • Biểu thị image tăng giảm của giá trị Bitcoin 
Ở đây tăng hay giảm đều được biểu thị 1 image tương ứng. Nếu không thay đổi gì ta ẩn image biểu thị
private func updateImage(originalPrice: NSNumber, newPrice: NSNumber) {
  if originalPrice.isEqualToNumber(newPrice) {
    // 1
    image.setHidden(true)
  } else {
    // 2
    if newPrice.doubleValue > originalPrice.doubleValue {
      image.setImageNamed("Up")
    } else {
      image.setImageNamed("Down")
    }
    image.setHidden(false)
  }
}

Các bạn đừng quên thêm 2 dòng code này vào bên dưới method có tên là awakeWithContext .
    image.setHidden(true)
   updateDate(tracker.cachedDate())
Đồng thời bên trong method tên là update(), ở bên dưới điều kiện  if error == nil {
các bạn thêm 2 dòng code sau.
      self.updateDate(NSDate())
self.updateImage(originalPrice, newPrice: price!)
 Cuối cùng các bạn build va run application để có được kết quả như bên dưới.

Như vậy là ta đã hoàn thành xong 1 application đơn giản cho Apple Watch . Nếu có gì thắc mắc cần thảo luận , xin các bạn comment vào bên dưới . Mình sẽ kịp thời thảo luận và giải quyết cùng các bạn. Chúc các bạn thành công!!!

Đây là toàn bộ source code cho hướng dẫn bên trên . Các bạn có thể download và thảo luận ở bên github repository . Lấy source-code ở đây


        



2014年11月30日日曜日

iOS8 and Swift language share

I have collected some reference resources for new iOS8 & Swift language written by some Japanese top engineers. They show some features of iOS8 and some differences with Swift language when comparing Swift to Objective-C.
1. iOS 8 / Swift introduction



虚数は作れる!Swift で学ぶ複素数 from Taketo Sano


3. Siori application written by Swift language



Ios8yahoo swift-json from dankogai

5. Swift and iOS8 SceneKit


2014年4月29日火曜日

100万個目の素数を求めるJavaソース


Java言語では素数を定義するライブラリーもあるので、そのため以下のソースコードのように簡単な素数を求めるプログラムです。
同様に100万個目の素数を求めるJavaソースを次に示します。
public class Prime {                                
                                                    
  private boolean isprime( int i )                  
  {                                                 
    int     j;                                      
                                                    
    for( j=2; j*j <= i ; ++j )                      
      if( i%j == 0 )                                
        return  false;      /* not prime */         
    return  true;                      /* prime */  
  }                                                 
                                                    
  public Prime()                                    
  {                                                 
    int     c = 0;                                  
    int     p;                                      
    int     maxprime = 0;                           
                                                    
    for( p=2; c<1000000; ++p )                      
      if( isprime( p ) ) {                          
        maxprime = p;                               
        ++c;                                        
        /*      printf( "%d\t%d\n", c, p );     */  
      }                                             
                                                    
    System.out.println( c + "\t" + maxprime );      
  }                                                 
                                                    
  public static void main(String[] args) {          
    new Prime();                                    
  }                                                 
}                                                   

timeコマンドで計測した実行時間は約6分3秒です。gccでのコンパイル時に最適化オプションを変更するなど チューニングを施せばCがJavaより速くなる可能性はあります。

$ java -version
java version "1.3.1"
Java(TM) 2 Runtime Environment, Standard Edition (build Blackdown-1.3.1-FCS)
Java HotSpot(TM) Client VM (build Blackdown-1.3.1-FCS, mixed mode)
$ javac -O Prime.java
$ time java Prime
1000000 15485863

real    6m2.813s
user    5m48.870s
sys     0m0.400s


しかし、プログラムを現代的に並行処理させていたので、Executor を使って平行処理プログラムを組んでみました。もちろん、先のプログラムより処理時間が短縮されているだろうと予想される。
結果は。。。。。。
973815個の素数を検出しました。
0時間0分14秒2449762050000004
おおっ! 凄いぞ! さすが Java5 の主役の Executor だ!
***************************
以下のソースコードを参照してください。

package  prime.test.primenumber4executor;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PrimeNumber4Executor implements Callable<List<Integer>>{
    private static final int UP_NUMBER = 32_000_000;
    private final int from;
    private final int to;
    public PrimeNumber4Executor(final int from, final int to) {
        this.from = from;
        this.to = to;
    }
    public static void main(String[] args) {       
        long startTime = System.nanoTime();        
        final int procs = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
        ExecutorService executor = Executors.newFixedThreadPool(procs);
        final int range = UP_NUMBER / procs;

        List<Future<List<Integer>>> futures = new ArrayList<>();
        for (int i = 0; i < procs; i++) {
           final int from = i * range + 1;
            final int to = (i + 1) * range;
            futures.add(executor.submit(new PrimeNumber4Executor(from, to)));
        }        
        executor.shutdown();        
        List<Integer> totalPrimes = new ArrayList<>();        
        for (Future<List<Integer>> future : futures) {
            try {
               List<Integer> primes = future.get();
               totalPrimes.addAll(primes);
            } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(PrimeNumber4Executor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }        
        System.out.println(totalPrimes.size() + "個の素数を検出しました。");
        long time = System.nanoTime() - startTime;
        System.out.println((int) (time * 1e-9) / 3_600 + "時間"
                + (int) ((time * 1e-9) / 60) % 60 + ""
                + (int) (time * 1e-9 % 60) + "秒"
                + Double.toString((time * 1e-9 % 60) % 1).substring(2));       
//        for (Integer result : totalPrimes){
//            System.out.println(result);
//        }       
    }    
    @Override
    public List<Integer> call() throws Exception {
        List<Integer> primes = new ArrayList<>();
        for (int i = from; i < to + 1; i++) {           
            if ((i & 0b1) == 0 && i > 0b10) {
                continue;
            }
            int j;
            for (j = (int) Math.sqrt(i); i % j != 0; j--) {            
            }                   
            if (j == 1 && i != 1) {
                primes.add(i);
            }
        }
        return primes;
    }   
}

そもそも、Java8 時代の素数を求めるプログラムを組んでみました。


package  prime.test.primenumber8executor;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PrimeNumber8ParallelStream {
private static final int UP_NUMBER = 32_000_000;
   
 public static void main(String[] args) {
long startTime =System.nanoTime();
        List<Integer> primeList = IntStream.rangeClosed(1, UP_NUMBER)
.parallel()
        .filter(n-> ((n & 0b1) != 0 || n == 0b10)&& n > 0b1)
                 .filter(PrimeNumber4ParallelStream::isPrime)
                .boxed()
                 .collect(Collectors.toList());
        System.out.println(primeList.size() + "個の素数を検出しました。");
        long time =System.nanoTime() - startTime;
        System.out.println((int) (time * 1e-9) / 3_600 + "時間" 
+(int) ((time * 1e-9) / 60) % 60 + ""
+(int) (time * 1e-9 % 60) + ""             
+Double.toString((time * 1e-9 % 60) % 1).substring(2));
//        primeList.forEach(System.out::println);
    }
    private static boolean isPrime(int number) {
        return IntStream.rangeClosed(2, (int) Math.sqrt(number))
.allMatch(n-> (number % n) != 0);
    }
}



凄くシンプルで Java5 時代の Executor より綺麗です! 
それに Java8 に慣れるとこちらの方がソースコードの可読性が良いと思うようになります。 さぁ、Java8 時代の素数を求めるコードの処理速度はどうなんだ!? 
1973815個の素数を検出しました。
0時間0分3秒5042672730000004
なんと! Executor 使って並行処理してプログラムより5倍近く高速に処理されてます!
この Java8 時代のプログラムですが 20 行目の parallel() メソッドをコメントアウトして
シーケンシャル処理させると処理時間は次のようになりました。
1973815個の素数を検出しました。
0時間0分49秒009503713000000857
一番最初に紹介した古いプログラムよりかは高速ですね。でも。遅いよね。