Akhir nya kita bertemu lagi di artikel bagian 4 untuk development iOS :). Artikel ini adalah artikel bagian terakhir untuk pembahasan mengenai development iOS. Pada artikel ini, kita hanya akan membahas bagaimana cara memutar music yang berasal dari API spotify, ingat bahwa lagu yang akan diputar adalah hanya preview lagu saja, jadi lagu yang diputar tidak nya lengkap :).

Oke, silahkan buka project anda dengan xcode, nah disini kita perlu terlebih dahulu memisahkan antara view dan controller. Silahkan buat sebuah group dengan nama controllers, silahkan buka group views, disana terdapat file TrackTableView silahkan buka file tersebut, lalu copy isi source code nya lalu hapus file tersebut, silahkan buat file kembali dengan nama TrackTableView di dalam group controller dan paste kan isinya. Oke setelah selesai memindahkan nya, tahap selanjutnya silahkan buat sebuah file swift kembali di dalam group controllers dengan file TrackPreviewViewController.

Langkah selanjutnya kita akan terlebih dahulu membuat view, silahkan buat sebuah story board di dalam group views dengan nama TrackPreview, lalu untuk desainnya silahkan lihat video berikut.

Nah tahap desain udah selesai :D. Selanjutnya silahkan buka kembali file TrackPreviewViewController lalu isikan codingan seperti berikut.

//
//  TrackPreviewViewController.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 5/11/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import UIKit
import AudioPlayerManager
import Kingfisher
import MarqueeLabel

class TrackPreviewViewController: UIViewController {

    @IBOutlet weak var buttonPlayPause: UIButton!
    @IBOutlet weak var labelArtist: MarqueeLabel!
    @IBOutlet weak var labelTrack: MarqueeLabel!
    @IBOutlet weak var imageAlbum: UIImageView!
    @IBOutlet weak var imageBackground: UIImageView!
    
    let track = UserDefaults.standard.object(forKey: "trackPreview") as! String
    let artistsName = UserDefaults.standard.object(forKey: "trackName") as! String
    let trackName = UserDefaults.standard.object(forKey: "artistName") as! String
    let image = UserDefaults.standard.object(forKey: "imageAlbum") as! String
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "Belajar iOS"
        
        self.labelArtist.type = .continuous
        self.labelArtist.speed = .duration(15)
        self.labelArtist.animationCurve = .easeInOut
        
        self.labelTrack.type = .continuous
        self.labelTrack.speed = .duration(15)
        self.labelTrack.animationCurve = .easeInOut
        
        self.labelArtist.text = artistsName
        self.labelTrack.text = trackName
        
        let url = URL(string: image)
        self.imageBackground.kf.setImage(with: url)
        self.imageAlbum.kf.setImage(with: url)
        
        if AudioPlayerManager.shared.isPlaying() == true {
            AudioPlayerManager.shared.stop()
        }
        AudioPlayerManager.shared.play(urlString: track)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func playButtonAction(_ sender: Any) {
        if AudioPlayerManager.shared.isPlaying() == true {
            AudioPlayerManager.shared.pause()
            self.buttonPlayPause.setImage(UIImage(named: "play-icon"), for: .normal)
        } else {
            AudioPlayerManager.shared.play()
            self.buttonPlayPause.setImage(UIImage(named: "pause-icon"), for: .normal)
        }
    }
}

Untuk memutar music, penulis menggunakan library AudioPlayerManager :). Nah untuk mengambil data dari storyboard sebelumnnya, kita menggunakan fungsi UserDefaults.standard, fungsi ini sama seperti fungsi LocalStorage pada web browser. Untuk memutar musik, kita dapat menggunakan fungsi AudioPlayerManager.shared.play, baik itu musik di local mauapun melalui url, pada contoh kali ini kita menggunakan url. Untuk menganti gambar pada button, kita hanya perlu melakuka set image seperti perintah berikut.

self.buttonPlayPause.setImage(UIImage(named: "play-icon"), for: .normal)

Sehingga pada saat musik sedang berjalan, maka posisi button adalah pause, sedangkan pada saat musik berhenti maka posisi button adalah play.

Oke langkah selanjutnya, bagaimana cara untuk berpindah dari 1 storyboard ke storyboard yang lain ?. Ada beberapa cara yaitu bisa menggunakan segue atau menggunakan pushViewController, biasanya saya akan menggunakan pushViewController. Silahkan buka file ViewController lalu pada bagian extension UITableViewDelegate tambahkan kodingan berikut.

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if indexPath.row == (self.rowData.count - 1) {
            if (self.nextPage ?? "").isEmpty == false {
                self.getDefaultTrack(track: track, offset: nextInt, limit: 20)
            }
        }
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if UserDefaults.standard.object(forKey: "trackPreview") != nil {
            UserDefaults.standard.removeObject(forKey: "trackPreview")
            UserDefaults.standard.synchronize()
        }
        if UserDefaults.standard.object(forKey: "trackName") != nil {
            UserDefaults.standard.removeObject(forKey: "trackName")
            UserDefaults.standard.synchronize()
        }
        if UserDefaults.standard.object(forKey: "artistName") != nil {
            UserDefaults.standard.removeObject(forKey: "artistName")
            UserDefaults.standard.synchronize()
        }
        if UserDefaults.standard.object(forKey: "imageAlbum") != nil {
            UserDefaults.standard.removeObject(forKey: "imageAlbum")
            UserDefaults.standard.synchronize()
        }

        UserDefaults.standard.set(self.rowData[indexPath.row].previewUrl, forKey: "trackPreview")
        UserDefaults.standard.set(self.rowData[indexPath.row].name, forKey: "trackName")
        UserDefaults.standard.set(ConvertString().fromArrayArtistToString(array: self.rowData[indexPath.row].artists!), forKey: "artistName")
        UserDefaults.standard.set(self.rowData[indexPath.row].album?.images?[0].url, forKey: "imageAlbum")
        UserDefaults.standard.synchronize()

        let storyboard = UIStoryboard(name: "TrackPreview", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "trackPreviewStoryBoard") as UIViewController
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

fungsi dari function kedua diatas adalah jika user memilih salah satu row table, maka akan dijalankan aksi di dalam nya, di dalam function tersebut kita menyimpan data musik yang akan dikirim ke storyboard berikutnya, untuk berpindah ke storyboard kedua, kita menggunakan perintah berikut.

let storyboard = UIStoryboard(name: "TrackPreview", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "trackPreviewStoryboard") as UIViewController
self.navigationController?.pushViewController(vc, animated: true)

Pertama kita deklarasikan terlebih dahulu variabel storyboard dengan value nama storyboard, tadinya kita membuat storyboard dengan nama TrackPreview, lalu buat sebuah variabel dengan menampung id dari storyboard tersebut, id ini bersifat unik walaupun berbeda storyboard, silahkan lihat video diatas pada saat deklarasikan storyboard id, storyboard id nya yaitu trackPreviewStoryboard. Selanjutnya untuk menampilkan storyboard berikutnya, kita menggunakan perintah pushViewController sehingga storyboard kedua akan berada diatas storyboard pertama sehingga akan saling tumpang tindih, konsep ini sebenarnya sama seperti konsep intent pada android :).

Berikut kodingan lengkap untuk ViewController

//
//  ViewController.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 4/30/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import UIKit
import RxSwift
import Kingfisher
import SwiftOverlays

class ViewController: UIViewController {

    @IBOutlet weak var trackTable: UITableView!
    let searchBar = UISearchBar()

    var rowData: [Item] = []
    var nextPage: String? = nil
    var nextInt = 0
    var limitInt = 20
    var track = "ColdPlay"
    var isSearch = false

    private var disposeBag = DisposeBag()
    private var trackService = TrackService()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Belajar iOS"
        self.getDefaultTrack(track: track, offset: nextInt, limit: limitInt)
        self.trackTable.delegate = self
        self.trackTable.dataSource = self

        self.searchBar.placeholder = "Cari"
        self.searchBar.delegate = self
        self.searchBar.tintColor = UIColor.black
        self.searchBar.sizeToFit()
        self.navigationItem.titleView = searchBar
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func getDefaultTrack(track: String, offset: Int, limit: Int) {
        SwiftOverlays.showBlockingWaitOverlay()
        trackService
            .getTrackByQuery(parameters: ConvertString().toPlusString(text: track), offset: offset, limit: limit)
            .subscribe(
                onNext: { trackResponse in
                    if offset == 0 && trackResponse.tracks?.items?.count != 0 {
                        self.rowData.removeAll()
                    }
                    if trackResponse.tracks?.items?.count != 0 {
                        self.rowData.append(contentsOf: (trackResponse.tracks?.items)!)
                        if ((trackResponse.tracks?.next) ?? "").isEmpty == false {
                            self.nextPage = (trackResponse.tracks?.next)!
                        } else {
                            self.nextPage = nil
                        }
                        self.nextInt = (trackResponse.tracks?.offset)! + 20
                        self.trackTable.reloadData()
                    } else {
                        let alert = UIAlertController(title: "Info", message: "Maaf, Data Tidak ditemukan :(", preferredStyle: UIAlertControllerStyle.alert)
                        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                        self.present(alert, animated: true, completion: nil)
                    }
                    SwiftOverlays.removeAllBlockingOverlays()
                },
                onError: { error in
                    SwiftOverlays.removeAllBlockingOverlays()
                }
            )
            .addDisposableTo(disposeBag)
    }

}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if indexPath.row == (self.rowData.count - 1) {
            if (self.nextPage ?? "").isEmpty == false {
                self.getDefaultTrack(track: track, offset: nextInt, limit: 20)
            }
        }
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if UserDefaults.standard.object(forKey: "trackPreview") != nil {
            UserDefaults.standard.removeObject(forKey: "trackPreview")
            UserDefaults.standard.synchronize()
        }
        if UserDefaults.standard.object(forKey: "trackName") != nil {
            UserDefaults.standard.removeObject(forKey: "trackName")
            UserDefaults.standard.synchronize()
        }
        if UserDefaults.standard.object(forKey: "artistName") != nil {
            UserDefaults.standard.removeObject(forKey: "artistName")
            UserDefaults.standard.synchronize()
        }
        if UserDefaults.standard.object(forKey: "imageAlbum") != nil {
            UserDefaults.standard.removeObject(forKey: "imageAlbum")
            UserDefaults.standard.synchronize()
        }

        UserDefaults.standard.set(self.rowData[indexPath.row].previewUrl, forKey: "trackPreview")
        UserDefaults.standard.set(self.rowData[indexPath.row].name, forKey: "trackName")
        UserDefaults.standard.set(ConvertString().fromArrayArtistToString(array: self.rowData[indexPath.row].artists!), forKey: "artistName")
        UserDefaults.standard.set(self.rowData[indexPath.row].album?.images?[0].url, forKey: "imageAlbum")
        UserDefaults.standard.synchronize()

        let storyboard = UIStoryboard(name: "TrackPreview", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "trackPreviewStoryboard") as UIViewController
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.rowData.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = Bundle.main.loadNibNamed("TrackTableViewCell", owner: self, options: nil)?.first as! TrackTableViewCell

        cell.trackNameLabel.text = self.rowData[indexPath.row].name
        cell.artistNameLabel.text = ConvertString().fromArrayArtistToString(array: self.rowData[indexPath.row].artists!)
        cell.albumNameLabel.text = self.rowData[indexPath.row].album?.name

        let url = URL(string: (self.rowData[indexPath.row].album?.images?[0].url)!)
        cell.gambarAlbumImage.kf.indicatorType = .activity
        cell.gambarAlbumImage.kf.setImage(with: url)

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 98
    }
}

extension ViewController: UISearchBarDelegate {
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        self.track = self.searchBar.text!
        self.nextInt = 0
        self.getDefaultTrack(track: self.track, offset: self.nextInt, limit: self.limitInt)
        self.searchBar.resignFirstResponder()
        self.searchBar.setShowsCancelButton(false, animated: true)
        self.searchBar.resignFirstResponder()
    }

    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        self.searchBar.setShowsCancelButton(true, animated: true)
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        self.searchBar.text = nil
        self.searchBar.setShowsCancelButton(false, animated: true)
        self.searchBar.resignFirstResponder()
    }
}

Sekarang silahkan jalankan, berikut adalah hasil output nya

Screen Shot 2017-05-11 at 3.53.33 PM.png

Wah tombol back nya kurang bagus nih :(, oke kita akan coba ganti tulisan nya menjadi kembali dengan warna hitam, silahkan buka file TrackPreviewViewController lalu tambahkan kodingan berikut di function viewDidLoad.

self.navigationController?.navigationBar.tintColor = UIColor.black
self.navigationController?.navigationBar.topItem?.title = "Kembali"

dan hasilnya seperti berikut :D.

Akhirnya selesai juga artikel mengenai belajar iOS dari bagian 1 hingga 4 :). Untuk source code diatas dapat anda akses di Belajar-iOS. Sekian artikel mengenai Belajar iOS bagian 4, jika ada saran dan komentar silahkan isi dibawah dan terima kasih :)

Sebelumnya kita telah membuat aplikasi untuk melihat list musik, akan tetapi kurang jika kita tidak menambahkan fitur search :). Oke, pada artikel ini kita akan membahas mengenai bagaimana cara membuat search :D. Mungkin artikel ini adalah artikel yang terpendek dikarenkan untuk membuat sebuah search bar di swift sangatlah gampang :). Oke kita langsung buka project iOS dengan xcode, project yang kita gunakan adalah project sebelum nya yang ada di artikel sebelumnya.

Untuk membuat search bar silahkan buka file ViewController, kemudian deklarasikan sebuah variabel searchBar seperti berikut.

let searchBar = UISearchBar()

Setelah selesai, kita akan membuat search bar tersebut tepat di navigation menu, silahkan masukkan codingan berikut tepat di dalam function viewDidLoad

self.searchBar.placeholder = "Cari"
self.searchBar.delegate = self
self.searchBar.tintColor = UIColor.black
self.searchBar.sizeToFit()
self.navigationItem.titleView = searchBar

Nah karena diatas ada perintah delegate maka kita harus membuat 1 extension untuk menghadle action dari search bar, silahkan masukkan codingan extension berikut di bagian paling bawah.

extension ViewController: UISearchBarDelegate {
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        self.track = self.searchBar.text!
        self.nextInt = 0
        self.getDefaultTrack(track: self.track, offset: self.nextInt, limit: self.limitInt)
        self.searchBar.resignFirstResponder()
        self.searchBar.setShowsCancelButton(false, animated: true)
        self.searchBar.resignFirstResponder()
    }

    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        self.searchBar.setShowsCancelButton(true, animated: true)
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        self.searchBar.text = nil
        self.searchBar.setShowsCancelButton(false, animated: true)
        self.searchBar.resignFirstResponder()
    }
}

fungsi function searchBarSearchButtonClicked berfungsi untuk menghandle action ketika user menekan tombol search, dan pada saat itu, kita akan menjalankan perintah getDefaultTrack untuk mengambil data musik. function kedua berfungsi memunculkan tombol cancel ketika kita melakukan editing pada search bar nya dan pada function terakhir ketika user menekan tombol cancel maka tombol cancel akan kembali seperti biasa.

Berikut adalah hasil dari seluruh kodingannya.

//
//  ViewController.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 4/30/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import UIKit
import RxSwift
import Kingfisher
import SwiftOverlays

class ViewController: UIViewController {

    @IBOutlet weak var trackTable: UITableView!
    let searchBar = UISearchBar()

    var rowData: [Item] = []
    var nextPage: String? = nil
    var nextInt = 0
    var limitInt = 20
    var track = "ColdPlay"
    var isSearch = false

    private var disposeBag = DisposeBag()
    private var trackService = TrackService()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Belajar iOS"
        self.getDefaultTrack(track: track, offset: nextInt, limit: limitInt)
        self.trackTable.delegate = self
        self.trackTable.dataSource = self

        self.searchBar.placeholder = "Cari"
        self.searchBar.delegate = self
        self.searchBar.tintColor = UIColor.black
        self.searchBar.sizeToFit()
        self.navigationItem.titleView = searchBar
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func getDefaultTrack(track: String, offset: Int, limit: Int) {
        SwiftOverlays.showBlockingWaitOverlay()
        trackService
            .getTrackByQuery(parameters: ConvertString().toPlusString(text: track), offset: offset, limit: limit)
            .subscribe(
                onNext: { trackResponse in
                    if offset == 0 && trackResponse.tracks?.items?.count != 0 {
                        self.rowData.removeAll()
                    }
                    if trackResponse.tracks?.items?.count != 0 {
                        self.rowData.append(contentsOf: (trackResponse.tracks?.items)!)
                        if ((trackResponse.tracks?.next) ?? "").isEmpty == false {
                            self.nextPage = (trackResponse.tracks?.next)!
                        } else {
                            self.nextPage = nil
                        }
                        self.nextInt = (trackResponse.tracks?.offset)! + 20
                        self.trackTable.reloadData()
                    } else {
                        let alert = UIAlertController(title: "Info", message: "Maaf, Data Tidak ditemukan :(", preferredStyle: UIAlertControllerStyle.alert)
                        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                        self.present(alert, animated: true, completion: nil)
                    }
                    SwiftOverlays.removeAllBlockingOverlays()
                },
                onError: { error in
                    SwiftOverlays.removeAllBlockingOverlays()
                }
            )
            .addDisposableTo(disposeBag)
    }

}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if indexPath.row == (self.rowData.count - 1) {
            if (self.nextPage ?? "").isEmpty == false {
                self.getDefaultTrack(track: track, offset: nextInt, limit: 20)
            }
        }
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.rowData.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = Bundle.main.loadNibNamed("TrackTableViewCell", owner: self, options: nil)?.first as! TrackTableViewCell

        cell.trackNameLabel.text = self.rowData[indexPath.row].name
        cell.artistNameLabel.text = ConvertString().fromArrayArtistToString(array: self.rowData[indexPath.row].artists!)
        cell.albumNameLabel.text = self.rowData[indexPath.row].album?.name

        let url = URL(string: (self.rowData[indexPath.row].album?.images?[0].url)!)
        cell.gambarAlbumImage.kf.indicatorType = .activity
        cell.gambarAlbumImage.kf.setImage(with: url)

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 98
    }
}

extension ViewController: UISearchBarDelegate {
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        self.track = self.searchBar.text!
        self.nextInt = 0
        self.getDefaultTrack(track: self.track, offset: self.nextInt, limit: self.limitInt)
        self.searchBar.resignFirstResponder()
        self.searchBar.setShowsCancelButton(false, animated: true)
        self.searchBar.resignFirstResponder()
    }

    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
        self.searchBar.setShowsCancelButton(true, animated: true)
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        self.searchBar.text = nil
        self.searchBar.setShowsCancelButton(false, animated: true)
        self.searchBar.resignFirstResponder()
    }
}

Nah setelah selesai, silahkan jalankan aplikasi kalian, berikut adalah demo aplikasi nya :)

Artikel selanjutnya akan membahas mengenai bagaimana cara untuk memutar lagu :D. Untuk source code diatas dapat anda akses di Belajar-iOS. Sekian artikel mengenai Belajar iOS bagian 3, jika ada saran dan komentar silahkan isi dibawah dan terima kasih :)

Hy jumpa lagi :D, artikel ini merupakan kelanjutan dari artikel sebelumnya. Pada bagian kedua ini kita akan mencoba membuat hingga tahap menampilkan data :). Berikut adalah tahapan development yang akan kita lakukan.

  • Apa Itu Storyboards, xib dan auto layout ?
  • Membuat View Dan Navigation
  • Membuat Service Dengan RxSwift Dan Alamofire
  • Membuat Helper Dan Controller

Apa Itu Storyboards, XIB dan Auto Layout ?

Storyboard merupakan salah satu fitur untuk melakukan desain aplikasi. Pada zaman dulu, developer hanya dapat menggunakan fitur XIB. Storyboard dan XIB sama - sama mempunyai fungsi untuk melakukan desain interface aplikasi, akan tetapi pada zaman sekarang developer apple merekomendasikan untuk menggunakan storyboard. Di dalam story board kita dapat menggunakan beberapa layer aplikasi seperti gambar berikut.

Screen Shot 2017-04-30 at 9.50.18 PM.png

Sehingga anda dapat mengetahui alur dari aplikasi tersebut secara detail. Berbeda dengan XIB, di dalam xib kita hanya dapat menggunakan 1 layer aplikasi saja seperti gambar berikut.

Screen Shot 2017-04-30 at 9.52.35 PM.png

Akan tetapi, ketika dihadapkan dengan suatu project besar maka kedua nya akan tetap bisa kita gunakan karena masing - masing dari mereka berdua mempunyai kelebihan dan kekurangan masing - masing misalnya jika kita menggunakan 1 storyboard dengan banyak layer maka proses membuka storyboard tersebut akan memakan waktu lama disebabkan banyak nya layer di dalam nya.

Bagaimana dengan auto layout ?

Auto Layout biasanya digunakan jika kita akan mendevelop aplikasi dengan berbagai ukuran layar

Biasanya kita tidak hanya terpaku di 1 ukuran layar saja, kita diharuskan untuk membuat untuk beberapa ukuran layar maka kita dapat menggunakan fitur auto layout. Fitur auto layout ini sangat mirip dengan fitur constraint nya android. Berikut adalah percobaan untuk membuat auto layout.

Membuat Contoh Auto Layout

Silahkan buat sebuah project, lalu silahkan drag button dari menu kanan bawah seperti berikut.

Screen Shot 2017-04-30 at 9.59.24 PM.png

Setelah selesai, silahkan klik menu berikut lalu sesuaikan property nya seperti berikut.

Screen Shot 2017-04-30 at 10.03.16 PM.png

dan kemudian pilih add 4 constrains. Maka secara otomatis button kalian akan seperti berikut.

Screen Shot 2017-04-30 at 10.04.59 PM.png

Gambar diatas telah diubah warna backgroud dan warna tulisan nya, untuk mengubahnya, kamu dapat menggunakan menu seperti gambar berikut.

Screen Shot 2017-04-30 at 10.06.40 PM.png

Silahkan dijalankan di beberapa emulator untuk melihat hasil nya :).

Membuat View Dan Navigation

Oke, kembali ke project yang akan kita buat. Pada bagian ini kita akan membuat view dan navigation view. Secara default project anda hanya memiliki 1 storyboard yaitu main.storyboard. Silahkan buka storyboard tersebut, lalu pilih view controller nya, pada menu bagian atas silahkan pilih editor lalu pilih embed in dan terakhir pilih navigation controller maka akan muncul output seperti berikut.

Screen Shot 2017-04-30 at 10.34.18 PM.png

Setelah selesai, silahkan drag 1 table view ke view controller, lalu berikan constraint seperti berikut.

Screen Shot 2017-04-30 at 10.36.25 PM.png

Selanjutnya silahkan buat 1 group yaitu views, di dalam group views kita akan membuat view khusus untuk menampilkan list data lagu. Karena kita menggunakan table view, maka kita membutuhkan table view cell untuk list cell nya. Untuk membuat table view cell, silahkan klik kanan pada group views, lalu pilih new file, lalu pilih cocoa touch class, lalu klik next dan isikan property nya seperti berikut.

Screen Shot 2017-04-30 at 10.40.55 PM.png

Maka akan muncul 2 file yaitu TrackTableViewCell.swift dan TrackTableViewCell.xib. Silahkan buka yang xib lalu silahkan membuat desain seperti berikut.

Screen Shot 2017-04-30 at 10.44.49 PM.png

Setelah selesai, silahkan ikuti tutorial berikut untuk konfigurasi dari view table cell ke controller table cell.

Tahap selanjutnya, silahkan ikuti tutorial berikut untuk main storyboard.

Membuat Service Dengan RxSwift Dan Alamofire

Pada bagian ini, kita akan membuat service. Biasanya service disini saya gunakan untuk melakukan request data ke API. Untuk melakukan request data, kita akan menggunakan Alamofire. Untuk melakukan parsing json ke object swift biasanya saya menggunakan ObjectMapper. Untuk membuat aplikasi suatu event request yang asynchronous maka kita gunakan RxSwift, bagi anda yang ingin memperdalam tentang reactivex silahkan baca di ReactiveX.

Silahkan buat sebuah group dengan nama services lalu buatlah sebuah file swift dengan nama TrackService di dalam group service tersebut. Lalu isikan kodingan seperti berikut.

//
//  TrackService.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 4/30/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import Alamofire
import ObjectMapper
import AlamofireObjectMapper
import RxSwift

class TrackService {
    func getTrackByQuery(parameters: String, offset: Int, limit: Int) -> Observable<TrackResponse> {
        return Observable<TrackResponse>.create { observer -> Disposable in
            let request = Alamofire
                .request("https://api.spotify.com/v1/search?q=\(parameters)&type=track&offset=\(offset)&limit=\(limit)", method: .get)
                .validate()
                .responseObject(completionHandler: { (response: DataResponse<TrackResponse>) in
                    switch response.result {
                    case .success(let trackResponse):
                        observer.onNext(trackResponse)
                        observer.onCompleted()
                    case .failure(let error):
                        observer.onError(error)
                    }
                })
            return Disposables.create(with: {
                request.cancel()
            })
        }
    };
}

Dengan menggunakan RxSwift maka kita dapat menggunakan fitur Observable, fitur ini mirip dengan fitur List di java. Disana terdapat 3 parameter yaitu parameters biasanya ini untuk parameter yang akan kita search, offset sebagai halaman ke berapa karena pada api ini kita menggunakan paging, dan yang terakhir adalah limit yang berfungsi untuk mendeklarasikan berapa per halaman yang akan kita tampilkan.

Membuat Helper Dan Controller

Tahap terakhir kita akan membuat helper dan controller. Helper disini sendiri biasanya kita buat agar helper ini dapat digunakan di banyak tempat. Contohnya adalah, jika kita melakukan request, dimana jika kita menggunakan parameter dan parameter tersebut mempunyai spasi maka kita harus menconvert spasi dengan +, akan tetapi fungsi merubah karakter ini bergantung dari kebutuhan API, ada yang mengharuskan diubah menjadi + atau ada juga menggunakan %20 dan lain sebagainya.

Silahkan buat sebuah group dengan nama helpers lalu buat sebuah file swift dengan nama ConvertString, kemudian masukkan kodingan seperti berikut.

//
//  ConvertString.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 5/1/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import Foundation

class ConvertString {
    func toPlusString(text: String) -> String {
        return text.replacingOccurrences(of: " ", with: "+")
    }

    func fromArrayArtistToString(array: [Artist]) -> String {
        var text = ""
        if array.count == 1 {
            text = array[0].name!
        } else {
            for i in 0..<array.count {
                text = text + array[i].name! + ", "
            }
        }

        if text.substring(from: text.index(text.endIndex, offsetBy: -2)) == ", " {
            text.remove(at: text.index(text.endIndex, offsetBy: -2))
        }

        return text
    }
}

Pada class diatas terdapat 2 fungsi yaitu, fungsi toPlusString berfungsi untuk me replace string kosong dengan karakter +. Sedangkan fungsi fromArrayArtistToString kita gunakan untuk menconvert dari array object Artist menjadi string biasa.

Selanjutnya silahkan buka kembali file TrackTableViewCell lalu ubah codingan nya menjadi seperti berikut.

//
//  TrackTableViewCell.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 4/30/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import UIKit
import MarqueeLabel

class TrackTableViewCell: UITableViewCell {

    @IBOutlet weak var albumNameLabel: MarqueeLabel!
    @IBOutlet weak var artistNameLabel: MarqueeLabel!
    @IBOutlet weak var trackNameLabel: MarqueeLabel!
    @IBOutlet weak var gambarAlbumImage: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()
        
        self.trackNameLabel.type = .continuous
        self.trackNameLabel.speed = .duration(15)
        self.trackNameLabel.animationCurve = .easeInOut
        
        self.artistNameLabel.type = .continuous
        self.artistNameLabel.speed = .duration(15)
        self.artistNameLabel.animationCurve = .easeInOut
        
        self.albumNameLabel.type = .continuous
        self.albumNameLabel.speed = .duration(15)
        self.albumNameLabel.animationCurve = .easeInOut
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}

Pada kodingan diatas, kita mendeklarasikan text marquee, tujuan nya adalah jika terdapat text yang panjang melebihi lebar view, maka secara otomatis text tersebut akan berjalan sehingga semua tulisan dapat anda baca. Setelah selesai, silahkan buka file ViewController lalu ubah kodingannya menjadi seperti berikut.

//
//  ViewController.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 4/30/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import UIKit
import RxSwift
import Kingfisher
import SwiftOverlays

class ViewController: UIViewController {

    @IBOutlet weak var trackTable: UITableView!

    var rowData: [Item] = []
    var nextPage: String? = nil
    var nextInt = 0
    var limitInt = 20
    var track = "ColdPlay"
    var isSearch = false

    private var disposeBag = DisposeBag()
    private var trackService = TrackService()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Belajar iOS"
        self.getDefaultTrack(track: track, offset: nextInt, limit: limitInt)
        self.trackTable.delegate = self
        self.trackTable.dataSource = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func getDefaultTrack(track: String, offset: Int, limit: Int) {
        SwiftOverlays.showBlockingWaitOverlay()
        trackService
            .getTrackByQuery(parameters: ConvertString().toPlusString(text: track), offset: offset, limit: limit)
            .subscribe(
                onNext: { trackResponse in
                    if offset == 0 && trackResponse.tracks?.items?.count != 0 {
                        self.rowData.removeAll()
                    }
                    if trackResponse.tracks?.items?.count != 0 {
                        self.rowData.append(contentsOf: (trackResponse.tracks?.items)!)
                        if ((trackResponse.tracks?.next) ?? "").isEmpty == false {
                            self.nextPage = (trackResponse.tracks?.next)!
                        } else {
                            self.nextPage = nil
                        }
                        self.nextInt = (trackResponse.tracks?.offset)! + 20
                        self.trackTable.reloadData()
                    } else {
                        let alert = UIAlertController(title: "Info", message: "Maaf, Data Tidak ditemukan :(", preferredStyle: UIAlertControllerStyle.alert)
                        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                        self.present(alert, animated: true, completion: nil)
                    }
                    SwiftOverlays.removeAllBlockingOverlays()
                },
                onError: { error in
                    SwiftOverlays.removeAllBlockingOverlays()
                }
            )
            .addDisposableTo(disposeBag)
    }

}

Karena kita membutuhkan delegate atau action untuk table, maka kita membutuhkan sebuah class extension, dimana class ini berfungsi untuk memisahkan fungsi implementasi class Protocol. Class Protocol ini sama seperti class interface yang ada di java. Silahkan tambahkan codingan extension berikut di akhir baris.

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if indexPath.row == (self.rowData.count - 1) {
            if (self.nextPage ?? "").isEmpty == false {
                self.getDefaultTrack(track: track, offset: nextInt, limit: 20)
            }
        }
    }
}

Karena dia ingin menampilkan data maka kita juga membutuhkan dataSource, silahkan buat 1 extension lagi untuk dataSource seperti berikut.

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.rowData.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = Bundle.main.loadNibNamed("TrackTableViewCell", owner: self, options: nil)?.first as! TrackTableViewCell

        cell.trackNameLabel.text = self.rowData[indexPath.row].name
        cell.artistNameLabel.text = ConvertString().fromArrayArtistToString(array: self.rowData[indexPath.row].artists!)
        cell.albumNameLabel.text = self.rowData[indexPath.row].album?.name

        let url = URL(string: (self.rowData[indexPath.row].album?.images?[0].url)!)
        cell.gambarAlbumImage.kf.indicatorType = .activity
        cell.gambarAlbumImage.kf.setImage(with: url)

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 98
    }
}

Function pertama berfungsi sebagai mendefinisikan jumlah data. Pada function kedua, kita melakukan load file xib, dimana file xib ini adalah table view cell, nah data nya akan kita input melalui parent class nya yaitu ViewController, dan pada function terakhir mendefinisikan tinggi dari xib.

Jika telah selesai, silahkan jalankan project nya, jika berhasil maka anda dapat melihat hasil nya seperti berikut :D.

Screen Shot 2017-05-01 at 1.16.04 AM.png

Artikel selanjutnya akan membahas mengenai bagaimana cara membuat search bar :). Untuk source code diatas dapat anda akses di Belajar-iOS. Sekian artikel mengenai Belajar iOS bagian 2, jika ada saran dan komentar silahkan isi dibawah dan terima kasih :)

Pada artikel sebelumnya penulis telah membahas mengenai bahasa pemrograman swift versi 3. Pada artikel ini, penulis akan membahas bagaimana cara membuat aplikasi iOS sederhana :).

Kebutuhan Project

Pada dasarnya, swift dapat dijalankan di linux dan OSX, akan tetapi ketika kita mulai ingin membangun sebuah aplikasi iOS maka kita membutuhkan sistem operasi OSX. Mengapa demikian ? karena untuk membuat aplikasi iOS membutuhkan IDE Xcode, mungkin ada yang bertanya, apakah kita dapat membuat aplikasi iOS tanpa Xcode ? ya bisa saja seperti menggunakan AppCode punya jetbrains, akan tetapi kita diharuskan semuanya untuk ngoding mulai dari logic hingga UI nya :(, berbeda dengan Xcode yang akan memberikan fitur UI layaknya android studio :D. Penulis menyarankan anda untuk menggunakan Xcode untuk development iOS, terdapat banyak fitur salah satunya adalah Xcode dapat memberitahukan kepada kita jika layout yang kita buat tidak responsive bahkan dia dapat mengetahui jika tata letak suatu komponent tidak sesuai dengan letaknya :). Wow… mungkin fitur Xcode bahkan bisa dibilang lebih lengkap dari android studio, oke berikut adalah kebutuhan project untuk development iOS.

  • MacBook / Laptop dengan Hackintosh, disarankan menggunakan OSX sierra
  • Xcode terbaru, pada artikel ini penulis menggunakan Xcode versi 8.3.2
  • secangkir kopi untuk bersantai :D

Tahap - Tahap Development

Kira - kira kita akan membuat apa ya ? ya, pada artikel ini, kita akan membuat sebuah aplikasi untuk menampilkan lagu - lagu yang ada di spotify :D. Bagaimana cara nya kita dapat mendapatkan datanya ? :o, gampang kok, kita hanya perlu mengakses API yang telah disediakan oleh spotify disini, karena kita hanya akan menampilkan item tertentu maka kita akan menggunakan API search yang ada pada spotify yaitu API yang ada disini. Pada API spotify, kita tidak diharuskan untuk login terkecuali jika berhubungan dengan data user :). Berikut adalah tahapan development yang akan kita lakukan.

  • Membuat Project iOS dengan Xcode
  • Melakukan Instalasi Dependency Dengan Cocoapods
  • Membuat Model Response
  • Membuat Service / Consume API
  • Membuat View Dengan Multi StoryBoard Dan Navigation
  • Membuat Controller
  • Uji Coba Aplikasi

Membuat Project iOS dengan Xcode

Hal yang pertama kita lakukan adalah membuat project dengan Xcode. Silahkan buka Xcode anda maka akan muncul tampilan seperti berikut.

Screen Shot 2017-03-31 at 10.29.11 PM.png

Lalu pilih create a new Xcode project maka akan muncul menu seperti berikut.

Screen Shot 2017-03-31 at 10.31.17 PM.png

Lalu pilih single view application lalu klik next dan muncul menu untuk pengisian deskripsi aplikasi, silahkan isi seperti berikut.

Screen Shot 2017-03-31 at 10.33.39 PM.png

Jika berhasil maka akan muncul menu project seperti berikut.

Screen Shot 2017-03-31 at 10.34.24 PM.png

Melakukan Instalasi Dependency Dengan Cocoapods

Terkadang pada sebuah project yang kita buat, kita membutuhkan dependency library dari pihak lain misalnya seperti Alamofire untuk consume API dan lain sebagainya. Setiap bahasa pemrograman mempunya tool tersendiri, begitu pula dengan swift, di swift kita dapat menggunakan Cocoapods sebagai dependency management karena Cocoapods salah satu tool yang sangat banyak digunakan oleh kalangan developer iOS.

Untuk melakukan instalasi Cocoapods, kita dapat menggunakan fungsi gem yang secara default telah tersedia di OSx, silahkan jalankan perintah berikut untuk melakukan instalasi cocoapods.

gem install cocoapods

Setelah selesai, silahkan close project Xcode anda, lalu akses folder project iOS anda dan jalankan perintah berikut untuk membuat konfigurasi cocoapods.

pod init

Maka akan terbentuk sebuah file yaitu Podfile langka selanjutnya silahkan buka file Podfile tersebut lalu silahkan ubah konfigurasi seperti berikut.

# Uncomment the next line to define a global platform for your project
 platform :ios, '10.0'

target 'Belajar-iOS' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for Belajar-iOS
  pod 'RxSwift',    '~> 3.0'
  pod 'RxCocoa',    '~> 3.0'
  pod 'ObjectMapper', '~> 2.2'
  pod 'Alamofire', '~> 4.4'
  pod 'AlamofireObjectMapper', '~> 4.0'
  pod 'Kingfisher', '~> 3.0'
  pod 'SwiftOverlays', '~> 3.0.0'
  pod 'AudioPlayerManager'
  pod 'MarqueeLabel/Swift'

  target 'Belajar-iOSTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'Belajar-iOSUITests' do
    inherit! :search_paths
    # Pods for testing
  end

end

Berikut adalah beberapa penjelasan dari konfigurasi diatas :

  • platform : mendefiniskan versi iOS yang akan kita gunakan
  • RxSwift : berfungsi sebagai manipulasi UI Events dan untuk menghandle response API
  • RxCocoa : berfungsi sebagai library tambahan untuk mendukung RxSwift karena kita menggunakan Cocoapods
  • ObjectMapper : berfungsi untuk melakukan mapping json
  • Alamofire : berfungsi untuk mengakses API
  • AlamofireObjectMapper : berfungsi untuk mengubah response dari alamofire menjadi object swift
  • Kingfisher : berfungsi untuk cache gambar
  • SwiftOverlays : berfungsi untuk animasi loading
  • AudioPlayerManager : berfungsi untuk memutarkan lagu
  • MarqueeLabel : berfungsi untuk membuat text marquee

Oke, selanjutnya silahkan jalankan perintah berikut untuk melakukan instalasi dependency diatas.

pod install

Silahkan buka folder project anda, jika berhasil maka akan muncul sebuah workspace seperti berikut.

Screen Shot 2017-03-31 at 11.03.41 PM.png

Lalu silahkan klik kanan pada Belajar-iOS.xcworkspace tersebut lalu open with Xcode maka tampilan project anda akan seperti berikut.

Screen Shot 2017-03-31 at 11.06.02 PM.png

Membuat Model Response

Setelah selesai dengan dependency management, langkah selanjutnya kita akan membuat model response dimana sebenarnya model response ini adalah representasi dari response json yang berasal dari API. Berikut adalah contoh json yang dihasilkan dari API spotify.

{
  "tracks": {
    "href": "https://api.spotify.com/v1/search?query=Coldplay&type=track&offset=0&limit=20",
    "items": [
      {
        "album": {
          "album_type": "single",
          "artists": [
            {
              "external_urls": {
                "spotify": "https://open.spotify.com/artist/69GGBxA162lTqCwzJG5jLp"
              },
              "href": "https://api.spotify.com/v1/artists/69GGBxA162lTqCwzJG5jLp",
              "id": "69GGBxA162lTqCwzJG5jLp",
              "name": "The Chainsmokers",
              "type": "artist",
              "uri": "spotify:artist:69GGBxA162lTqCwzJG5jLp"
            },
            {
              "external_urls": {
                "spotify": "https://open.spotify.com/artist/4gzpq5DPGxSnKTe4SA8HAU"
              },
              "href": "https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU",
              "id": "4gzpq5DPGxSnKTe4SA8HAU",
              "name": "Coldplay",
              "type": "artist",
              "uri": "spotify:artist:4gzpq5DPGxSnKTe4SA8HAU"
            }
          ],
          "available_markets": [
            "AD",
            "AR",
            "AT",
            "AU",
            "BE",
            "BG",
            "BO",
            "BR",
            "CA",
            "CH",
            "CL",
            "CO",
            "CR",
            "CY",
            "CZ",
            "DE",
            "DK",
            "DO",
            "EC",
            "EE",
            "ES",
            "FI",
            "FR",
            "GB",
            "GR",
            "GT",
            "HK",
            "HN",
            "HU",
            "ID",
            "IE",
            "IS",
            "IT",
            "JP",
            "LI",
            "LT",
            "LU",
            "LV",
            "MC",
            "MT",
            "MX",
            "MY",
            "NI",
            "NL",
            "NO",
            "NZ",
            "PA",
            "PE",
            "PH",
            "PL",
            "PT",
            "PY",
            "SE",
            "SG",
            "SK",
            "SV",
            "TR",
            "TW",
            "US",
            "UY"
          ],
          "external_urls": {
            "spotify": "https://open.spotify.com/album/7IzpJkWQqgz1BTutQvSitX"
          },
          "href": "https://api.spotify.com/v1/albums/7IzpJkWQqgz1BTutQvSitX",
          "id": "7IzpJkWQqgz1BTutQvSitX",
          "images": [
            {
              "height": 640,
              "url": "https://i.scdn.co/image/a57625bbecee7b4e7fe932a31cd92319d2a32855",
              "width": 640
            },
            {
              "height": 300,
              "url": "https://i.scdn.co/image/4c45b4ad2a79f5a345e854180cec249731db1908",
              "width": 300
            },
            {
              "height": 64,
              "url": "https://i.scdn.co/image/cd7345b44b0e950735295029bfd96b5d174bead6",
              "width": 64
            }
          ],
          "name": "Something Just Like This",
          "type": "album",
          "uri": "spotify:album:7IzpJkWQqgz1BTutQvSitX"
        },
        "artists": [
          {
            "external_urls": {
              "spotify": "https://open.spotify.com/artist/69GGBxA162lTqCwzJG5jLp"
            },
            "href": "https://api.spotify.com/v1/artists/69GGBxA162lTqCwzJG5jLp",
            "id": "69GGBxA162lTqCwzJG5jLp",
            "name": "The Chainsmokers",
            "type": "artist",
            "uri": "spotify:artist:69GGBxA162lTqCwzJG5jLp"
          },
          {
            "external_urls": {
              "spotify": "https://open.spotify.com/artist/4gzpq5DPGxSnKTe4SA8HAU"
            },
            "href": "https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU",
            "id": "4gzpq5DPGxSnKTe4SA8HAU",
            "name": "Coldplay",
            "type": "artist",
            "uri": "spotify:artist:4gzpq5DPGxSnKTe4SA8HAU"
          }
        ],
        "available_markets": [
          "AD",
          "AR",
          "AT",
          "AU",
          "BE",
          "BG",
          "BO",
          "BR",
          "CA",
          "CH",
          "CL",
          "CO",
          "CR",
          "CY",
          "CZ",
          "DE",
          "DK",
          "DO",
          "EC",
          "EE",
          "ES",
          "FI",
          "FR",
          "GB",
          "GR",
          "GT",
          "HK",
          "HN",
          "HU",
          "ID",
          "IE",
          "IS",
          "IT",
          "JP",
          "LI",
          "LT",
          "LU",
          "LV",
          "MC",
          "MT",
          "MX",
          "MY",
          "NI",
          "NL",
          "NO",
          "NZ",
          "PA",
          "PE",
          "PH",
          "PL",
          "PT",
          "PY",
          "SE",
          "SG",
          "SK",
          "SV",
          "TR",
          "TW",
          "US",
          "UY"
        ],
        "disc_number": 1,
        "duration_ms": 247626,
        "explicit": false,
        "external_ids": {
          "isrc": "USQX91700278"
        },
        "external_urls": {
          "spotify": "https://open.spotify.com/track/1dNIEtp7AY3oDAKCGg2XkH"
        },
        "href": "https://api.spotify.com/v1/tracks/1dNIEtp7AY3oDAKCGg2XkH",
        "id": "1dNIEtp7AY3oDAKCGg2XkH",
        "name": "Something Just Like This",
        "popularity": 97,
        "preview_url": "https://p.scdn.co/mp3-preview/499eefd42a24ec562c464bd7acfad7ed41eb9179?cid=null",
        "track_number": 1,
        "type": "track",
        "uri": "spotify:track:1dNIEtp7AY3oDAKCGg2XkH"
      }
    ],
    "limit": 20,
    "next": "https://api.spotify.com/v1/search?query=Coldplay&type=track&offset=20&limit=20",
    "offset": 0,
    "previous": null,
    "total": 4618
  }
}

Kita tidak akan menggunakan semua value diatas, kita hanya akan menampilkan track, nama artist, nama album, judul album, gambar album dan akan memutarkan contoh lagu nya. Silahkan klik kanan pada Folder Belajar-iOS yang berwarna kuning lalu pilih New Group dan rename menjadi models. Setelah selesai, silahkan klik kanan pada group model lalu pilih menu New File, silahkan pilih menu Swift File, kemudian untuk menetukan direktory, anda harus membuat folder terlebih dahulu sehingga group models akan direpresentasikan ke folder models, berikan nama TrackResponse pada file swift yang anda buat, kemudian masukkan codingan seperti berikut.

//
//  TrackResponse.swift
//  Belajar-iOS
//
//  Created by rizki mufrizal on 4/30/17.
//  Copyright © 2017 rizki mufrizal. All rights reserved.
//

import Foundation
import ObjectMapper

struct TrackResponse: Mappable {
    var tracks: Track?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        tracks <- map["tracks"]
    }
}

struct Track: Mappable {
    var limit: Int?
    var next: String?
    var offset: Int?
    var previous: String?
    var total: Int?
    var items: [Item]?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        limit <- map["limit"]
        next <- map["next"]
        offset <- map["offset"]
        previous <- map["previous"]
        total <- map["total"]
        items <- map["items"]
    }
}

struct Item: Mappable {
    var name: String?
    var previewUrl: String?
    var album: Album?
    var artists: [Artist]?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        name <- map["name"]
        previewUrl <- map["preview_url"]
        album <- map["album"]
        artists <- map["artists"]
    }
}

struct Artist: Mappable {
    var name: String?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        name <- map["name"]
    }
}

struct Album: Mappable {
    var name: String?
    var images: [Image]?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        name <- map["name"]
        images <- map["images"]
    }
}

struct Image: Mappable {
    var height: Int?
    var width: Int?
    var url: String?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        height <- map["height"]
        width <- map["width"]
        url <- map["url"]
    }
}

Akhirnya selesai juga untuk class response nya :D. Artikel selanjutnya akan membahas mengenai bagaimana cara membuat service, view dan controller nya :). Untuk source code diatas dapat anda akses di Belajar-iOS. Sekian artikel mengenai Belajar iOS bagian 1, jika ada saran dan komentar silahkan isi dibawah dan terima kasih :)

Bermula dari project yang sedang saya kerjakan di kantor yaitu aplikasi iOS, pada kesempatan ini saya akan share sedikit mengenai dasar - dasar pada swift :D.

Apa Itu Swift ?

Swift adalah bahasa pemrograman yang digunakan untuk membangun aplikasi untuk produk apple seperti iOS, OSX dan lain sebagainya.

Terdapat 2 bahasa pemrograman yang dapat kita gunakan untuk mengembangkan aplikasi untuk produk apple yaitu Objective-C dan Swift. Pada artikel ini, penulis hanya membahas mengenai bahasa pemrograman swift untuk versi 3, untuk migrasi versi swift 3 silahkan lihat di migration swift 3.

Mengapa Menggunakan Swift ?

Mungkin beberapa developer swift pemula pasti akan bertanya, mengapa kita menggunakan swift ? mengapa tidak menggunakan Objective-C ?, Baiklah kita akan membahas satu - persatu.

Pada zaman sekarang, para developer core dari berbagai bahasa pemrograman akan membuatkan sebuah bahasa dimana bahasa ini lebih mudah dimengerti manusia, berbeda dengan developer apple, mereka membuat bahasa pemrograman swift ini sendiri lebih dekat dengan mesin layaknya bahasa C, C++ dan Objective-C sehingga akan meningkatkan performance dari aplikasi.

Swift sendiri dibangun dengan paradigma yang berbeda dengan Objective-C, dimana bahasa pemrograman swift lebih mudah dibaca jika anda beralirah ke bahasa pemrograman berorientasi object seperti java, javascript, ruby dan lain sebagainya. Jika anda dulu nya adalah developer bahasa C atau C++ maka anda akan lebih mudah membaca source code Objective-C. Jika anda adalah developer pemula, penulis sarankan untuk mempelajari bahasa pemrograman swift 3 karena Objective-C dan swift adalah bahasa pemrograman yang berbeda dan tidak ada kaitan dari kedua nya.

Jika anda adalah seorang developer android, terlebih jika anda telah terbiasa menggunakan bahasa pemrograman kotlin maka anda tidak akan terlalu susah jika migrasi dari android ke iOS. Bahkan anda akan memiliki learning curve yang lebih cepat.

Latihan Swift

Pada artikel ini, penulis menggunakan IBM Swift Sandbox karena kita hanya akan latihan cli dengan menggunakan bahasa pemrograman swift 3. IBM Swift Sandbox adalah salah satu editor cli online untuk latihan bahasa pemrograman swift, disana terdapat 2 versi yaitu swift versi 2 dan versi 3. Pada artikel ini, penulis akan menggunakan swift 3.

Latihan Variabel Pada Swift

Secara default, pada IBM Swift Sandbox akan menampilkan source code seperti berikut.

print("Hello world!")

Source code diatas berfungsi untuk menampilkan sebuah tulisan Hello Word! :-). Tahap selanjutnya tulisankan code seperti berikut.

let nama: String = "rizki mufrizal"
print(nama)

Codingan diatas berfungsi untuk membuat sebuah variabel dengan tipe data string, lalu kita inisialisasi dengan sebuah string lalu kita tampilkan. Di dalam swift terdapat 2 cara untuk mendeklarasikan variabel yaitu dengan menggunakan let dan var, dimana let berfungsi sebagai constanta atau immutable sehingga hanya bisa diinisialisasi sekali saja, berbeda dengan var yang bersifat mutable sehingga dapat diubah - ubah. Silahkan ubah source code diatas menjadi seperti berikut.

let nama: String = "rizki mufrizal"
nama = "ganti aja"
print(nama)

maka akan muncul error seperti berikut.

ERROR at line 5, col 6: cannot assign to value: 'nama' is a 'let' constant
nama = "ganti aja"
~~~~ ^
NOTE at line 4, col 1: change 'let' to 'var' to make it mutable
let nama: String = "rizki mufrizal"
^~~
var

Lalu silahkan ubah seperti berikut.

var nama: String = "rizki mufrizal"
nama = "ganti aja"
print(nama)

maka hasil nya akan sukses dikarenakan kita ingin mengubah value yang ada di dalam variabel nama.

Latihan Function Pada Swift

Sama seperti bahasa lain, swift juga dapat menggunakan function / method. Function / method pada swift juga dapat mengembalikan nilai berdasarkan tipe data nya dan juga dapat berupa void atau tidak mengembalikan nilai.

Berikut adalah contoh jika mengambalikan nilai.

func perjumlahan(angka1: Int, angka2: Int) -> Int {
    return angka1 + angka2
}
print("hasilnya adalah : \(perjumlahan(angka1: 2, angka2: 3))")

Diatas adalah contoh jika kita mengembalikan nilai, dan pada source code diatas kita menggunakan fungsi \() dimana fungsi tersebut adalah fungsi untuk mengeksekusi sebuah function atau variabel yang berada di dalam sebuah string atau “” (kutip dua).

Berikut adalah contoh jika function / method yang tidak mengembalikan nilai.

func perjumlahan(angka1: Int, angka2: Int) {
    print("hasilnya adalah : \(angka1 + angka2)")
}
perjumlahan(angka1: 2, angka2: 3)

Latihan Array Pada Swift

Pada latihan ini, kita akan membuat sebuah struct / record. Apa itu struct / record ?

Struct / record adalah kumpulan data dengan type yang berbeda

Sekilas memang mirip dengan array, akan tetepi terdapat perbedaan dimana array mempunyai banyak data dengan type data yang sama sedangkan struct / record memiliki data yang banyak dan memiliki type data yang berbeda. Jika kita menggunakan java, maka struct / record sama dengan class domain / entity / pojo. Berikut adalah contoh penggunaan array struct pada swift 3.

struct Barang {
    var idBarang: String?
    var namaBarang: String?
    var jumlahBarang: Int?
}

var barangs = [Barang]()

barangs.append(Barang(idBarang: "B001", namaBarang: "Rinso", jumlahBarang: 10))

for b in barangs {
    print("id Barang     : \(b.idBarang)")
    print("Nama Barang   : \(b.namaBarang)")
    print("Jumlah Barang : \(b.jumlahBarang)")
}

Maka hasilnya akan seperti berikut.

id Barang     : Optional("B001")
Nama Barang   : Optional("Rinso")
Jumlah Barang : Optional(10)

Agar tulisan Optional hilang maka tambahkan ! seperti berikut.

struct Barang {
    var idBarang: String?
    var namaBarang: String?
    var jumlahBarang: Int?
}

var barangs = [Barang]()

barangs.append(Barang(idBarang: "B001", namaBarang: "Rinso", jumlahBarang: 10))

for b in barangs {
    print("id Barang     : \(b.idBarang!)")
    print("Nama Barang   : \(b.namaBarang!)")
    print("Jumlah Barang : \(b.jumlahBarang!)")
}

Maka hasilnya akan seperti berikut.

id Barang     : B001
Nama Barang   : Rinso
Jumlah Barang : 10

Sekian artikel mengenai Belajar Swift, jika ada saran dan komentar silahkan isi dibawah dan terima kasih :)