Showing posts with label iOS. Show all posts
Showing posts with label iOS. Show all posts

Saturday, September 8, 2018

Berkenalan dengan CocoaPods

What is CocoaPods?
Cocoapods adalah dependency-manager untuk XCode projects. Cara penggunaan yang mudah dan sederhana membuat Cocoapods banyak digunakan oleh Cocoa user. Oke, mungkin masih ada yang bingung apa itu dependency-manager. Dependency manager adalah suatu module yang akan mengatur dependency, sebut saja library, yang akan kita gunakan di project. Misalnya saja kita ingin membuat sebuah Picker dengan suatu tampilan yang bisa dicustomize, kita punya 2 pilihan yaitu membuatnya sendiri atau menggunakan 3rd party library, misalnya saja dari Kingfisher yang dibahas di artikel ini.
"Do not reinvent the wheel"
Familiar dengan kata-kata di atas? Dalam kasus ini kita bisa saja membuat Picker itu jika tidak ada orang di luar sana yang telah membuat dan membagikannya, namun jika ada library yang telah dibuat, dibagikan, dan memenuhi kebutuhan kita maka lebih baik jika kita menggunakannya. Dengan begitu maka kita bisa mengalihkan waktu pembuatan Picker untuk proses lain yang lebih membutuhkan. Cara paling mudah untuk menggunakan library yang sudah dibuat dan dibagikan oleh orang lain di github adalah menggunakan CocoaPods

Why use CocoaPods?
  1. Mudah di-install
  2. Mudah digunakan
  3. Banyak library yang sudah support instalasi menggunakan CocoaPods
  4. Besar komunitasnya
  5. Reliable


Installing CocoaPods
Instalasi sangat mudah, cukup buka terminal dan ketikkan line berikut :

sudo gem install cocoapods

How to use CocoPods
Buka terminal, lalu arahkan current directory ke dalam project anda lalu ketikkan line berikut :

pod init

Contohnya : 


Selanjutnya, di file project anda akan ada beberapa file baru yaitu Podfile, Podfile.lock, [nama_project].xcoworkspace, dan folder Pods.
Anda cukup memperhatikan Podfile dan [nama_project].xcoworkspace.

Podfile adalah tempat anda mendaftarkan library yang akan digunakan di project.
[nama_project].xcoworkspace adalah file yang perlu anda gunakan saat membuka project. Anda wajib  menggunakan .xcoworkspace jika menggunakan pods agar library yg anda gunakan bisa di-load ke project. Jika menggunakan .xcodeproj maka library yang diinstall mengunakan CocoaPods tidak akan dikenali di project.


Is there any alternatives?
Tentu saja ada alternatif lain dari CocoaPods, alternatif ini adalah Carthage dan Swift Package Manager

Sharing is Caring

References :
https://cocoapods.org/
https://github.com/CocoaPods
https://github.com/Carthage/Carthage
https://github.com/apple/swift-package-manager

Displaying & Caching Image Using Kingfisher

Image atau gambar adalah hal yang tidak bisa dihindari dan pasti selalu digunakan dalam setiap front-end software development tidak peduli platform apa yang digunakan, baik itu mobile, desktop, ataupun web. Pada kesempatan kali ini saya akan membahas dalam platform mobile, khususnya iOS. 

Dalam pembuatan aplikasi mobile pasti kita akan membutuhkan image untuk mempercantik sekaligus menjelaskan sesuatu, misalnya untuk melakukan send email kita menggunakan button bergambar amlop. Salah satu hal penting yang tidak terlihat dalam software development mobile adalah memperhatikan jumlah data yang digunakan. Tentunya yang banyak memakan data adalah jika kita menggunakan image yang tidak disimpan di dalam installer (.ipa). Tentu saja jika user menyadari bahwa aplikasi kita menggunakan banyak sekali data, maka bukan tidak mungkin user akan berhenti menggunakan aplikasi tersebut.

Solusi dari permasalahan ini adalah menggukan cache. Apa itu cache? Cache adalah suatu cara untuk menyimpan data yang sudah diproses oleh user. Data ini suatu waktu bisa saja digunakan lagi tanpa perlu melakukan proses komputasi ulang. Untuk lebih jelasnya bisa dibaca di dewaweb dan techtarget.

Salah satu library yang populer di iOS (Swift) development adalah Kingfisher. Kingfisher merupakan library yang bisa membantu kita menampilkan image melalui URL dan sekaligus melakukan caching pada image tersebut, sehingga jika akan digunakan lagi maka aplikasi tidak akan me-load ulang gambar itu melalui URL-nya.

Install Kingfisher
Jika menggunakan CocoaPods, maka cukup dengan menambahkan line berikut di Podfile :

pod 'Kingfisher', '~> 4.0'


Jika menggunakan Carthage, maka tambahkan line berikut di Cartfile :

github "onevcat/Kingfisher" ~> 4.0

Jika menggunakan Swift-Package-Manager, maka tambahkan liner berikut di Package.swift :

import PackageDescription

let package = Package(
    name: "YourAwesomeSoftware",
    dependencies: [
        .Package(url: "https://github.com/onevcat/Kingfisher.git",
                 majorVersion: 4)
    ]
)

Fitur-fitur dari Kingfisher
  1. Unduhan gambar dan caching tidak sinkron.
  2. Jaringan berbasis URLSession. Prosesor dan filter gambar dasar disediakan.
  3. Cache multi-layer untuk memori dan disk.
  4. Tugas mengunduh dan memproses yang tidak dapat dibatalkan untuk meningkatkan kinerja.
  5. Komponen independen. Gunakan sistem pengunduh atau caching secara terpisah sesuai kebutuhan Anda.
  6. Mengambil gambar terlebih dahulu dan menampilkannya dari cache nanti bila diperlukan.
  7. Ekstensi untuk UIImageView, NSImage dan UIButton untuk langsung mengatur gambar dari URL.
  8. Animasi transisi internal saat mengatur gambar.
  9. Placeholder yang dapat disesuaikan saat memuat gambar.
  10. Pemrosesan gambar yang dapat diperluas dan dukungan format gambar.
Cara menggunakan
Cara paling dasar dalam menggunakan Kingfisher adalah menggunakannya di ImageView.
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
Tentu saja kingfisher tidak hanya bisa digunakan di ImageView saja, anda juga bisa menggunakannya pada komponen lain, misalnya UIButton, custom-made-view, dan lainnya menggunakan cara yang sama.

Sekian informasi yang dapat saya bagikan, jika ada kesalahan, masukan, ataupun pertanyaan silakan tinggalkan di komentar atau kirim email

Sharing is Caring

References :
https://github.com/onevcat/Kingfisher
https://github.com/onevcat/Kingfisher/wiki/Installation-Guide
https://searchstorage.techtarget.com/definition/cache
https://www.dewaweb.com/blog/penjelasan-cache-dan-jenis-jenisnya/

Sunday, July 1, 2018

Menghindari bad UX dengan menggunakan Multithreading di iOS, Grand Central Dispatch (GCD)

Hai sahabat, pada artikel kali ini saya akan mencoba membahas tentang konsep multithreading di dalam mobile programming di dalam platform iOS. Multithreading adalah sebuah konsep dimana kita dapat membagi setiap task yang kita perintahkan ke dalam device untuk di eksekusi. Sebelum masuk lebih jauh, saya akan memberikan sedikit gambaran tentang apa itu thread. Thread adalah urutan instruksi yang dikelola oleh sistem operasi secara bebas. Artinya ketika sistem operasi memiliki alokasi memori yang cukup untuk mengeksekusi sebuah task, maka pada saat itu juga memori tersebut akan digunakan untuk memproses task tersebut. Hal ini apabila tidak dikelola dengan baik, biasanya akan menyebabkan perangkat yang kita gunakan mengalami hang, atau bahkan crash (bad UX).

Apabila kita ingin membuat aplikasi iOS yang kompleks, responsif, namun pada saat bersamaan berkesan "ringan", Grand Central Dispatch (GCD) adalah jawabannya. Apple menggunakan pendekatan GCD untuk membuat multithreaded programming lebih mudah. GCD memberikan improvisasi yang sistemik serta komprehensif untuk proses concurrent code execution yang terdapat di dalam hardware iOS serta macOS.

Masalah besar yang dihadapi oleh pengembang perangkat lunak pada saat ini ialah, bagaimana cara mengeksekusi perintah yang rumit di dalam sebuah aplikasi, namun pada saat yang bersamaan aplikasi tersebut masih responsif untuk menangani perintah lain dari pengguna. Solusi saat ini biasanya berujung di penambahan kecepatan CPU (hardware solutions), sehingga pengguna tidak menyadari adanya eksekusi yang rumit di background aplikasi. Sementara masalah tersebut masih saja ada, tanpa memikirikan efisiensi dari sisi perangkat lunak.

Untuk lebih jelasnya, saya akan memberikan salah satu contoh implementasi dari GCD di dalam source code sebuah aplikasi. Pada pembahasan sebelumnya yaitu Tutorial ios part 1 kita telah belajar membuat suatu aplikasi sederhana yaitu "Hello World". Saya anggap kalian telah mengerti isi dari source code yang ingin saya berikan dibawah ini:

@IBOutlet var startButton: UIButton!
@IBOutlet var resultsTextView: UITextView!

    func fetchSomethingFromServer() -> String {
        Thread.sleep(forTimeInterval: 1)
        return "Hi there"
    }

    func processData(_ data: String) -> String {
        Thread.sleep(forTimeInterval: 2)
        return data.uppercased()
    }

    func calculateFirstResult(_ data: String) -> String {
        Thread.sleep(forTimeInterval: 3)
        return "Number of chars: \(data.characters.count)"
    }

    func calculateSecondResult(_ data: String) -> String {
        Thread.sleep(forTimeInterval: 4)
        return data.replacingOccurrences(of: "E", with: "e")
    }

    @IBAction func doWork(_ sender: AnyObject) {
            let startTime = NSDate()
            self.resultsTextView.text = ""
            let fetchedData = self.fetchSomethingFromServer()
            let processedData = self.processData(fetchedData)
            let firstResult = self.calculateFirstResult(processedData)
            let secondResult = self.calculateSecondResult(processedData)
            let resultsSummary =
                "First: [\(firstResult)]\nSecond: [\(secondResult)]"
            self.resultsTextView.text = resultsSummary
            let endTime = NSDate()
            print("Completed in \(endTime.timeIntervalSince(startTime as Date)) seconds")
    }
Seperti yang kalian lihat pada method di atas, task di dalam controller ini terbagi menjadi beberapa bagian. Untuk membuatnya lebih menarik, saya telah menyisipkan Thread.sleep di setiap function di dalam controller tersebut. Thread.sleep adalah fungsi untuk memerintahkan aplikasi kita ke dalam state pause dan tidak dapat melakukan aktivitas apapun.

Kita memiliki @IBAction func, dimana didalamnya terdapat fungsi DoWork(). Fungsi tersebut ialah fungsi yang di trigger dari outlet yang disediakan oleh Swift (UIButton), sehingga ketika outlet UIButton tersebut ditekan, maka fungsi DoWork() akan dieksekusi.

@IBAction func doWork(_ sender: AnyObject) {
            let startTime = NSDate()
            self.resultsTextView.text = ""
            let fetchedData = self.fetchSomethingFromServer()
            let processedData = self.processData(fetchedData)
            let firstResult = self.calculateFirstResult(processedData)
            let secondResult = self.calculateSecondResult(processedData)
            let resultsSummary =
                "First: [\(firstResult)]\nSecond: [\(secondResult)]"
            self.resultsTextView.text = resultsSummary
            let endTime = NSDate()
            print("Completed in \(endTime.timeIntervalSince(startTime as Date)) seconds")
    }
Fungsi DoWork() tersebut, akan membuat aplikasi mengalami sensasi "hang" yang diakibatkan dari fungsi Thread.sleep() yang saya sisipkan diawal tadi. Dimana pada keadaan "hang" tersebut, outlet UIButton akan mengedip dan memudar satu kali tetapi tidak kembali pada keadaan semula. Hal itu disebabkan oleh Thread.sleep() yang sedang dieksekusi di background.

Kita akan menerapkan GCD ke dalam method DoWork(), dimana pada hasilnya nanti method DoWork() ini akan dieksekusi sepenuhnya di background, sehingga pengguna tidak akan mengalami sensasi "hang" ketika menekan outlet UIButton tersebut.

 @IBAction func doWork(sender: AnyObject) {
        let startTime = NSDate()
        resultsTextView.text = ""
        let queue = DispatchQueue.global(qos: .default)
        queue.async {
            let fetchedData = self.fetchSomethingFromServer()
            let processedData = self.processData(fetchedData)
            let firstResult = self.calculateFirstResult(processedData)
            let secondResult = self.calculateSecondResult(processedData)
            let resultsSummary =
                "First: [\(firstResult)]\nSecond: [\(secondResult)]"
            self.resultsTextView.text = resultsSummary
            let endTime = NSDate()
            print("Completed in \(endTime.timeIntervalSince(startTime as Date)) seconds")
        }
    }
Perubahan line of code yang saya lakukan adalah penambahan fungsi DispatchQueue.global(), queue tersebut kemudian dialihkan ke fungsi queue.async() berikut dengan closure nya. GCD mengeksekusi closure tersebut kemudian memasukkannya ke dalam queue, dimana pengeksekusiannya dilakukan di background thread, dan outlet UIButton dapat berlaku sebagaimana behaviour semestinya tanpa mengurangi (bad UX).

Sunday, March 19, 2017

Membuat Mobile Apps dengan React Native

Introduction:
React Native adalah framework yang dikembangkan oleh Facebook untuk mengembangkan mobile apps (Android dan iOS) dengan menggunakan JavaScript. Library JavaScript yang digunakan dalam React Native based on React.js

Beberapa kelebihan dari React Native adalah aplikasi yang dibuat sama dengan native apps dalam penggunaannya. Kita hanya perlu mempelajari satu bahasa pemrograman, yaitu JavaScript untuk membuat aplikasi Android dan iOS. Selain itu, kita tidak perlu menghabiskan waktu banyak untuk melakukan compile ulang ketika pengembangan aplikasi. Dalam Android Studio dinamakan "instant run", namun hanya untuk pengembangan Android versi lolipop keatas.

Kekurangannya React Native masih relatif baru (rilis tahun 2015) sehingga masih sedikit tutorial React Native. Beberapa API native dari Android dan iOS belum semuanya supported, namun perkembangan React Native sangat cepat jadi mungkin kedepannya semua API akan supported.

Getting Started:

1. Windows
Untuk platform Windows, React Native hanya dapat mengembangkan aplikasi Android saja. Berikut adalah hal-hal yang diperlukan untuk melakukan instalasi:
  • Java, Android Studio, Genymotion (atau emulator Android lainnya)
  • Install chocholatey
  • buka cmd, lalu jalankan
choco install nodejs.install
choco install python2npm install -g react-native-cli 
  • setelah itu untuk membuat project jalankan:
react-native init NamaProject 
  • Untuk menjalankan aplikasinya, jalankan:

    cd NamaProject
    react-native run-android
2. MacOS
Berikut adalah hal-hal yang diperlukan untuk melakukan instalasi dalam platform MacOS:
  • iOS: XCode
  • Android: Java, Android Studio, Genymotion (atau emulator Android lainnya)
  • Install Homebrew
  • buka cmd, lalu jalankan
brew install node
brew install watchman
npm install -g react-native-cli
  •  Setelah itu untuk membuat project jalankan:
react-native init NamaProject
  • Untuk menjalankan aplikasinya, jalankan:
cd NamaProject
react-native run-android (untuk Android)
react-native run-ios (untuk iOS) 
Selanjutnya akan muncul aplikasi tersebut dalam emulator seperti ini,



Untuk membuat apps dengan React Native juga bisa mengunakan Visual Studio Code , cukup install extention "React Native Tools" nantinya akan ada pilihan untuk menjalankan code untuk Android atau iOS. Berikut adalah contoh isi dari direktori project React Native.





Kita hanya perlu edit file "index.ios.js" untuk membuat aplikasi iOS dan "index.android.js" untuk membuat aplikasi Android. Setelah edit code dalam file tersebut, jalankan command+R (MacOS) atau ctrl+R (Windows) untuk reload aplikasi pada emulator dengan instant.

Selamat mencoba dan Happy Coding :)

Referensi:

Tuesday, March 15, 2016

How to Transfer Objects Between UIViewController

Dalam pengembangan aplikasi iOS, seringkali kita membutuhkan untuk mentransfer data dari satu view ke view lainnya. Misalnya pada kasus ketika kita ingin menampilkan informasi detil sebuah user profile hasil dari pencarian user. Bagaimana cara kita melakukan hal tersebut pada iOS? Kesempatan kali ini saya ingin berbagi cara sederhana untuk melakukannya dengan menggunakan bahasa pemrograman Swift dan storyboard.

Pada pengembangan aplikasi iOS menggunakan Xcode, alur dari view aplikasi dapat didisain dengan menggunakan fitur storyboard. Storyboard merupakan fitur yang sangat bermanfaat karena pengembang aplikasi dapat merancang bentuk dan alur dari aplikasi secara independen tidak tergantung dengan kode logika dari aplikasi. Kode logika aplikasi dapat disambungkan dan dilepaskan dengan storyboard dengan usaha yang relatif minimal (cukup Ctrl+Drag). Saya sempat memiliki pengalaman ketika project files saya corrupt sehingga saya harus mendisain ulang storyboard beserta view-nya dari awal. Ketika sudah selesai, storyboard dan kode logika aplikasi dapat disambungkan kembali. Agak banyak, namun setidaknya tidak harus mengkodekan ulang. :)

Gambar di atas menunjukkan storyboard yang saya gunakan pada artikel kali ini. Storyboard aplikasi ini cukup sederhana di mana hanya terdapat 2 view. Pada view pertama hanya terdapat sebuah tombol yang ketika ditekan akan menginisiasi perpindahan view ke view pada sebelah kanan melalui show segue (panah). Perpindahan data di sini hanya akan memindahkan nilai yang dimasukkan pada textfield sehingga label "Nilai" pada view sebelah kanan akan berubah menjadi nilai apapun yang dimasukkan di textfield view pertama. Berilah nama pada show segue tersebut dengan nama yang identik dalam scope 1 project. Pada contoh ini saya beri nama "transferDataSegue". Buat 2 subclass dari UIViewController dan asosiasikan dengan masing-masing view pada storyboard. Saya mengasosiasikan view pertama dengan StartViewController dan view kedua dengan FinishViewController.


Bind textfield di view pertama ke IBOutlet pada StartViewController. Kemudian bind pula label di view kedua ke IBOutlet pada FinishViewController. Buat sebuah variable pada FinishViewController untuk meletakkan nilai yang dipindahkan dari StartViewController. Kunci dari transfer data antarview pada iOS adalah pada fungsi prepareForSegue dari UIViewController. Override fungsi tersebut kemudian isi agar sesuai seperti gambar berikut:

Fungsi prepareForSegue ini akan selalu dipanggil setiap kali terdeteksi adanya transisi dari sebuah view ke view yang lain. Jika segue yang dihubungkan lebih dari 1, maka transisi dapat terjadi beberapa kemungkinan. Oleh karena itu kita membutuhkan identifier unik untuk segue yang dibahas sebelumnya. Pada fungsi ini langkah pertama kita harus menentukan segue mana yang akan dieksekusi, dengan cara memeriksa identifier-nya. Variabel identifier pada object segue berisikan informasi identifier yang sedang dijalankan.

Setelah kita sudah memperoleh object segue yang sedang dieksekusi, kita pastikan terlebih dahulu apakah object segue tersebut memiliki kaitain dengan UIViewController tujuan. UIViewController tujuan memiliki relasi dengan sebuah segue object yaitu pada variabel destinationViewController yang merupakan FinishViewController. Jika sudah tervalidasi, maka proses transfer data antarview cukup dilakukan semudah assignment variabel biasa. Pada FinishViewController, load nilai yang telah diterima dengan meng-assign-nya ke IBOutlet textfield pada fungsi viewDidLoad.

Silahkan coba dijalankan. Sangat mudah bukan? ;)




Friday, February 19, 2016

Using Carthage for iOS Project Dependency Management

Pertama kali saya mulai mengembangkan aplikasi iOS, saya belum mengenal dependency management yang baik. Ketika saya membutuhkan library pihak ketiga dalam project saya, saya menggunakan cara tradisional yaitu download dan include library yang diinginkan ke dalam project utama saya. Cara ini cukup efektif dan segera mudah dimengerti, namun akan mempersulit pengembangan selanjutnya. Setiap ada update dari library yang kita include, kita harus men-download kembali library tersebut. Apabila ada update yang membuat project kita tidak berjalan kita harus rollback kembali dan memilih versi yang tepat.

Menggunakan dependency management untuk iOS project development memiliki beberapa keuntungan dibanding cara tradisional (download dan copy project dari Github). Dependency management dapat mempermudah proses download library yang sesuai dengan yang kita inginkan tanpa harus satu per satu ke halaman Github dari setiap library yang diperlukan, klik download, dan include masing-masing libraray ke dalam project utama. Selain itu kita dapat mengunci versi yang kita perlukan, untuk mencegah kemungkinan code tidak berjalan ketika ada update pada salah satu library-nya.

Ada 2 dependency manager yang biasa digunakan di iOS development, yaitu CocoaPods dan Carthage. Pada kesempatan kali ini saya akan mencoba membahas bagaimana cara menggunakan Carthage, karena saya menggunakan bahasa Swift untuk pengembangan aplikasi. Untuk library yang akan digunakan saya akan menggunakan library untuk mempermudah pemanggilan Web service yaitu Alamofire.

Instalasi Carthage


Ada 2 cara menginstalasi Carthage, yaitu menggunakan Homebrew atau menggunakan package installer. Saya akan menggunakan Homebrew, karena sudah terinstall di MacBook Pro saya. Jalankan 2 perintah berikut di Terminal.
$ brew update
$ brew install carthage

Persiapan Project


Buat project baru pada Xcode, pilih template apa saja. Untuk keperluan artikel ini, saya akan menggunakan template Single View Application.
Xcode New Project Wizard Page 1

Masukkan nama project, dan buat project tersebut.
Xcode New Project Wizard Page 2

Buat sebuah file Cartfile di folder yang sama dengan file .xcodeproj, isi dengan konfigurasi dependency ke library Alamofire seperti berikut ini:
# Depends on Alamofire 3.0
github "Alamofire/Alamofire" ~> 3.0

Simpan file Cartfile, dan lakukan dependency update dengan menjalankan perintah berikut pada folder tersebut dan perhatikan Carthage akan mendownload semua dependency yang diperlukan untuk platform iOS ke dalam direktori Carthage/Checkouts. Selain itu perintah tersebut akan melakukan kompilasi library Alamofire ke dalam direktori Carthage/Build.
$ carthage update --platform iOS
*** Fetching Alamofire
*** Checking out Alamofire at "3.2.0"
*** xcodebuild output can be found in /var/folders/qy/lcpj5ypn1_7_gbfyv5c9dlq40000gn/T/carthage-xcodebuild.119QZ8.log
*** Building scheme "Alamofire iOS" in Alamofire.xcworkspace

Buka kembali project pada Xcode, pilih project root SimpleRestCallDemoApp dan pilih target SimpleRestCallDemoApp. Tambahkan file Alamofire.framework yang telah dikompilasi ke dalam Linked Frameworks and Binaries. Pilih Add Other untuk mengakses langsung dari Finder. Setelah selesai tampilannya akan menjadi seperti ini.
Xcode Linked Frameworks

Untuk memudahkan dalam proses submit aplikasi ke Apple App Store, saran dari [3] adalah dengan menambahkan Run Script pada Build Phases seperti berikut ini.
Xcode Build Phases Run Script

Selesai, kita sekarang siap untuk menggunakan framework Alamofire di dalam project kita.

Referensi

[1] https://github.com/Carthage/Carthage
[2] https://github.com/Alamofire/Alamofire
[3] http://www.raywenderlich.com/109330/carthage-tutorial-getting-started

Tuesday, December 15, 2015

Mengatur UIView dengan Auto Layout pada iOS

Salah satu isu yang saya temui ketika memulai mengembangkan aplikasi mobile, khususnya pada iOS, adalah dalam hal pengaturan layout antarmuka. Seringkali saya sudah meletakkan dengan rapih, namun ketika dijalankan di iOS Simulator layout-nya menjadi berantakan pada device tertentu. Misalnya saya ingin meletakkan 2 label dengan pasangan text field-nya seperti UIView berikut ini:
UIView pada Storyboard

Terlihat pada Storyboard, UIView tersebut sudah rapih. Tetapi setelah dijalankan di iOS Simualtor, hasilnya berantakan tidak sesuai seperti yang saya bayangkan ketika menuangkannya di Storyboard:
UIView ketika runtime di iOS Simulator

Apple telah menyediakan fitur Auto Layout pada iOS untuk mengkalkukasikan secara dinamis jarak dan posisi dari masing-masing komponen antarmuka, sehingga layout dapat tetap rapih dan dinamis pada device apapun. Awalnya saya kurang memahami perilaku dari Auto Layout, sehingga saya merasa menggunakan Auto Layout justru malah mempersulit pengembangan. Ternyata setelah berulang kali saya mencoba, Auto Layout tidak sesulit itu. Bahkan terasa sangat natural dan logis. Pada artikel ini saya ingin berbagi cara memahami dan memanfaatkan Auto Layout dalam pengembangan aplikasi iOS.

Pertama-tama, pastikan "Use Auto Layout" dan "Use Size Classes" pada property Main.storyboard dalam kondisi terpilih. Kemudian ketika kita sedang mendisain UIView di Storyboard, Size Classes berada di posisi "w Any h Any". 
Size Classes Default

Size Classes "w Any h Any" memiliki arti UIView yang sedang diedit akan diaplikasikan secara universal. Artinya kita hanya cukup mendisain layout sekali untuk berbagai ukuran model device maupun orientasi device. Faktor penting lainnya adalah dalam Auto Layout, kita membutukan sebuah object atau batasan untuk menjadi patokan utama. Misalnya pada contoh di atas, hasil akhir yang kita inginkan adalah keseluruhan disain berada tepat di tengah layar bagian atas. Untuk itu kita membutuhkan sebuah object tambahan sebagai patokan titik tengah horizontal. Ambil saja sebuah label baru dan kita letakkan di antara Name dan text field sebelahnya hinggap snap to horizontal centre grid.
Penambahan Komponen Dummy Posisi Tengah Horizontal

Kecilkan ukurannya, kemudian ketika dalam kondisi memilih label tersebut pilih "Pin" di sudut kanan bawah Storyboard dan pilih "Width" dan "Height", kemudian pilih "Align" dan pilih "Horizontally in Container". Tampilannya akan menjadi seperti ini:

Garis merah ini pertanda ada sebuah contraint yang masih tidak benar. Target kita adalah membuat garis merah ini hilang dan menjadi garis biru yang menandakan layout sudah tidak ambigu. Tahan tombol "Ctrl" dan tarik cursor mouse dari label tersebut ke arah atas container dan lepaskan. Kemudian pilih "Vertical Spacing to Top Layout Guide". Seharusnya garis sudah menjadi biru sekarang. 

Kita sudah memiliki patokan titik horizontal sekarang, yaitu label dummy yang telah dibuat sebelumnya.  Sekarang saatnya memanfaatkan titik tersebut. Geser text field ke kanan, label Name ke kiri. Tahan tombol Ctrl dan mouse dari text field ke titik horizontal, begitu pula dengan label Name. Tarik pula dari masing-masing label dan text field ke arah atas ke top container. Jangan lupa untuk membuat fix width pada textfield. Coba jalankan kembali ke iOS Simulator. Seharusnya sekarang posisinya sudah lebih dinamis dan rapih, baik pada oritivertikal maupun horizontal.
Lakukan hal serupa pada label Role dan textfield-nya, kemudian hide titik horizontal. Hasil akhirnya kurang lebih seperti ini:

Selamat mencoba! :)

Friday, December 11, 2015

Introduction to CocoaPods

Saat kita membuat sebuah project aplikasi tentunya kita akan banyak menggunakan Library dan plugins yang nantinya kita butuhkan untuk mempermudah dalam pengembangan aplikasi. Untuk memudahkan kita dalam memanage kumpulan Library tersebut kita membutuhkan sistem depedency manager. Begitu juga saat kita memulai Ios project kita akan menggunakan depedency manager "Cocoapods".

Cocoapods adalah sebuah depedency manager untuk Swift atau Objective-C project yang memiliki banyak kumpulan Library yang memungkinkan untuk mengembangkan aplikasi secara elegant.

Kita langsung saja ke proses instalasi cocoapods ke project IOS,

Instalasi

Sebelum melakukan instalasi pastikan Ruby sudah terinstall di device kalian, hal ini dikarenakan Cocoapods dibangun menggunakan Ruby.

setelah instalasi Ruby selesai, jalankan script berikut di terminal :

$ sudo gem install cocoapods
Sekarang cocoapods sudah terinstall di device kalian.

Membuat Podfile

Podfile yang nantinya akan menampung seluruh list Library yang akan kita gunakan didalam project. Ada 2 cara untuk membuat Podfile

  1. Buat file kosong bernama Podfile didalam project yang akan digunakan.
  2. Jalankan Pod init di project yang akan digunakan.
Masukan List Library

Berikut contoh dari list Library:
platform :ios, '7.0'
pod 'AFNetworking', '~> 2.0'


Dalam contoh ini kita akan menggunakan Library AFNetworking. sebelum kita menggunakan Library didalam project kita harus menambakan semua dependencies yang dibutuhkan ke dalam Podfile.

Untuk menginstall semua Library yang dibutuhkan jalan kan perintah berikut di terminal:

$ pod install

Setelah melakukan perintah diatas kita sudah bisa menggunakan semua library yang di list di Podfile.

Thanks.

Friday, September 18, 2015

Country Selection with Swift in iOS 8


Beberapa bulan terakhir ini, saya mulai mempelajari dan mengembangkan aplikasi iOS menggunakan bahasa Swift. Agar tidak lupa sekaligus menguji pengalaman yang saya peroleh, saya ingin mulai menuangkannya ke topik artikel-artikel saya ke depan. Pada artikel kali ini saya ingin membahas bagaimana cara membuat pilihan negara pada sebuah form menyerupai cara memilih negara pada Address Book iOS. Untuk lebih terbayang tujuan artikel ini, silahkan lihat video berikut ini:

Skenario yang ada pada video tersebut adalah pada scene pertama aplikasi menampilkan scene Edit Profile, di mana salah satu field profile adalah memilih negara. User kemudian memilih negara, lalu transisi ke scene selanjutnya yang muncul dari bawah berupa pilihan negara. Scene pemilihan negara ini tidak hanya berupa daftar negara saja, namun seperti pada Address Book dilengkapi juga dengan indeks huruf depan masing-masing nama negara untuk mempermudah navigasi user.
Gambar 1. Potongan Storyboard

 Storyboard yang saya gunakan pada dasarnya terdiri dari 3 scenes:
  1. EditProfileViewController, subclass dari TableViewController yang saya gunakan untuk scene edit profile.
  2. CountryHelperTableViewController, subclass dari TableViewController yang saya gunakan untuk scene dari pemilihan negara itu sendiri.
  3. NavigationViewController, scene ini bertugas menjembatani dengan EditProfileViewController. NavigationViewController ini berfungsi agar kita dapat melakukan transisi modal dengan lebih baik.
Buat sebuah Swift file bernama CountryHelperTableViewController.swift
import UIKit

class CountryHelperTableViewController: UITableViewController {
    
    var countries: [String]?
    var sectionTitles: [String]?
    var selectedCountryRowId: Int = 0
    var selectedCountryOffset: Int = 0
    var selectedSectionTitleRowId: Int = 0
    var selectedCountry: String?
    var generatedRows: Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        populateCountries()
        populateSectionTitles()
        setInitialCountrySelection()
        setSelectedCountryOffset()
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        let indexPath = NSIndexPath(forRow: selectedCountryOffset, inSection: selectedSectionTitleRowId)
        self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Middle, animated: true)
    }
    
    func populateCountries() {
        countries = [String]()
        
        for code in NSLocale.ISOCountryCodes() as! [String] {
            let id = NSLocale.localeIdentifierFromComponents([NSLocaleCountryCode: code])
            let name = NSLocale(localeIdentifier: "en_US").displayNameForKey(NSLocaleIdentifier, value: id) ?? "Country not found for code: \(code)"
            countries!.append(name)
        }
        
        countries!.sort({ $0 < $1 })
    }
    
    func populateSectionTitles() {
        var countrySections = [String: String]()
        
        for country in countries! {
            countrySections[country[0]] = country[0]
        }
        
        var sortedCountrySections = [String]()
        
        for (key, value) in countrySections {
            sortedCountrySections.append(key)
        }
        
        sortedCountrySections.sort({ $0 < $1 })
        sectionTitles = sortedCountrySections
    }
    
    func setInitialCountrySelection() {
        for (index, country) in enumerate(countries!) {
            if country == selectedCountry {
                selectedCountryRowId = index
                
                for (sectionIndex, sectionTitle) in enumerate(sectionTitles!) {
                    if selectedCountry![0] == sectionTitle {
                        selectedSectionTitleRowId = sectionIndex
                        break
                    }
                }
                
                break
            }
        }
    }
    
    func setSelectedCountryOffset() {
        var offset: Int = 0
        
        for country in countries! {
            if country[0] == self.sectionTitles![selectedSectionTitleRowId] {
                if selectedCountry != country {
                    offset++
                } else {
                    break
                }
            }
        }
        
        selectedCountryOffset = offset
    }

    override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
 if section == tableView.numberOfSections() - 1 {
            return UIView(frame: CGRectMake(0, 0, 1, 1))
 } else {
     return nil
 }
    }

    override func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        if section == tableView.numberOfSections() - 1 {
     return 1
        } else {
            return 0
        }
    }
    
    func rowsInPreviousSection(section: Int) -> Int {
        var rows = 0
        
        for country in self.countries! {
            if self.sectionTitles![section] == country[0] {
                break
            } else {
                rows++
            }
        }
        
        return rows
    }
    
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return self.sectionTitles!.count
    }
    
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return self.sectionTitles![section]
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let indexTitle = self.sectionTitles![section]
        var numberOfRows = 0

        for country in self.countries! {
            if indexTitle == country[0] {
                numberOfRows++
            }
        }
        
        return numberOfRows
    }
    
    override func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
        return index
    }
    
    override func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]! {
        return self.sectionTitles
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let row = indexPath.row
        let section = indexPath.section
        let prevSectionRows = rowsInPreviousSection(section)
        let cell = self.tableView.dequeueReusableCellWithIdentifier("CountryCell") as! UITableViewCell
        
        cell.textLabel?.text = countries![row + prevSectionRows]
        
        if selectedCountryRowId == row + prevSectionRows {
            cell.accessoryType = UITableViewCellAccessoryType.Checkmark
        } else {
            cell.accessoryType = UITableViewCellAccessoryType.None
        }
        
        return cell
    }
    
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        selectedCountryRowId = indexPath.row + rowsInPreviousSection(indexPath.section)
        tableView.reloadData()
    }
    
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "editProfileUnwindSegueWithValue" {
            if let destinationViewController = segue.destinationViewController as? EditProfileViewController {
                destinationViewController.selectedCountry = countries![selectedCountryRowId]
                destinationViewController.updateSelectedValues()
            }
        }
    }
    
}

Setelah selesai membuat class CountryHelperTableViewController, assign class tersebut ke scene paling kanan. Buat juga String extension seperti berikut ini untuk fungsi mengambil indeks huruf pertama pada nama negara.
import Foundation

extension String {
    
    subscript (i: Int) -> Character {
        return self[advance(self.startIndex, i)]
    }
    
    subscript (i: Int) -> String {
        return String(self[i] as Character)
    }
    
    subscript (r: Range) -> String {
        return substringWithRange(Range(start: advance(startIndex, r.startIndex), end: advance(startIndex, r.endIndex)))
    }
}

Method tableView(_:viewForFooterInSection:) dan tableView(_:heightForFooterInSection:) digunakan agar garis border horizontal dari TableView berakhir pada baris atau nama negara terakhir di dalam array. Perilaku default jika kedua method ini tidak diaplikasikan adalah garis border horizontal akan terus berulang hingga batas layar. Saya lebih suka jika garis horizontal berakhir sesuai dengan data.

Method viewDidLoad() dipanggil pada saat pertama kali saja untuk me-load daftar negara dari library Swift melalui method populateCountries(). Setelah negara-negara ter-load, kita membutuhkan daftar indeks huruf pertama dari masing-masing negara yang ter-load dengan menggunakan method populateSectionTitles(). Method setInitialCountrySelection() digunakan untuk menentukan posisi indeks yang sudah pernah dipilih oleh User dengan nilai default 0 jika User belum pernah melakukan pemilihan negara. Sedangkan method setSelectedCountryOffset() digunakan untuk menentukan offset ke-berapakah negara yang terpilih berdasarkan dari indeks section title. Method ini bermanfaat untuk menentukan TableViewCell manakah yang perlu ditandai dengan checkmark.

Method viewWillAppear() ini pada dasarnya bertujuan untuk melakukan autoscroll terhadap daftar negara, agar fokus dari view berada di tengah-tengah sesuai dengan negara yang pernah dipilih oleh User sebelumnya. Pada method ini tidak perlu lagi me-load daftar negara yang lengkap, karena daftar negara sudah di-load oleh viewDidLoad() untuk pertama kalinya. 

Method tableView(_:numberOfRowsInSection:) merupakan salah satu fungsi penting dalam menampilkan  daftar negara ini. Method inilah yang akan menyaring negara-negara yang sesuai dengan indeks huruf pertamanya. Sementara method tableView(_:sectionForSectionIndexTitle:) dan method sectionIndexTitlesForTableView() berguna untuk menampilkan bar horizontal di bagian kanan yang scene sebagai indeks huruf pertama dari masing-masing negara.

Sekian artikel saya tentang pengembangan iOS kali ini, semoga bermanfaat!

Tuesday, September 16, 2014

Tutorial iOS Part 2

Di blog sebelumnya, kita membahas sekilas iOS Programming dengan membuat program HelloWorld. Sekarang kita akan mencoba menggunakan beberapa componen standart iOS Programming, seperti :

  • UITextField
  • UIStepper
  • UISlider
  • UISegmentedControl
  • UISwitch
  • UIActivity

Untuk tampilan User Interface di Simulator iOS adalah sebagai berikut :

Skenario dari program ini adalah :
  1. Saat user memasukkan tulisan di Text Field dan user menekan tombol Press, apapun tulisannya akan di tampilkan di Label.
  2. Saat user menekan Stepper +, nilai dari stepper akan bertambah dan di tampilkan di Label. Jika user menakan Stepper -, nilai akan berkurang. Iterasi yang digunakan adalah 1.
  3. Begitu juga dengan Slidder. Nilai akan di tampilkan di Label dengan iterasi 0.5.
  4. Untuk Segmented Control, yang tampil di Label adalah nilai dari indexnya.
  5. Sedangkan komponen Swicth akan menjalankan animasi proses. Animasi akan mulai saat switcher on dan berhenti saat switcher off.
Tanpa berlama-lama lagi, mari kita buat project dengan nama HelloUIKit.

Selanjutkan kita buat IBOutlet dan IBAction-nya di ViewController.h


Untuk Design Interface-nya adalah sebagai berikut :

Jangan lupa untuk membuat koneksi antara file xib (Main.storyboard) dengan source code-nya.

Dan yang terakhir adalah membuat implementasinya di ViewController.m

Sedangkan untuk method IBAction-nya adalah

Source code dapat di download disini. Untuk compile dan testing cukup dengan shortcut cmd + R.

Sekali lagi begitu mudahnya membuat program di iOS. Semoga tutorial ini bermafaat dan menambah wawasan kita bersama. Tunggu tutorial iOS selanjutnya di blog ini ya...