Tag: ios

  • Generic Cache từ NSCache

    Generic Cache từ NSCache

    Chào mọi người, mình là Lâm.

    Trước đây mình có đọc 1 bài viết hay về việc tự custom 1 class để phục vụ cho việc cache dữ liệu (nguồn bài viết: https://www.swiftbysundell.com/articles/caching-in-swift/ )
    Mình đã áp dụng nó vào 1 số project mini của mình, thấy hữu ích nên ở bài viết này mình xin được trình bày lại, cũng như có 1 số phân tích của bản thân để đâu đấy có thể giúp clear hơn, giúp mn tiết kiệm thời gian đọc hơn.

    Note: source code mình có để ở link git bên dưới bài viết.

    Tóm tắt:

    • Cache là 1 chủ đề quen thuộc của mobile app, cache dữ liệu để ko phải load lại nhiều lần, tối ưu cho app, or khi không có mạng thì mình vẫn xem được những nội dung đã cache.

    • Ở bài viết này mình sẽ trình bày cách viết 1 class Cache based trên 1 class build-in của iOS là NSCache (https://developer.apple.com/documentation/foundation/nscache).
      NSCache nó có 1 cái rất hay là nó sẽ tự động remove record khỏi cache khi bộ nhớ của app bị warning, hay nói đơn giản app đang ăn quá nhiều RAM, vì nó là build-in class nên việc này chắc chắn là đã được optimize rồi, nó còn 1 số optimize khác nữa mn đọc trong document refer ở bên trên.

    • Câu hỏi là sao ko dùng luôn NSCache mà phải viết 1 class trên nó làm gì? Lý do là bởi vì NSCache nó là class thuộc về Objective-C,  khi cache nó yêu cầu key phải là 1 NSObject, và value phải là 1 class instance (ko cần phải là NSObject nhưng phải là instance của 1 class).
    • ( NSString là 1 NSObject, và MyClass là 1 custom class)
    • Swift thì lại hay dùng struct, bây giờ muốn cache được cả class, struct… thì phải đi đường vòng 1 chút. Ý tưởng là mình sẽ wrap key/value thực sự của mình vào trong NSObject/Class để có thể adapt được với NSCache.

    1. Tạo Custom class:

    Tạo 1 class tên Cache, với 2 generics là Key và Value. Key phải là Hashable thì nó với làm key được, vì nó phải là unique.

    Tiếp theo tạo 2 inner class để wrap key/value:


    Class WrappedKey là 1 subclass của NSObject để nó có thể trở thành key của NSCache, có property key bên trong là key thực sự mà mình sẽ dùng, type của key là Key được resolve từ outer class là Cache.

    Đến đây mn cũng có thể hình dung vì sao WrappedKey phải là inner class của Cache, vì nó phải resolved cái generic Key của Cache, nếu nó là 1 class nằm bên ngoài Cache thì nó sẽ ko nhìn thấy generic của Cache.  Tiếp theo, mình sẽ override lại property hash:Int và func isEqual(_ object: Any) của NSObject và implement lại dưới cái key của mình (vì đây với là cái key thực sự).

    Tương tự với class Entry (hay có thể gọi là WrappedValue, gọi Entry nghe ngắn hơn). Entry thì ko cần phải unique nên ko cần override lại hash, và cũng ko cần là subclass của NSObject, cứ wrap value của nó vào là được.

    Note:

    2. Cache’s methods:

    OK, với 3 class được dựng ở trên thì mình có thể viết 1 số API được rồi:

    nên viết 1 subscript để lúc sử dụng code nó ngắn gọn, đẹp hơn.

    3. Thêm constraint cho Cache

    Bây giờ mình muốn có thêm nhiều control hơn nữa với cache của mình:

    • Cache 1 số lượng record nhất định
    • Cache trong 1 khoảng thời gian nhất định

    NSCache có 1 property gọi là countLimit: Int, muốn cache bao nhiêu record thì mình gán giá trị cho nó, khi cache vượt quá số record này thì nó sẽ tự động remove record cũ đi. NSCache còn có 1 property gọi là totalCostLimit để đặt ra threshold về số byte sẽ được cache, tuy nhiên vì nó đã auto handle memory rồi nên cái này chắc cũng hiếm dùng.
    Về thời gian cache, mình sẽ tạo biến entryLifeTime để giữ lượng thời gian, tính bằng giây (TimeInterval).

    cần phải update lại Entry, lúc này Entry của ngoài giữ value còn giữ cả expirationDate nữa, mỗi entry sẽ có expirationDate của nó:

    Sau đấy update lại method của Cache:

    • Với method insert, trước khi insert mình sẽ tính thêm expirationDate cho entry
    • Với method value(forKey: ), mình sẽ check xem entry đấy đã bị expired hay chưa, nếu rồi thì remove nó.

    4. Persistent Cache

    OK, khá ngon rồi, nhưng mà cache hiện vẫn đang chỉ là in-memory, tức là trên RAM thôi. Bây giờ mong muốn là phải save được nó xuống disk, để sau này lấy ra.
    Vấn đề của NSCache là nó ko public ra cho mình thông tin keys  –> dẫn đến việc giả sử mình có save được cache rồi, lúc lấy lại ra cũng ko thể lấy được value, vì mình ko có keys –> mình phải tự control list key của mình bằng cách tạo thêm 1 inner class KeyTracker.


    class này sẽ giữ 1 set keys, và nó sẽ là delegate cho NSCache. Sau này khi NSCache tự động remove record, nó sẽ bắn về delegate method là cache(_ cache: , willEvictObject: Any), response của mình với method này là sẽ remove key tương ứng với record đấy ra khỏi set keys.

    Tuy nhiên delegate này trả về cho mình object sẽ bị remove (ở đây là Entry), làm sao mình biết được là key nào đang associated với object đấy? Mình đang bị lack data,  ở Entry mình cần phải giữ thêm key associated với nó.

    và update method khi insert data (add thêm key vào khi init entry):

    OK, vậy là đã xử lý xong vấn đề về keys, bước cuối để có thể save được cache là cache của mình phải serialize được thành data để lưu, và lúc mình lấy cục data đấy ra thì mình phải deserialize ngược lại được thành object,  hay nói đơn giản là encode/decode. Vì mình làm việc với json từ API, nên dùng Codable ở đây là hợp lý nhất.

    Đầu tiên phải serialize được Entry, vì vậy nên sẽ Entry sẽ phải là Codable.  Tuy nhiên mình nếu fix như vậy thì generic của mình sẽ bị cứng, khi tạo cache sẽ require Entry bắt buộc phải là Codable, ko hay. Thế nên ở đây sẽ dùng kỹ thuật gọi là protocol conditional conform, hay nói đơn giản là 1 type sẽ chỉ conform vào protocol dưới 1 số điều kiện nhất định.

    Ở đây mình chỉ muốn Entry conform vào Codable dưới điều kiện là Key và Value của nó Codable:

    Trong quá trình encode/decode, sẽ cần cả insert và retrieve những entries hiện tại, nên sẽ tạo 2 methods để handle việc này, nên đặt tên khác 1 chút để  phân biệt tránh bị nhầm lẫn với API của Cache, và nên mark nó là private luôn, vì nó ko phải API của cache.


    Với 2 methods này bây giờ mình có thể encode cục Cache rồi:

    • Khi encode thì sẽ sử dụng list keys ở keyTracker + map với method entry(forKey key: Key) để ra 1 list Entry, và encode list này bằng singleValueContainer
    • Khi decode thì lấy ngược lại list Entry đã encode và add lại vào cache bằng method insert(_ entry: Entry).

    Có thể đọc thêm về encode/decode bằng singleValueContainer ở đây: https://medium.com/swiftly-swift/swift-4-decodable-beyond-the-basics-990cc48b7375

    Công việc còn lại chỉ là write cái cục vừa encode đấy xuống file, để ý là write vào folder cache (.cachesDirectory) luôn cho nó chuẩn:

    5. Demo with App

    Để demo, hãy viết 1 app đơn giản lấy dữ liệu từ 1 open API: https://developers.themoviedb.org/3

    đây là API về phim, mình mê phim cho nên là quyết định dùng API này:D

    mỗi phim thì sẽ chưa thông tin gồm: Id/Name/Overview/posterImage

    Như vậy mình sẽ cần 2 cache: 1 cho thông tin về phim, 2 cho posterImage load được

    đang để access global luôn

    Khi lấy được dữ liệu từ API thì mình sẽ ghi nó vào cache:

    đây là khi API đã trả về response

    Và khi app xuống background thì mình sẽ save nó xuống disk, tuỳ vào bài toán, mình có thể đặt 1 cái timer cho nó 5 phút save 1 lần cũng được, ở đây mình làm thế này cho dễ test.

    M.n có để ý là với movieCache mình có thể gọi method saveToDisk được, nhưng với imageCache thì lại ko có method đấy? Lý do là vì protocol conditional conformance, nếu mn nhìn lại sẽ thấy method saveToDisk() chỉ được conform với điều kiện Key và Value đều Codable. imageCache value của nó là UIImage, UIImage ko Codable –> imageCache sẽ ko conform vào method này, và sẽ ko visible luôn, có thể đổi UIImage thành Data thì sẽ lại conform.

    OK, sau khi mà save cache xuống disk rồi thì nó sẽ nằm ở folder cache: 

    bây giờ sẽ lấy ngược lại cache ra khi mở app, ở đây mình lấy ở application didFinishLaunching và ở main thread luôn:

    và mình viết thêm 1 computed property nữa để lấy ra list Movie đã cache, 1 cái conditional conformances nữa, property  listCacheMovie này sẽ chỉ visible với movieCache, hợp lý đúng ko?

    bây giờ giả sử trong case API fail thì mình sẽ dùng cache, ví dụ vậy đi:

    Lúc đầu mình có nói là nếu mình đặt countLimit cho cache thì vượt quá số đấy nó sẽ tự delete record đi, để test mn có thể pull source về rồi đặt print ra console để xem new recrod được insert vào và old record bị remove đi liên tục nhé.

    Link source: https://github.com/nguyenlam96/CacheIt

    Bài viết cũng đã khá dài rồi, nhưng mà mình tin nó cũng gói gọn được 1 vấn đề, hy vọng mn cảm thấy hữu ích. 
    Thanks for reading.

  • iOS/Swift – View Controller Lifecycle

    iOS/Swift – View Controller Lifecycle

    Lời mở đầu

    Nói về ViewController thì chắc hẳn tất cả iOS Developer đều biết đến và đã sử dụng rất nhiều. Nhưng đối với các bạn mới bắt đầu với iOS, mọi người thường không chú ý nhiều đến vòng đời của ViewController, dẫn đến mắc phải một số lỗi không đáng có.
    Bài viết này mình sẽ giới thiệu cho các bạn mới bắt đầu với iOS về vòng đời của View Controller và cách sử dụng để tránh những lỗi không đáng có.

    View Controller Lifecycle là gì?

    View Controller lifecycle là vòng đời của một view controller được tính từ lúc nó được nạp vào bộ nhớ (RAM) cho tới khi nó bị giải phóng khỏi bộ nhớ.

    Phân tích vòng đời của View Controller

    Dưới đây là sơ đồ về vòng đời của nó:

    Như các bạn đã thấy, trên sơ đồ này có khá nhiều trạng thái mà các bạn chắc dã nhìn thấy rất nhiều nhưng một số thì không phải không 😀
    Và nó cũng là các phương thức tương ứng được gọi tự động trong vòng đời của View Controller

    OK, bây giờ chúng ta sẽ đi vào chi tiết.

    loadView

    Phương thức này được gọi khi View hiện tại đang bằng nil. Cơ bản nó sẽ đưa View mà bạn tạo trong phương thức này vào view của ViewController.

    NOTE: Phương thức này được sử dụng khi View Controller được tạo bằng code. Nếu chúng ta tạo View Controller từ file .xib hoặc storyboard thì tốt nhất không sử dụng hương thức này.

    viewDidload:

    Phương thức này được gọi một lần duy nhất trong vòng đời của ViewController. Nó được gọi khi tất cả các view đã được load vào bộ nhớ(RAM).

    Ứng dụng:
    1. Khi bạn muốn cài đặt giao diện người dùng (User Interface)
    2. Những công việc mà bạn muốn nó chỉ chạy duy nhất một lần trên View Controller này.

    viewWillAppear:

    Phương thức này được gọi mỗi lần trước khi nội dung của View được thêm vào view hierarchy của ứng dụng.

    Ứng dụng:
    Vì phương thức này sẽ được gọi mỗi lần trước khi View được xuất hiện nên nó thường dùng khi bạn muốn 1 công việc nào đó luôn được gọi mỗi khi View Controller đó hiển thị trên màn hình.
    VD: Kiểm tra kết nối mạng, kiểm tra service state, add observer Notification v.v.

    NOTE:
    • Tránh làm các công việc mà bạn chỉ muốn thực hiện nó một lần trong vòng đời của View Controller trong phương thức này.
    • Nếu bạn add observer notification ở hàm này thì cần remove notification ở phương thức viewDidDisappear:. Để tránh trường hợp khi quay trở lại màn hình này hàm add observer notification sẽ được đăng kí một lần nữa -> nó sẽ thực thi hàm trong #selector nhiều lần.
    • Với tương tác tầng Application (VD: Bấm Home, show notification center, show control center… ) rồi trở lại ứng dụng thì sẽ không kích hoạt phương thức này mà nó sẽ kích hoạt các phương thức của UIApplication.

    viewDidAppear:

    Phương thức này được gọi mỗi lần sau khi nội dung của View được thêm vào view hierarchy của ứng dụng.

    Ứng dụng:
    Thường được sử dụng để lưu dữ liệu, bắt đầu animation, bắt đầu chơi Video hoặc âm thanh hoặc thu thập dữ liệu từ network.

    NOTE:
    • Tương tự như viewWillAppear, với tương tác tầng Application (VD: Bấm Home, show notification center, show control center… ) rồi trở lại ứng dụng thì sẽ không kích hoạt phương thức này mà nó sẽ kích hoạt các phương thức của UIApplication.
    • Nếu bạn add observer notification ở hàm này thì cũng phải xóa notification ở viewDidDisappear:

    viewWillDisappear:

    Phương thức này được gọi trước khi view được xóa khỏi view hierarchy. View vẫn còn trên view hierarchy nhưng chưa được xóa.

    Ứng dụng:
    Thường dùng để quản lí các timer, ẩn bàn phím, hủy các network request và lưu lại các trạng thái.

    viewDidDisappear:

    Phương thức này được gọi sau khi view của ViewController được xóa khỏi view hierarchy.

    Ứng dụng:
    Thường sử dụng để hủy việc lắng nghe các thông báo (Notification) hoặc các cảm biến của thiết bị các trên màn hình này.

    deinit:

    Phương thức này được gọi trước khi một view controller bị xóa khỏi bộ nhớ.

    Ứng dụng:
    Thường được sử dụng để xóa tài nguyên mà view controller đã được phân bổ nhưng không được giải phóng bới ARC(Automatic reference counting)

    NOTE:
    Hãy nhớ rằng một view controller không còn hiển thị trên màn hình nữa không có nghĩa là nó đã được giải phóng. Ngay cả khi màn hình bị tắt nếu nó vẫn còn trong bộ nhớ thì nó vẫn hoạt động như thường.

    didReceiveMemoryWarning:

    Phương thức này được gọi khi bộ nhớ (RAM) của máy gần đầy. Và iOS không tự động di chuyển dữ liệu từ bộ nhớ sang không gian ổ cứng hạn chế của nó.

    Ứng dụng:
    Xóa một số đối tượng ra khỏi bộ nhớ.

    NOTE:
    Hãy nhớ rằng nếu bộ nhớ của ứng dụng vượt quá một ngưỡng nhất định, iOS sẽ tắt ứng dụng của bạn. Và nó trông giống như là ứng dụng bị crash


    Cảm ơn mọi người đã theo dõi bài viết!
    Mọi ý kiến đóng góp mọi người hãy comment xuống phía dưới để mình có thể thay đổi cho bài biết được tốt hơn. :v

  • Cách lấy UDID của thiết bị iPhone, iPad, iPod…

    Cách lấy UDID của thiết bị iPhone, iPad, iPod…

    + UDID là gì?
    UDID là viết tắt của Unique Device Identifier là hình thức cho nhận dạng thiết bị duy nhất và là một chuỗi chữ số xác định duy nhất iPad hoặc iPhone… của bạn từ tất cả các thiết bị khác.
    + UDID dùng khi nào?
    Nó dùng ở nhiều chỗ, nhưng dùng nhiều nhất với lập trình viên là để đăng kí thiết bị trên trang Developer Apple, chuẩn bị cho quá trình build file .ipa theo hình thức Adhoc…
    Dưới đây là các cách lấy UDID mà các bạn có thể tham khảo. ?

    Cách 1: Dùng thiết bị chạy MacOS.

    B1: Nối thiết bị iDevice với máy tính của bạn.
    B2: Mở “Finder” và click vào các bước giống hình.
    1) Nhấp chuột tên device.
    2) Nhấp chuột vào thông tin device, đến khi hiện ra thông tin UDID như hình.

    Cách 2: Dùng thiết bị chạy Window.

    Mở ứng dụng “iTunes”, chọn vào hình “thiết bị”.

    Chọn mục “Summary” , kích chuột vào dòng “Serial Number” đến khi hiện ra “UDID” như hình dưới.

    Hình mình mượn trên mạng vì ko có thiết bị window. 😀

    Cách 3: Dùng ngay iDevices.
    (Cách này khá hữu dụng, vì nó giúp lấy UDID ngay trên điện thoại…trong trường hợp khách hàng, hoặc sếp… của bạn ở xa yêu cầu gửi cho bản build .ipa để cài đặt… thì bạn có thể gửi ngay hướng dẫn này cho họ ?)


    Truy cập đường link https://get.udid.io/ và làm theo các bước dưới hình

    Kết quả là dãy số ở bước 11.
    Trên đây là 1 số cách mình thấy khá thông dụng, tất nhiên còn nhiều hình thức khác.
    Hi vọng bài viết hữu dụng cho các bạn. ?

  • Cách upload 1 App iOS lên Deploygate

    Cách upload 1 App iOS lên Deploygate

    Xin chào mọi người, hôm nay mình sẽ hướng dẫn các bạn cách upload 1 app lên Deploygate

    Thông thường việc cài đặt ứng dụng sẽ được thực hiện thông qua App Store, tuy nhiên sẽ có một số trường hợp sẽ ta không cần phải đưa app lên App Store mà thiết bị vẫn có thể cài đặt được. đa số sẽ thuộc vào một trong hai trường hợp sau :

    • Testing: Trước khi release app, ta cần test ứng dụng, vì vậy việc cung cấp bản build để tester có thể test trước khi release là một điều cần thiết
    • In-house Applications: Là những ứng dụng chỉ được sử dụng internal trong một công ty hay tổ chức nào đó ( đối với những ứng dụng In-house application, ta cần có tài khoản Apple Developer Enterprise Program)

    Những điều bắt buộc

    1. Valid Apple developer program account (not the Apple Developer Enterprise Program)
    2. Máy tính chạy Mac OS X
    3. Đã cài Xcode

    Tổng quát

    Bên dưới là danh sách các bước bắt buộc để submit 1 app

    1. Tạo 1 record của app trên iTunes Connect
    2. Cấu hình XCode project cho việc distribution
    3. Export ipa file from xcode
    4. Upload app lên deploygate

    1. Tạo 1 record của app trên iTunes Connect Bạn phải tạo 1 record của app trên iTunes Connect trước khi bạn upload app lên App Store. Nó sẽ chứa tất cả thông tin cần thiết để có thể quản lý để xử lý và hiển thị app trên App Store. Xem thông tin chi tiết tại đây

    2. Cấu hình XCode project cho việc distribution Bạn phải nhập các thông tin để chứng thực app: Identity, Team, Bundle ID, import provisioning file, set version number,… Tạo 1 provisiong profile Xem thông tin chi tiết tại đây

    3. Export ipa file from xcode

    – Achieve app: bước đầu tiên bạn tạo 1 bản lưu trữ của app để build và lưu trữ thông tin app.

    • Chọn scheme hiện tại của app: Ở mục Build only device -> Generic iOS Device
    • Sau đó, trên thanh status bar trên cùng, chọn Product -> Archive Rồi đợi Xcode nó archive app, khi xong thì vô Window -> Organizer để xem cái bản mình vừa mới archive được, bạn cũng có thể xem các bản archive trước đó

    – tiếp theo chúng ta export ra file ipa Xem thông tin chi tiết ở đây

    4. Upload app lên deploygate

    bước 1: đăng ký tài khoản deploygate link đăng ký ở đây

    bước 2: sau khi đăng ký xong ta chọn account -> Organizations -> Create

    • tiếp theo ta điền đầy đủ các thông tin ở trong hình -> Create -> Finish

    bước 3 : Sau khi ấn Finish thành công thì việc còn lại của chúng ta là upload file ipa mà chúng ta export từ xcode lên đây

    • chọn upload App -> tìm tới folder lưu file ipa -> open -> upload
    • việc tiếp theo là chờ đợi tới khi việc upload thành công -> sẽ hiển thị ra một màn hình như bên dưới, tới được bước này thì xin chúc mừng các bạn đã upload thành công app của mình lên deploygate rồi đấy ^^
    • muốn lấy link để tải app của mình cho mọi người, ta chọn vào Add a link for sharing -> hệ thống sẽ tự sinh ra cho mình một link để tải app 😀

    Chúc Các bạn thành công ^^

  • Tổng quan về Mobile App

    Tổng quan về Mobile App

    Ghi chú:  Bài viết này chỉ là một góc nhìn chủ quan của tác giả về mảng mobile app. vì vậy có gì không đúng mọi người có thể đóng góp ở phần comment nhé!! Thank.

    Mở đầu:
    Ở thời điểm hiện tại việc xây dựng ứng dụng native không phải là lựa chọn duy nhất để tạo lên một một ứng dụng mobile app. Ngày nay chúng ta có thể dựa vào yêu cầu của khách hàng, các chức năng của sản phẩm để lựa chọn được hướng đi phù hợp hơn. Ta có thể dựa trên vào công nghệ web (HTML5, CSS3 và JavaScript) đang phát triển mạnh mẽ trên mobile. Hoặc tận hưởng những lợi ích của các công cụ phát triển đa nền tảng như React Native hoặc Flutter. Dưới đây, bạn sẽ tìm thấy chìa khóa để giải quyết vấn đề khó khăn này khi chọn phương pháp phát triển ứng dụng di động.

    Native App

    Native app hay còn được gọi là ứng dụng gốc. Vốn dĩ nó có cái tên này là bởi vì nó được viết bằng chính các ngôn ngữ lập trình gốc thần nhất dành riêng cho từng nền tảng cụ thể. Hai nền tảng di động phổ biến nhất hiện nay là Android và iOS (Windows Phone thì đã bị khai tử vào tháng 10/ 2017 ). Từ đó, các ngôn ngữ lập trình tương ứng được chính các công ty mẹ tạo ra phù hợp với từng nền tảng. Chẳng hạn như Apple đã có Swift, Objecive-C được dành cho lập trình ứng dụng trên nền tảng iOS. Lập trình trên Android thì dùng Java, mặc dù đây không phải ngôn ngữ do Google tạo ra.

    Viết Native App nghĩa là lập trình viên sẽ sử dụng IDE, SDK mà nhà sản xuất cung cấp để lập trình ra một ứng dụng, build ứng dụng đó thành file cài và gửi lên App Store để kiểm duyệt. Người dùng sẽ phải tìm ứng dụng trên App Store, tải về máy và chạy.  

    Với những hệ thống lớn, cần đồng bộ, ta vẫn phải viết phần back-end trên server. Server sẽ đưa ra một số API. Native app lấy dữ liệu về máy, truyền dữ liệu lên server thông qua các API này.

    Ưu điểm

    • Tận dụng được toàn bộ những tính năng của device: Chụp ảnh, nghiêng máy, rung, GPS, notification.
    • Có thể chạy được offline.
    • Performance rất nhanh vì code native sẽ được chạy trực tiếp.
    • UX phù hợp với từng nền tảng
    • Là lựa chọn duy nhất cho các ứng dụng game, xử lý hình ảnh hay video …

    Khuyết điểm

    • Cần cài đặt nặng nề (Android Studio, XCode, Android SDK, …), khó tiếp cận.
    • Với mỗi hệ điều hành, ta phải viết một ứng dụng riêng. Khó đảm bảo sự đồng bộ giữa các ứng dụng (1 button trên Android sẽ khác 1 button trên iOS, pop cũng khác).
    • Cần phải submit app lên App Store, mỗi lần update phải thông báo người dùng.
    • Code mệt và lâu hơn so với Mobile Web dẫn đến một khuyết điểm là chi phí phát triển cao.

    Kĩ năng cần có

    • Ngôn ngữ lập trình: Java / Kotlin cho Android, Objective-C / Swift cho iOS
    • Kiến thức chuyên sâu về ứng dụng: View, Action, Adapter trong Android …
    • Cách xây dựng Web Serivce, Restful API, cách gọi API từ device, …

    __________________________________________________________________________

    Hybrid App

    Hybrid App kết hợp những ưu điểm của Mobile Web và Native App. Ta xây dựng một ứng dụng bằng HTML, CSS, Javascript, chạy trên WebView của mobile. Tuy nhiên, Hybrid App vẫn có thể tận dụng những tính năng của device: chụp hình, GPS, rung, ….

    Hybrid App sẽ được viết dựa trên một cross-platform framework: Cordova, Phonegap, Ionic …. Ta sẽ gọi những chức năng của mobile thông qua API mà framework này cung cấp, dưới dạng Javascript. Bạn chỉ cần viết một lần, những framework này sẽ tự động dịch ứng dụng này ra các file cài đặt cho Android, iOS . Một số ứng dụng không quá nặng về xử lý, cần tận dụng chức năng của device sẽ chọn hướng phát triển này.

    Ưu điểm

    • Chỉ cần biết HTML, CSS, JS .
    • Viết một lần, chạy được trên nhiều hệ điều hành
    • Tận dụng được các chức năng của device.

    Khuyết điểm

    • Không ổn định, khó debug. Framework sẽ dịch code của bạn thành code native, việc sửa lỗi ứng dụng khá khó vì bạn không biết code sẽ được dịch ra như thế nào.
    • Performance chậm.
    • Cần cài đặt nhiều thứ (phải cài đặt SDK này nọ thì mới build ứng dụng được).

    Kiến thức cần biết

    • HTML, CSS, Javscript cơ bản.
    • Cách dùng một số framework CSS, Javascript: jQuery Mobile, Ionic Framework, AngularJS, Bootstrap, …
    • Kiến thức về các cross-platform framework: Cordova, Phonegap
    • Cách xây dựng Web Serivce, Restful API, cách gọi API từ device, … (Hybrid app cũng sẽ kết nối với server thông qua API như Native App).

    __________________________________________________________________________

    Cross-Platform App

    Được sinh ra nhằm mục đích để giải quyết bài toán hiệu năng của Hybrid và bài toán chi phí khi mà phải viết nhiều loại ngôn ngữ native cho từng nền tảng di động. Nhưng chúng ta lại hay nhầm lẫn giữa Hybrid AppCross-Platform App, trên thực tế thì chúng khác hoàn toàn nhau. Có lẽ, đặc điểm chung duy nhất giữa chúng là khả năng chia sẻ source code. Lập trình viên chỉ cần lập trình một lần và biên dịch hoặc phiên dịch ra thành nhiều bản Native App tương ứng với từng nền tảng khác nhau.

    Công cụ quan trọng nhất để thực hiện các dự án ứng dụng đa nền tảng (Cross Platform) chính là Frameworks đa nền tảng. Có rất nhiều Framework đa nền tảng. Mỗi loại sẽ có những điểm mạnh và điểm yếu khác nhau. Tùy vào mục tiêu xây dựng App mà lập trình viên sẽ lựa chọn Framework nào cho phù hợp.

    Nổi tiếng và phổ biến nhất là Framework Xamarin. Ngôn ngữ lập trình chủ đạo trong Xamarin là C#, ngoài ra còn có Objective-C, Swift và Java. Ngoài ra, còn một số cái tên mà khá hot đó là React-Native (thằng này có ông bô là Facebook ), Flutter (thằng này có ông bác là Google)…

    Ưu điểm

    • Tận dụng được những tính năng của device: Chụp ảnh, nghiêng máy, rung, GPS, notification.
    • Hiệu năng tương đối ổn định.
    • Tiết kiệm tiền.
    • Hiệu quả về mặt thời gian khi mà bạn muốn phát triển một ứng dụng nhanh chóng.
    • Trải nghiệm người dùng tốt hơn là hybrid app.

    Nhược điểm

    • Hiệu năng sẽ thấp hơn với app native code.
    • Khó học vẫn đòi hỏi kiến thức native code.
    • Vẫn còn có hạn chế từ framework

    Kĩ năng cần có

    • Kiến thức C# (đối với Xamarin ), JS (đối với React-Native ), Dart(đối với Flutter) Objective-C, Swift và Java cơ bản.
    • Kiến thức về một số framework React-Native, Xamarin …

    __________________________________________________________________________

    Web App

    Hướng Mobile Web thường được áp dụng khi các bạn đã có sẵn một website đang hoạt động. Ta sẽ tạo thêm 1 trang web riêng cho mobile, sử dụng HTML, CSS, một số framework hỗ trợ mobile và responsive (Bootstrap, jQuery Mobile, Materialize). Người dùng sẽ trang web dành cho mobile để dùng ứng dụng.

    Các xử lý khác liên quan đến backend như database sẽ được thực hiện phía trên server. Với một số framework như Angular, VueJS … một trang web có thể giống y hệt một ứng dụng di động thật sự.

    Ưu điểm

    • Chỉ cần có kiến thức về web là viết được
    • Viết một lần, chạy được trên mọi hệ điều hành
    • Người dùng không cần phải cài app, có thể vào thẳng trang web
    • Không cần phải thông qua App Store, tiết kiệm tiền
    • Dễ nâng cấp (Chỉ việc nâng cấp web là xong)

    Nhược điểm

    • Với một số máy đời cũ, Web App sẽ bị bể giao diện, hiển thị sai, hoặc javascript không chạy.
    • Performance chậm
    • Không thể tận dụng được các tính năng của di động: Push notification, chụp hình, nghiêng máy, định vị GPS…

    Kĩ năng cần có

    • Kiến thức HTML, CSS, Javascript cơ bản.
    • Kiến thức về một số framework responsive/mobile như: jQuery Mobile, Bootstrap, …
    • Một số framework javascript để viết Single Page Application: AngularJS, VueJS, …

    Kết Bài

    Sorry các bạn bài viết hơi dài, sau khi nhìn tổng quan về mobile app thì các bạn đã chọn cho mình hướng đi nào chưa? còn mình thì sẽ tiếp tục theo hướng Cross-Platform app.
    Cảm ơn các bạn đã đọc đến đây nhé.

    Tham khảo: https://railsware.com/blog/native-vs-hybrid-vs-cross-platform/

  • iOS — Play RTSP Streaming

    iOS — Play RTSP Streaming

    Hướng dẫn build IJK Player để play RTSP streaming

    Table of Contents

    Chuẩn bị môi trường

    • Cài đặt Homebrew ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    • Cài git brew install git
    • Cài yasm brew install yasm
    • Clone IJKPlayer từ github:
      • git clone https://github.com/RioV/ijkplayer.git ijkplayer-ios
        • Note: ở đây dùng /RioV/ijkplayer bởi vì đang tìm hiểu thấy IJKPlayer có issue, vậy nên folk sang một bản khác để tiện fix bug
        • Note: chú ý checkout source code về folder mà tên không có space, ví dụ: IJK Player => NG, IJK-Player => OK. Việc này sẽ ảnh hưởng đến tiến trình build lib, nếu như có space thì build sẽ bị lỗi.
      • cd ijkplayer-ios
      • git checkout -B latest k0.8.8 version lấy theo release tag của IJKPlayer Release Nếu sau này sửa lỗi lib ở branch develop thì sẽ là git checkout -B develop

    Build lib IJKPlayer

    • cd config
    • rm module.sh
    • ln -s module-lite.sh module.sh -> việc này sẽ bỏ module.sh default và thay vào đó sử dụng moule-lite.sh nhằm giảm binary size
    • Để build lib support RTSP thì cần chỉnh sửa file module-lite.sh như sau:
      • Xoá: export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtp"
      • Add: export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
      • Add: export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp" (nên để trong section của demuxer)
    • cd ..
    • ./init-ios.sh
    • cd ios
    • ./compile-ffmpeg.sh clean
    • Sửa file ijkplayer-ios/ios/compile-ffmpeg.sh
      • Chuyển:

        FF_ALL_ARCHS_IOS6_SDK="armv7 armv7s i386" FF_ALL_ARCHS_IOS7_SDK="armv7 armv7s arm64 i386 x86_64" FF_ALL_ARCHS_IOS8_SDK="armv7 arm64 i386 x86_64"

        Thành

        FF_ALL_ARCHS_IOS8_SDK="arm64 i386 x86_64"

    • ./compile-ffmpeg.sh all
      • Note: Với câu lệnh ./compile-ffmpeg.sh all thì rất dễ xảy ra lỗi nếu như source code đang ở trong directoy có chứa space. Ví dụ: working directory là /Documents/JLK Player thì sẽ lỗi, để fix thì chuyển thành /Documents/IJKPlayer

    Tích hợp IJKPlayer vào project

    • Add IJKPlayer vào project: File -> add File to "Project" -> chọn ijkplayer-ios/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj
    • Chọn: Application’s target.
      • Vào: Build Phases -> Target Dependencies -> Chọn IJKMediaFramework
      • Chọn IJKMediaPlayer.xcodeproj, chọn target IJKMediaFramework và build.
      • Vào: Build Phases -> Link Binary with Libraries -> Thêm:
        • libc++.tbd
        • libz.tbd
        • libbz2.tbd
        • AudioToolbox.framework
        • UIKit.framework
        • CoreGraphics.framework
        • AVFoundation.framework
        • CoreMedia.framework
        • CoreVideo.framework
        • MediaPlayer.framework
        • MobileCoreServices.framework
        • OpenGLES.framework
        • QuartzCore.framework
        • VideoToolbox.framework

    Sample

    Sử dụng đoạn source code sau để play thử RTSP stream bằng IJKPlayer

    import UIKit
    import IJKMediaFramework
    
    class IJKPlayerViewController: UIViewController {
        var player: IJKFFMoviePlayerController!
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let options = IJKFFOptions.byDefault()
            let url = URL(string: "rtsp://170.93.143.139/rtplive/470011e600ef003a004ee33696235daa")
            guard let player = IJKFFMoviePlayerController(contentURL: url, with: options) else {
                print("Create RTSP Player failed")
                return
            }
    
            let autoresize = UIView.AutoresizingMask.flexibleWidth.rawValue |
                UIView.AutoresizingMask.flexibleHeight.rawValue
            player.view.autoresizingMask = UIView.AutoresizingMask(rawValue: autoresize)
    
            player.view.frame = self.view.bounds
            player.scalingMode = IJKMPMovieScalingMode.aspectFit
            player.shouldAutoplay = true
            self.view.autoresizesSubviews = true
            self.view.addSubview(player.view)
            self.player = player        
            self.player.prepareToPlay()
        }
    }