higan96技術メモ

https://github.com/higan96

UITableViewで無限スクロール

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
  if indexPath.row == (items.count - 1){
    items += nextItems
    tableView.reloadData()
  }
}

最後のcellに来たらnextItemsを追加しています。

たまにcellForRowAtIndexPathでこの処理をしている記事を見るけど、cellForRowAtIndexPathはあくまで初期化処理に徹するべきだと思うので、willDisplayCellで処理を記述しています。

実際の使用シーンではnextItemsの取得処理が入ると思います。

また、自分の場合、一番下にスクロールしたのが分かるように「ロード中」のようなテキストを表示するfooterを指定しています。その場合、固定表示したくないので、viewDidLoadで下のように指定してます。

tableView.tableFooteView = footer

linker command failed with exit code 1 (use -v to see invocation)の対処

file too small (length=0) file '/some/file/path' for architecture x86_64
linker command failed with exit code 1 (use -v to see invocation)

command + shift + k

これでだいたい解決する

多分ビルドが失敗したときに、変な風に(0byte)書き換わってるのが原因ぽい

OHHTTPStubとXCTestで、モックを使った非同期処理をテストする

通信をモックを使って、実際のサーバーサイドやWebAPIとの通信を行わずにテストする。

import UIKit
import XCTest

class SomeAPIClientTests: XCTestCase {
    let someAPIClient = SomeAPIClient()

    override func setUp() {
        super.setUp()
    }
    
    override func tearDown() {
        super.tearDown()
    }

    func testExample(){
        let obj = SomeObject()
        let stub:NSDictionary = ["id": 1]
        let expectation = self.expectationWithDescription("非同期通信は完了する")
        let stubDesc = OHHTTPStubs.stubRequestsPassingTest({ (request: NSURLRequest!) -> Bool in
            // 通信処理をモックを通して行なうか、の条件。trueを返すときに、モックを通す
            return true
            }, withStubResponse:( { (request: NSURLRequest!) -> OHHTTPStubsResponse in
                //レスポンス
                //失敗させたいときはステータスコードをいじるなどする
                return OHHTTPStubsResponse(JSONObject: stub, statusCode: 200, headers: ["Content-Type" : "text/json"])
            })
        )
        
        someAPIClient.post(obj, callback: {(postedObj, error) -> () in
            XCTAssertNil(error, "Error should be nil.")
            //通信が正常に行われたこと、失敗したことをテストしても意味がないので、実際にはそれぞれのケースで行われる処理をテストする。
            //expectation.fulfill()が呼ばれたことが、このテストの非同期処理が正常に行われたことを意味する。
            expectation.fulfill()
        })
        
        self.waitForExpectationsWithTimeout(5, handler: {(error) -> Void in
            //expectation.fulfill()が呼ばれたタイミングで実行される。
            //ここでOHHTTPStubsを消しておかないと、
            //他のテストの通信もこのテストの設定のモックが使用されてしまう。
            OHHTTPStubs.removeAllStubs()
            return
        })
    }
}

UIViewを45度傾かせる

吹き出しようの「ぺろっ」と出てている三角形を作りたかったので、正方形を45度傾かせて実現しました。 この辺の数学的知識が無いので、解説はできません。

let angle = CGFloat(M_PI * (45) / 180.0)
//miniBoxはUIViewのインスタンス
miniBox.transform = CGAffineTransformMakeRotation(angle)

CoreLocationで国、都道府県、市区町村の取得

import CoreLocation
 
let location:CLLocation = CLLocation(latitude: lat, longitude:lon)
geocoder.reverseGeocodeLocation(location, completionHandler:{(placeMarks, error) -> Void in
  if error == nil {
    if placeMarks.count > 0{
      let placemark: CLPlacemark = placeMarks[0] as CLPlacemark
      println(placemark.ISOcountryCode)     // JP
      println(placemark.administrativeArea)  // 都道府県
      println(placemark.subAdministrativeArea) // 群
      println(placemark.locality) // 市町村区
    }
  }else{
    println(error)
  }
})

コレクションはオプショナル型にしない

Swiftを始めてオプショナル型覚えたてで、オプショナル型使いたい病にかかってしまい、 メソッドの返り値のコレクションデータまでオプショナル型で返していました。

ですが、やっぱアンラップするのがめんどくさい。

そもそも、ArrayにもDictionaryにもisEmptyというメソッドが用意されているのだから、コレクションの中身がない場合を想定したコードを書くなら、オプショナル型を使わずにそれを使うべき。

なので、あるかないかわからないコレクションデータを扱いたい場合、空のコレクションを用意し、ない場合は空のコレクションを返したほうがいい

func getSomething() -> Array<Something> {
  //[Something]()
  items = Array<Something>()
  fetchSomething({(data, error) in
    if error == nil{
       if let unwrappedData = data {
          items.append(unwrappedData)
       }
    }else{
       // error
    }
    return items
  })
}

他にどんな書き方があるんだ、という話でもある 明らかに自分がアホだった