Search

'ios'에 해당되는 글 12건

  1. 2020.12.05 Realm
  2. 2020.12.05 CoreData
  3. 2020.12.05 UserDefaults
  4. 2020.11.30 IQKeyboardManager
  5. 2020.11.30 URLSession
  6. 2020.11.30 Pod
  7. 2020.11.24 tableview
  8. 2020.11.23 codable
  9. 2020.11.23 extension
  10. 2020.11.23 segue
  11. 2020.11.21 protocol
  12. 2020.11.21 Delegate

Realm

개발/iOS 2020. 12. 5. 15:21 Posted by 아는 개발자

Realm은 오픈소스로 운영되는 모바일용 데이터베이스 클래스다. CoreData 처럼 관계형 데이터베이스를 읽고 쓰는 것을 지원하고있다. 개인적으로 CoreData를 사용하는 것 보다 훨씬 직관적이고 사용하기가 간편하다. 이번 포스트에서는 Realm을 Swift에 사용하는 방법을 간단히 정리해본다. 

 

1. Realm cocoapod 임포트

 

pod 'RealmSwift', '~> 4.4.1'

 

RealmSwift 라이브러리를 임포트시킨다. 몇 ios 버전에선 이전 Realm 버전으로 임포트하면 persmission 에러가 뜬다고 해서 4.4.1 버전으로 임의로 설정했다.

 

2. 클래스 설정 

 

import Foundation
import RealmSwift

class Category: Object {
    @objc dynamic var name: String = ""
    
    let items = List<Item>() 
}

 

Realm의 테이블로 추가할 클래스를 선언한다. Object 클래스를 상속해야하는데 이 클래스가 RealmSwift에 포함돼 있어야하는것을 유의하자. name은 Category 클래스의 이름에 해당하는 값이고 items는 Category 클래스와 연결된(relation) 클래스를 의미한다. Category와 Item이 1:N 의 관계가 될 예정이다.

 

import Foundation
import RealmSwift

class Item: Object {
    @objc dynamic var title: String = ""
    @objc dynamic var done: Bool = false
    @objc dynamic var dateCreated: Date?
    var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
}

 

Category 클래스에 포함될 Item 클래스를 선언한다. 아래 parentCategory에 LinkingObjects 를 활용하면 쉽게 둘간의 관계를 정해줄 수 있다.

 

3. CRUD 

 

3.1 Create 

 

let newCategory = Category()
newCategory.name = textField.text!

save(category: newCategory)

func save(category: Category) {
    let realm = try! Realm()
    do {
        try realm.write {
            realm.add(category)
        }
    } catch {
        print("Error saving contet \(error)")
    }
    
    self.tableView.reloadData()
}

 

생성하는 작업은 CoreData랑 비슷하다. 임의의 Category 클래스를 만들고 realm 라이브러리로 추가해주는 작업을 하면 된다. realm.write 함수의 콜백 내에 add 함수를 추가하면 된다. 

 

if let currentCategory = self.selectedCategory {
    do {
        try self.realm.write {
            let realm = try! Realm()
            let newItem = Item()
            newItem.title = textField.text!
            newItem.done = false
            newItem.dateCreated = Date()
            currentCategory.items.append(newItem)
        }

 

Category에  포함된 Item의 경우는 약간 다르다. 아래 코드를 보면 Category처럼 add 함수로 추가하는게 아니라 포함되어있는 Category 클래스의 items 리스트에 append 시켜서 추가하고 있다. 서로 linking되어 있어서 그렇다.

 

3.2 READ 

 

func loadCategoreis() {
    let realm = try! Realm()
    self.categories = realm.objects(Category.self)
    
    self.tableView.reloadData()
}

 

읽어오는 작업은 realm 클래스에서 objects 함수에 읽어오려는 클래스를 추가하면 된다. 이러면 간단히 읽어올 수 있게 된다. 

 

3.3 UPDATE 

 

if let item = todoItems?[indexPath.row] {
    do {
        try realm.write {
            item.done = !item.done

 

realm으로부터 읽어온 클래스를 realm.write 콜백내에서 값을 수정하기만 하면 된다.

 

3.4. DELETE 

 

if let item = todoItems?[indexPath.row] {
    do {
        try realm.write {
            realm.delete(item)

 

수정과 마찬가지로 realm으로 읽어온 클래스를 realm.write 콜백내에서 삭제하면 된다.

 

4. 총평 

 

다른 개발자들도 CoreData가 정말 쓰기 불편했나보다. realm을 찬양하는 개발자들이 더 많다. 그리고 성능도 realm이 coredata에 비해서 압도적으로 훌륭하다. 물론 앱에서 데이터를 저장할 때는 이정도로 중요할것 같지는 않지만 말이다.

 

728x90

'개발 > iOS' 카테고리의 다른 글

Realm  (0) 2020.12.05
CoreData  (0) 2020.12.05
UserDefaults  (0) 2020.12.05
IQKeyboardManager  (0) 2020.11.30
URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30

CoreData

개발/iOS 2020. 12. 5. 13:16 Posted by 아는 개발자

CoreData는 iOS 플랫폼 단에서 지원하는 관계형 데이터베이스 라이브러리다. XCode에서 지원하는 툴과 CoreData를 사용하면 SQL의 어려운 쿼리문을 사용하지 않고도 테이블을 만들고 데이터를 추가할 수 있다. 이번 포스트에서는 Xcode를 이용해 CoreData 를 초기화하고 기본 CRUD 작업을 수행하는 것을 다뤄보려고 한다.

 

1. Data Model 파일 생성 및 초기화

 

CoreData를 사용하려면 Data Model 타입의 파일을 하나 생성해야한다. New File을 클릭하고 필터로 data 를 입력해서 Data Model 타입의 파일을 하나 추가한다.

 

이 작업이 완료되면 AppDelegate.swift 파일에 아래 코드를 추가해야한다. 현재 앱에서 바라보고 있는 데이터 모델을 세팅해주고 앱이 꺼지기 전에 데이터를 저장하는 콜백을 등록하는 작업이다. 여기서 추가한 변수인 persistentContainer는 나중에 ViewController에서 여기서 선언한 변수를 사용할 예정이다. 

 

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
	... 
    
    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        self.saveContext()
    }

    lazy var persistentContainer: NSPersistentContainer = {
    
        let container = NSPersistentContainer(name: "DataModel")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()
    
    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

 

2.  Entity, Attribute 추가 

 

아까 생성했던 DataModel 파일을 클릭해보면 왼쪽 하단에 Add Entitiy 라는 버튼이 있다. 현재 앱에서 관리할 테이블을 추가하는 과정이라고 봐도 될 것 같다. 클릭하면 Entity라는 이름으로 새로운 Entity가 생성된다. 용도에 맞게 이름을 변경해준다. 나는 Item이라고 정했다.

 

 

Entity를 만들고 나면 오른쪽 하단에 Add Attribute 버튼이 있는데 Entity가 가지게 될 속성을 추가하는 작업이다. SQL로 생각하면 테이블에 컬럼을 추가하는 것과 같다. 앱을 만들며 필요하다고 생각한 칼럼 값을 여기에 추가하면 된다. Item Entity에 name이라는 속성을 String 타입으로 추가했다. 오른쪽 패널에 이 속성에 특정 값들을 설정 할 수 있다. Optional이면 null을 허용한다는 뜻이고 Derived는 상속을 받는다? 인것 같다. 이거는 구상한 스키마에 따라서 결정하면 될 것 같다.

 

속성을 모두 업데이트 하면 Entity의 오른쪽 패널을 통해 Entity에 대한 속성을 설정 할 수 있다. Module은 Entity로부터 파생되는 Class가 사용되는 모듈을 설정하는데 특별한 경우가 아니면 현재 프로덕트 모듈(Current Product Module)로 변경해준다. Codegen은 Entity가 필요한 클래스를 자동 생성할지(Class Definition) 아니면 커스텀하게 생성할 지 (Category/Extension)을 결정하는 작업이다. Category/Extension을 사용하는 경우 직접 클래스를 만들어야하기 때문에 아직 CoreData를 공부하는 입장에서는 Class Defnition을 먼저 사용해보는게 좋다.

 

 

3. CRUD 

 

3.1 CREATE

 

DataModel 작업을 마치면 ViewController에서 아까 만든 Entity를 직접 데이터로 추가하는 작업이 남았다.  먼저 새로운 Entity를 추가하는 코드를 보자. 아래 코드는 Alert view에서 Item의 이름을 받아와 새로 추가하는 작업이다. 코드 중간부분을 보면 newItem 이라는 새로운 변수를 추가했는데 생성자로 사용한 클래스가 Item이다. 이 Item 클래스는 이전에 Data Model에서 추가한 Entity랑 이름이 같다. 이 생성자에서는 context라는 인자를 받는데 AppDelegate 클래스에서 선언한 persistentContainer 변수의 속성인 viewContext를 상용한다. 그다음 context.save() 함수를 호출해서 이 값을 통해 Item 변수를 새롭게 추가할 수 있다. 이것만 해주면 된다. SQL처럼 insert 어쩌고 저쩌고 쿼리를 날릴 필요가 없어서 간편하다.

 

@IBAction func addButtonPressed(_ sender: Any) {
    
    var textField = UITextField()
    
    let alert = UIAlertController(title: "Add New Todoey Item", message: "", preferredStyle: .alert)
    
    let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
        // what will happend once user clicks the Add Item button on our UIAlert
        
        let container = (UIApplication.shared.delegate as! AppDelegate).persistentContainer
        let context = container.viewContext
        let newItem = Item(context: context)
        newItem.name = textField.text!
        self.itemArray.append(newItem)
        
        do {
            try context.save()
        } catch {
            print("Error saving contet \(error)")
        }

 

3.2 READ 

 

DataModel에서 만든 Entity에는 모두 fetchRequest() 라는 함수가 있는데 이 함수는 Entity에 해당하는 데이터를 읽어올 수 있는 select Request 쿼리를 갖고 있다. 따로 변수로 만들어서 생성 작업과 동일하게 context를 불러오고 fetch 함수 내에 request를 인자로 넣으면 데이터를 읽어올 수 있다.

func loadItems() {
    let request: NSFetchRequest<Item> = Item.fetchRequest()
    
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    do {
        itemArray = try context.fetch(request)
    } catch {
        print("error fetching data from context \(error)")
    }

 

3.3 UPDATE 

 

수정작업은 CoreData로부터 받아온 클래스의 속성 값을 수정하고 save를 호출하는 작업으로 이뤄진다. 이것 또한 복잡한 쿼리를 넣을 필요 없이 간단히 수정이 가능하다.

 

func updateItem(item: Item) {
    item.name = "update name"
    
    do {
        try context.save()
    } catch {
        print("error fetching data from context \(error)")
    }

 

3.4 DELETE 

 

삭제도 크게 다르지 않다. CoreData로부터 받아온 클래스를 context.delete의 인자로 넣어주고 save를 호출한다. 이렇게만 하면 데이터가 삭제된다.

 

func delete(item: Item) {
    context.delete(item)
    
    do {
        try context.save()
    } catch {
        print("error fetching data from context \(error)")
    }

 

4. 총평 

 

복잡한 쿼리를 짤 필요가 없어서 간편하긴 한데 Xcode 특유의, 마우스를 활용한 작업이 많아서 그런가 익숙해지려면 시간이 좀 걸릴 것 같은 느낌이다. 속성같은 것들은 키보드로 설정하는게 더 간편하고 직관적인데 말이다.

728x90

'개발 > iOS' 카테고리의 다른 글

Realm  (0) 2020.12.05
CoreData  (0) 2020.12.05
UserDefaults  (0) 2020.12.05
IQKeyboardManager  (0) 2020.11.30
URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30

UserDefaults

개발/iOS 2020. 12. 5. 12:31 Posted by 아는 개발자

앱을 개발하다 보면 종종 단일의 데이터를 저장해야하는 경우가 생긴다. 예로 들면 어떤 가이드 화면을 보여줬는지 안보여줬는지 유무를 저장하는 Boolean 타입의 데이터나 영상의 음량을 미리 정해두는 Float 타입의 데이터값 같은 것들이 있다. 이런 데이터들은 관계형 데이터베이스로 저장하는 것 보다는 key - value로 저장하는게 효율적인데 iOS에서는 UserDefaults라는 라이브러리를 이용해 이 기능을 제공한다. 안드로이드를 경험한 개발자들은 SharedPreference 클래스와 비슷한 역할을 한다고 보면 될 것 같다. 사용하는 방법도 비슷하고 간편하다.

 

class TodoListViewController: UITableViewController {
    
    let defaults = UserDefaults.standard
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        defaults.setValue(false, forKey: "booleanKey")
        defaults.setValue(123, forKey: "integerKey")
        defaults.bool(forKey: "booleanKey")
        defaults.integer(forKey: "integerKey")

 

값을 업데이트 할 때는 setValue 함수로 value와 key값을 넣고 읽을 때는 불러오려는 데이터의 타입형의 함수에 key 값을 넣어서 호출한다. UserDefaults 함수로 초기화만 잘 해주면 돼서 사용하는데 큰 어려움은 없다.

728x90

'개발 > iOS' 카테고리의 다른 글

Realm  (0) 2020.12.05
CoreData  (0) 2020.12.05
UserDefaults  (0) 2020.12.05
IQKeyboardManager  (0) 2020.11.30
URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30

IQKeyboardManager

개발/iOS 2020. 11. 30. 11:07 Posted by 아는 개발자

 

iOS에서도 소프트 키보드를 토글하면 UI 뷰 화면을 덮어버리는 문제가 있었다. 안드에서는 그래도 activity 단에서 키보드가 뜰때 어떻게 UI 레이아웃을 변형할지 어느정도 조정이 가능한데 ios에서는 그런게 특별히 없는 것 같다. 키보드가 뜰 때 마다 OS에 물어봐서 keyboard의 height를 알아오고 그에 맞춰서 View를 올려줘야 한다고 하는데.. 매번 하기엔 꽤 번거로운 일이다. 

 

다행히 삽질(?)을 미리 해두고 라이브러리 형태로 배포를 해뒀다고 한다. 라이브러리 이름은 IQKeyboardManager 이고 사용방법도 아주 간단하다. 먼저 cocoapod을 이용해 라이브러리를 임포트 하자.

 

platform :ios, '13.0'

target 'Flash Chat iOS13' do
  use_frameworks!

  pod 'IQKeyboardManagerSwift'

end

 

임포트후 AppDelegate라는 파일에 IQKeyboardManager 클래스를 호출해서 관련 속성을 세팅하자.

 

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        IQKeyboardManager.shared.enable = true
        IQKeyboardManager.shared.enableAutoToolbar = false
        IQKeyboardManager.shared.shouldResignOnTouchOutside = true
        
        return true
    }

 

이렇게만 하고 다시 실행하면 keyboard가 올라옴에 따라서 UI가 자동으로 이동하는 것을 확인 할 수 있다.

 

728x90

'개발 > iOS' 카테고리의 다른 글

CoreData  (0) 2020.12.05
UserDefaults  (0) 2020.12.05
IQKeyboardManager  (0) 2020.11.30
URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30
tableview  (0) 2020.11.24

URLSession

개발/iOS 2020. 11. 30. 10:42 Posted by 아는 개발자

JAVA나 C, C++과 마찬가지로 swift 언어의 라이브러리를 이용해서 외부 서버랑 통신을 주고 받을 수 있다. 절차도 비슷한데

 

1. 먼저 통신할 주소를 URL 클래스로 생성하고

2. URLSession 을 만든 다음 

3. URLSession에 아까 만든 주소로 task를 할당하고 응답시 처리할 코드를 입력하고

4. task를 실행한다.

 

func performRequest(_ urlString: String) {
    // 1. Create a URL
    
    if let url = URL(string: urlString) {
        // 2. Create a URL session
        
        let session = URLSession(configuration: .default)
        
        // 3. Give the session a task
        
        let task = session.dataTask(with: url) { (data, response, error) in
            if  error != nil {
                self.delegate?.didFailWithError(error!)
                return
            }
            
            if let safeData = data {
                if let weather = self.parseJSON(safeData) {
                    self.delegate?.didUpdateWeather(self, weather)
                }
            }
        }
        
        // 4. Start the task
        task.resume()
    }
}
728x90

'개발 > iOS' 카테고리의 다른 글

UserDefaults  (0) 2020.12.05
IQKeyboardManager  (0) 2020.11.30
URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30
tableview  (0) 2020.11.24
codable  (0) 2020.11.23

Pod

개발/iOS 2020. 11. 30. 10:32 Posted by 아는 개발자

안드로이드에서 build.gradle 파일을 수정해 파이어베이스나 외부 깃허브 라이브러리를 다운 받을 수 있었던 것처럼 Xcode에서는 cocoapod과 swiftpackage 라는 툴로 이런 기능을 제공하고 있는데 . swiftpackage는 비교적 최근에 애플에서 만들었고 사용하기도 간편하지만 보편화되지 않아서 아직까지는 cocoapod을 상요하는 추세라고 한다. 이번 포스트에서는 cocoapod을 사용해서 외부 라이브러리를 임포트 하는 방법을 다뤄보려고 한다.

 

1. cocoapod 설치하기 

 

맥북에 cocoapod이 설치되지 않았다면 아래 명령어로 터미널을 켜서 cocoapod을 먼저 설치한다.

sudo gem install cocoapods
pod setup --verbose

 

2. 프로젝트에 pod 초기화하기 

 

개발중인 Xcode 프로젝트의 최상단 위치에서 아래 명령어를 수행한다. 최상단 위치는 *.xcodeproj 이런 파일이 있는 곳을 말한다.

pod init // 프로젝트 최상위 위치에서

 

실행하고나면 Podfile 이라는 루비 언어로 작성된 파일이 생긴다. 

 

platform :ios, '13.0'

target 'Flash Chat iOS13' do
  use_frameworks!

  # Pods for Flash Chat iOS13
end

 

3. 추가하고 싶은 라이브러리 넣기 

 

프로젝트에서 사용하려는 Third party 라이브러리 스크립트를 Podfile에 추가한다. 스크립트는 사용하려는 프로젝트 라이브러리의 github에서 확인 할 수 있다.

 

platform :ios, '13.0'

target 'Flash Chat iOS13' do
  use_frameworks!

  # Pods for Flash Chat iOS13
  pod 'Firebase/Auth'
  pod 'Firebase/Core'
  pod 'Firebase/Firestore'
  pod 'IQKeyboardManagerSwift'

end

 

4. 설치 스크립트 실행

 

라이브러리를 추가했다면 아래 코드를 실행해서 파일을 받아온다. 

 

pod install

 

5. 새로운 프로젝트 실행 

 

4번의 작업이 끝나면 아래 그림처럼 xcworkdspace 라는 확장자를 가진 파일이 만들어진다. 이 파일을 실행해서 프로젝트를 다시 실행한다. 프로젝트에 Pod을 설정하는 작업이기 때문에 처음 Pod을 초기화 할 때만 해주면 된다. 

 

728x90

'개발 > iOS' 카테고리의 다른 글

IQKeyboardManager  (0) 2020.11.30
URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30
tableview  (0) 2020.11.24
codable  (0) 2020.11.23
extension  (0) 2020.11.23

tableview

개발/iOS 2020. 11. 24. 13:37 Posted by 아는 개발자

안드로이드의 listview, recyclerview 처럼 ios에서도 여러 개의 동일한 형태의 아이템을 리스트 형태로 보여주는 UI 라이브러리가 있는데 바로 tableview 다. ios의 tableview는 크게 여러 개의 아이템을 바인딩하는 tableview와 각 table 안에 item을 그리는 cell로 이뤄져 있는데 안드로이드의 recyclerview와 viewholder 간의 관계와 동일하게 보면 될 것 같다. 이번 포스트에서는 view controller에서 가지고 있는 데이터를 tableview를 이용해서 리스트의 형태로 보여주는 간단한 예제를 다뤄보려고한다. 

 

1. cell 생성 

 

테이블에서 보여줄 아이템의 UI를 스토리보드의 형태로 그리는 작업이다. New File -> Cocoa Touch Class -> Subclass Of UITableViewCell 선택, Also create XIB file 선택 으로 아이템의 UI를 직접 그릴 수 있다. 스토리보드에서 했던것 처럼 디자인 하고, Cell의 identifier 값을 입력해둔다.

 

cell도 storyboard처럼 assitant를 사용해서 편집 할 수 있다.

 

2. tableview 초기화 

 

선작업으로 스토리보드에 추가한 TableView를 ViewController에 바인딩하고 리스트에 보여줄 아이템을 messages 변수 내에 담았다.

class ChatViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var messageTextfield: UITextField!
    
    var messages: [Message] = [
        Message(sender: "1@2.com", body: "Hey"),
        Message(sender: "a@b.com", body: "Hello!"),
        Message(sender: "1@2.com", body: "What's up")
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = K.appName
        tableView.delegate = self
        navigationItem.hidesBackButton = true
        
        tableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "ReusableCell")
    }
}

extension ChatViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // click listener
        print(indexPath.row)
    }
}

ViewController 내에서 tableview의 delegate과 datasource를 초기화해준다. tableView의 변수 delegate는 tableview 내의 item을 클릭 할 때 이벤트를 받기 위한 콜백이고 dataSource는 ViewController에서 갖고 있는 아이템을 바인딩하기 위함이다. 

 

그 아래 tableView.register() 함수를 호출하는데 이것은 tableview에서 사용할 cell을 등록하는 작업이다. nibName으로 커스텀 디자인한 cell을 선택할 수 있는데 앞서 디자인한 파일인 "MessageCell"을 선택하고 forCellReuseIdentifier로는 앞서 정의한 "ReusableCell"을 둔다. 

 

3. data 바인딩

 

 extension ChatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messages.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! MessageCell
        
        cell.label.text = messages[indexPath.row].body
        return cell 
    }
}

 

tableView.dataSource = self 을 구현하는 부분이며 실제로 ViewController에서 갖고 있는 메시지에 넣는 작업이다. 첫번째 함수에서는 item으로 표시할 메시지의 개수를 리턴하고 두번째 함수에서는 tableview에서 만든 cell에 데이터를 등록한다. 앞서 cell의 identifier을 ReusableCell으로 지정했으므로 withIdentifier로 동일한 값을 넣어서 받을 수 있는 메시지 셀을 받고 MessageCell로 타입캐스팅해서 클래스에 값을 입력하는 용도로 쓸 수 있다.

 

4. 구현 결과 

 

728x90

'개발 > iOS' 카테고리의 다른 글

URLSession  (0) 2020.11.30
Pod  (0) 2020.11.30
tableview  (0) 2020.11.24
codable  (0) 2020.11.23
extension  (0) 2020.11.23
closure  (0) 2020.11.23

codable

개발/iOS 2020. 11. 23. 15:49 Posted by 아는 개발자

codable은 swift4에서 추가된 프로토콜로 JSON 처리를 손쉽게 해준다. 예로 서버로부터 이런 json 결과물을 받으면 

 

{
  "coord": {
    "lon": -0.13,
    "lat": 51.51
  },
  "weather": [
    {
      "id": 721,
      "main": "Haze",
      "description": "haze",
      "icon": "50n"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 4.91,
    "feels_like": 1.93,
    "temp_min": 4.44,
    "temp_max": 6,
    "pressure": 1028,
    "humidity": 87
  },
  "visibility": 2800,
  "wind": {
    "speed": 2.1,
    "deg": 230
  },
  "clouds": {
    "all": 71
  },
  "dt": 1606106846,
  "sys": {
    "type": 1,
    "id": 1414,
    "country": "GB",
    "sunrise": 1606116755,
    "sunset": 1606147310
  },
  "timezone": 0,
  "id": 2643743,
  "name": "London",
  "cod": 200
}

 

Codable과 Struct을 조합해서 필요한 값들을 추출해줄 수 있다. JSON 값의 key와 데이터 타입만 일치하면 받아오는데는 문제 없다.

 

struct WeatherData: Codable {
    let name: String
    let main: Main
    let weather: [Weather]
}

struct Main: Codable {
    let temp: Double
}

struct Weather: Codable {
    let id: Int
    let description: String
}

////
func parseJSON(_ weatherData: Data) -> WeatherModel? {
    let decoder = JSONDecoder()
    
    do {
        let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
        
        let id = decodedData.weather[0].id
        let temp = decodedData.main.temp
        let name = decodedData.name
        let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
        return weather
    } catch {
        delegate?.didFailWithError(error)
        return nil
    }
}
728x90

'개발 > iOS' 카테고리의 다른 글

Pod  (0) 2020.11.30
tableview  (0) 2020.11.24
codable  (0) 2020.11.23
extension  (0) 2020.11.23
closure  (0) 2020.11.23
segue  (0) 2020.11.23
TAG Codable, ios, Swift

extension

개발/iOS 2020. 11. 23. 15:39 Posted by 아는 개발자

swift의 extension 은 클래스나 프로토콜에 새로운 함수를 추가해주는 기능을 제공한다. 일반적인 경우 클래스에 새로운 함수를 추가할 때 클래스를 수정하면 되지만 import 해서 사용중인 클래스의 경우에는 수정 할 수가 없다. 클래스는 수정 할 수 없으나 범용적으로 사용하는 함수의 경우에는 extension을 사용하면된다. 

 

예를들어 아래 코드처럼 둥근 빨간 버튼을 만드는 경우 이렇게 구현을 하면 가능하긴 하나 매번 이런식으로 만들어야되면 코드의 가독성이 떨어진다. 

 

이런 경우 extension을 이용해 UIButton 클래스에 새로운 함수, makeCircular를 만들 수 있다. 이러면 앞서 둥근 버튼을 만들기 위해 실행한 코드를 하나의 함수에 넣고 호출하는 식으로 해결 할 수 있다. UIButton 클래스를 수정 할 수는 없지만 새로운 함수를 추가함으로써 가능한 방식이다.

 

 

extension은 protocol에 적용할 때 유용한데 이때는 함수의 기본 body를 만들어줘서 상속받을 때 기본 body가 있는 함수들은 따로 구현하지 않아도 된다는 장점이 있다.  아래 코드를 보면 WeatherManagerDelegate라는 프로토콜은 두개의 함수가 있는데 그 아래 extension에서 didFailWithError 함수에 대해서 body를 넣어뒀다. 

 

protocol WeatherManagerDelegate {
    func didUpdateWeather(_ weatherManager: WeatherManager, _ weather: WeatherModel)
    
    func didFailWithError(_ error: Error)
}

extension WeatherManagerDelegate {
    func didFailWithError(_ error: Error) { }
}

 

이렇게 기본 body를 세팅해주면 WeatherManagerDelegate를 받는 쪽에서는 didUpdateWeather 함수만 구현하면 되고, didFailWithError는 구현하지 않아도 된다. 반드시 필요한 함수만 구현토록해 가독성을 좋아지게 하는 방법이다.

728x90

'개발 > iOS' 카테고리의 다른 글

tableview  (0) 2020.11.24
codable  (0) 2020.11.23
extension  (0) 2020.11.23
closure  (0) 2020.11.23
segue  (0) 2020.11.23
protocol  (0) 2020.11.21

segue

개발/iOS 2020. 11. 23. 12:24 Posted by 아는 개발자

segue(세그웨이)는 swift에선 ViewController 간의 전환하는 용도로 사용하는 라이브러리인데 ViewController가 하나의 화면을 담당하므로 화면 전환을 위한 라이브러리라고 봐도 될 것 같다. segue 가 포르투갈어로 팔로우라는 뜻을 가지고 있으니 단어의 의미와 함께 용도를 기억하면 좋을 것 같다.

 

세그웨이는 뷰 컨트롤러간의 연결을 통해서 만들수 있다. 스토리보드에서 작업중인 두개의 뷰컨트롤러중 화면전환이 시작되는 곳에서 도착지점까지 컨트롤을 눌러서 쭉 끌어준다. 실제 코드에 바인딩 해줄때 처럼 말이다. 

 

xcode는 왜이렇게 드래그를 좋아하는 걸까

 

옮기고 나면 위 그림의 파란색 박스로 표시한 새로운 아이템이 생긴다. 클릭하면 오른쪽 상단에서 세부 속성을 정의해 줄 수 있는데 identifier와 종류를 선택할 수 있다. identifier는 화면 전환 이벤트의 id와 같은 개념이고 kind는 새로운 view controller를 어떻게 띄워줄지를 선택 할 수 있다. 선호하는 방식대로 띄워주면 된다.

 

 

스토리보드에서 화면 전환에 대해서 정의한 후 ViewController에선 앞서 정의한 액션을 호출하는 역할을 한다. 아래 코드를 보면 calculatePressed 라고 스토리보드에 바인딩된 함수에서 performSegue 함수를 호출한다. 이때 withIdentifier에 들어가는 인자를 보면 아까 스토리보드에서 정의한 identifier와 동일한 값이 들어가있는걸 볼 수 있다. identifier 값을 확인해서 어떤 세그웨이를 사용할 지 선택하는 것이다. 

 

    @IBAction func calculatePressed(_ sender: UIButton) {
        print(heightSlider.value)
        print(weightSlider.value)
        
        let height = heightSlider.value
        let weight = weightSlider.value
        
        calculatorBrain.updateBmi(height, weight)

        self.performSegue(withIdentifier: "goToResult", sender: self)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "goToResult" {
            let destinationVC = segue.destination as! ResultViewController
            
            destinationVC.bmiValue = calculatorBrain.bmi
        }
    }

 

performSegue 함수를 호출하면 아래 오버라이딩된 prepare 함수가 호출되는데 이때 어떤 세그웨이가 호출됐는지 인자를 확인 할 수 있고 새로 생성된 ViewController 함수를 볼 수 있다. 위 코드를 보면 세그웨이의 도착점을 ResultViewController 클래스로 타입 캐스팅 한 것을 볼 수 있다. 그리고 새로운 ViewController에 전달하고 싶은 bmiValue를 입력하고 있는 것도 볼 수 있다.

728x90

'개발 > iOS' 카테고리의 다른 글

codable  (0) 2020.11.23
extension  (0) 2020.11.23
closure  (0) 2020.11.23
segue  (0) 2020.11.23
protocol  (0) 2020.11.21
Delegate  (0) 2020.11.21

protocol

개발/iOS 2020. 11. 21. 16:19 Posted by 아는 개발자

protocol은 네트워크나 의전에서 주로 쓰이는 용어인데 swift에서는 네트워크와 의전에서 사용되는 의미랑은 조금 다른 차원인 것 같고 차라리 자바나 코틀린에서 사용하는 interface가 좀더 와닿는 느낌이며 실제로도 비슷한 역할을 한다(자바 개발자를 너무 오래해서 그런가). 이 포스트에서는 객체 지향적 관점에서 protocol을 왜 사용해야하는지를 설명하기 보다는 어떻게 쓰는지를 중점적으로 설명하고자 한다.

 

protocol은 delegate 패턴을 쓸 때 사용하는 타입이다. delegate 패턴을 사용하는 예제 코드를 통해 protocol을 살펴보자.

 

protocol AdvancedLifeSupport {
    func performCPR()
}

class EmergencyCallHandler {
    var delegate: AdvancedLifeSupport?
    
    func assessSituation() {
        print("Can you tell me what happened?")
    }
    
    func medicalEmergency() {
        delegate?.performCPR()
    }
}

 

EmegencyCallHandler는 위급 상황을 평가하는 함수(assessSituation) 와 처리하는 함수(medicalEmergency)를 갖고 있는데 처리하는 작업은 다른 클래스에서 작업을 위임하고 있다. 위임할 때 호출하는 변수인 delegate는 AdvancedLifeSupport 라는 프로토콜 타입의 클래스로 선언돼있다. 

 

AdvanceLifeSupport protocol을 보면 performCPR() 함수만 있고 내부의 body는 구현되지 않았다. 이 함수는 이 명령을 위임 받을 클래스에서 반드시 구현해야 하는 내용이다.

 

class Doctor: AdvancedLifeSupport {
    func performCPR() {
        print("The doctor does chest compressions, 30 per second.")
    }
    
    init (handler: EmergencyCallHandler) {
        handler.delegate = self
    }
}

let handler = EmergencyCallHandler()
let doctor = Doctor(handler: handler)

handler.assessSituation()
handler.medicalEmergency()

 

Doctor 클래스에서는 AdvancedLifeSupport protocol을 구현하고 있다. body만 빠져 있던 performCPR 함수에 Doctor 클래스에서 처리하고 있다는 내용이 담긴 로그를 남기고 있다. 이 함수를 구현하지 않으면 컴파일 에러가 뜬다. 이 함수를 위임 받았으니, 해당하는 작업을 처리하라는 뜻이다.

 

아래 클래스에서 EmergencyHandler와 Doctor 클래스를 각각 초기화하고 Handler 에서 함수를 호출하면 다음과 같은 콘솔이 뜨는 것을 볼 수 있다. EmergencyHandler에서 작업을 위임 받은 Doctor 클래스에서 선언한 performCPR 함수가 호출되고 있다.

 

 

728x90

'개발 > iOS' 카테고리의 다른 글

codable  (0) 2020.11.23
extension  (0) 2020.11.23
closure  (0) 2020.11.23
segue  (0) 2020.11.23
protocol  (0) 2020.11.21
Delegate  (0) 2020.11.21
TAG ios, Protocol

Delegate

개발/iOS 2020. 11. 21. 16:02 Posted by 아는 개발자

swift 언어에서 사용하는 delegate 패턴은 용어에도 담긴 의미 처럼 특정한 작업을 다른 클래스에 위임 할 때 사용하는 디자인 패턴이다. 프로그래밍을 처음 경험하는 사람들은 어렵게 느낄 수 있으나 다른 언어를 먼저 경험해본 사람들한테는 콜백과 비슷한 사용 용도라고 될 것 같다. UI 클래스에서 주로 사용하는데 UITextField 클래스에서 사용 예시를 한번 확인해보자.

 

 

위 그림의 상단에 사용자로부터 텍스트를 받을 수 있도록 UITextField를 만들어뒀다. 시뮬레이터에서 유저가 이 영역을 클릭하면 자동으로 키보드가 올라오게 되는데 UITextField만 추가하고 아무런 추가 작업이 없었다면 엔터에 해당하는 이동 버튼을 눌러도 아무런 응답이 없다. 엔터 명령에 대해서 어떤 클래스도 이 작업을 위임 받지 않았기 때문이다.

 

iOS 상에선 특정 TextField 내에서 엔터 명령은 UITextField 내부적으로 처리하고 있다. 유저가 엔터를 여러번 클릭할 수록 UITextField 내부에서는 엔터 명령을 받게 된다. 개발자가 할 일은 UITextField의 엔터 작업을 위임 받을 클래스를 선언하고 그에 해당하는 명령을 처리하는 것이다. 다양한 방법으로 이 작업을 할 수 있는데 일반적으로 UITextField가 있는 ViewController에서 이 작업을 위임해준다

 

class WeatherViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var conditionImageView: UIImageView!
    @IBOutlet weak var temperatureLabel: UILabel!
    @IBOutlet weak var cityLabel: UILabel!
    @IBOutlet weak var searchTextField: UITextField!
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        searchTextField.endEditing(true)
        print(searchTextField.text!)
        
        return true
    }    

 

위의 코드를 보면 ViewController 클래스에서 UITextFieldDelegate 프로토콜을 받고 있고 이 프로토콜에 있는 textFieldShouldReturn 함수를 구현해둔 것을 볼 수 있다. 이 함수는 해당 UITextField의 키보드에서 엔터 명령이 있는 경우에 불린다. 위임 받은 클래스에선 편집 작업을 종료하고 현재 입력된 값을 출력하는 작업을 넣었다.

 

public protocol UITextFieldDelegate : NSObjectProtocol {

    
    @available(iOS 2.0, *)
    optional func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool

    @available(iOS 2.0, *)
    optional func textFieldDidBeginEditing(_ textField: UITextField)

    @available(iOS 2.0, *)
    optional func textFieldShouldEndEditing(_ textField: UITextField) -> Bool

    @available(iOS 2.0, *)
    optional func textFieldDidEndEditing(_ textField: UITextField)

 

UITextField에선 다른 작업에 대해서도 위임 받을 수 있는데 입력을 시작할 때 호출되는 textFieldDidBeginEditing 과 입력이 끝날 때 호출되는 textFieldDidEndEditing 같은 것들이 있다. 실제 코드로 따라가보면 더 많은 함수들이 있으니 구현할 때 참고해보면 좋을 것 같다.

 

이렇게 작업을 위임하는 delegate 패턴은 protocol 이라는 것을 사용해서 구현한다. 자바의 interface와 엇비슷하고 코틀린의 interface와는 아주 비슷한데 이에 대한 내용은 다음 포스트에 다루려고 한다.

728x90

'개발 > iOS' 카테고리의 다른 글

codable  (0) 2020.11.23
extension  (0) 2020.11.23
closure  (0) 2020.11.23
segue  (0) 2020.11.23
protocol  (0) 2020.11.21
Delegate  (0) 2020.11.21