Goalist Developers Blog

AWS RDSにJDBCでSSL接続する

こんにちは。ゴーリスト開発の飯尾です。
AWS RDS for MySQLに、SSL証明書を使ってJDBC接続する時に行ったことをまとめました。

前提

  • RDSのセキュリティグループで自IPからのアクセスを許可しておく
  • 2017年10月の試行メモ

手順

1. 証明書をゲットだぜ

$ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

2. 証明書をjksファイルに変換する

YOUR_JKS_PASSWORDのところはお好みで変えてください。

Macなのでこう

$ split -p "-----BEGIN CERTIFICATE-----" rds-combined-ca-bundle.pem individual-
$ find . -iname 'individual*' -exec keytool -import -file {} -alias {} -storepass YOUR_JKS_PASSWORD -keystore amazon.jks \;

Linuxならcsplitで分割して同様にひとつのjksファイルにまとめる。

このへんを見ました。
Using Java to establish a secure connection to MySQL Amazon RDS (SSL/TLS) - Stack Overflow

3. 作成したjksファイルを任意のフォルダに移動

# mkdir /opt/jks
# mv amazon.jks /opt/jks

4. 接続URLに各パラメータを追加する

  • useSSL=true
  • requireSSL=true
  • verifyServerCertificate=true
  • trustCertificateKeyStoreUrl=file:///opt/jks/amazon.jks # 作成したjksファイルのパス
  • trustCertificateKeyStoreType=JKS
  • trustCertificateKeyStorePassword=YOUR_JKS_PASSWORD # jksファイル作成時に設定したパスワード

こんなかんじになりました

jdbc:mysql://YOUR_SERVER/YOUR_DB?characterEncoding=UTF8&useSSL=true&requireSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:///opt/jks/amazon.jks&trustCertificateKeyStoreType=JKS&trustCertificateKeyStorePassword=YOUR_JKS_PASSWORD

5. あとはいつものとおりDriverManager.getConnection

参考URL

qiita.com

blog.cles.jp

stackoverflow.com

MySQL :: MySQL Connector/J 5.1 Developer Guide :: 5.5 Connecting Securely Using SSL

JKS 形式のキーストアの作成 (SSL をサポートする Java CAPS の構成)

EC2上のTomcatからRDSにSSL接続する方法 | infoScoop開発者ブログ

ひとこと

真面目さがアフレデル〜

【GoogleMapsAPI】Javaで住所から緯度経度を取得する

こんにちは。ゴーリスト開発の飯尾です。

住所から緯度経度を割り出そ〜というときにはどうしたものか。

国交省のデータを使うという手もありますが
田舎は詳細データなくてちょっと精度がこころもとないし、
表記ぶれにも弱くて使いづらいところがあります。

そこで天下のGoogle御大よ
GoogleMapsAPIのジオコーダーを使うとかんたんに詳細な緯度経度を取得できます。

そんなわけでJavaで住所から緯度経度を取得する方法のメモです。

手順

  1. APIキーを取得
  2. GoogleMapsAPIのJavaクライアントライブラリを入れる
  3. つかう

説明

1. APIキーを取得

以下リンクからはじまり
キーの取得、認証  |  Google Maps Geocoding API  |  Google Developers

「キーを取得する」ボタンをポチッとな
f:id:y-iio:20171012184920p:plain

てきとうなプロジェクト名で登録
f:id:y-iio:20171012185007p:plain

取得完了
f:id:y-iio:20171012185113p:plain

やったね

2. GoogleMapsAPIのJavaクライアントライブラリを入れる

公式がライブラリ出しているので遠慮なく使わしてもらう

Google マップ ウェブサービスのクライアント ライブラリ  |  Google マップ ウェブサービス API  |  Google Developers

github.com

GradleプロジェクトなんでMavenからひっぱってきました

build.gradle

dependencies {
    compile 'com.google.maps:google-maps-services:0.2.2'
    compile 'org.slf4j:slf4j-nop:1.7.25'
}

3. つかう

雑だけどこんなんでいけるんじゃないか

public class Geocoder {

    private static GeoApiContext context = new GeoApiContext.Builder()
            .apiKey("YOUR_API_KEY") // さっき取得したAPIキー
            .build();

    public static void main(String[] args) {
        GeocodingResult[] resluts = getResults("東京都千代田区神田須田町1-18");
        if (results != null && results.length > 0) {
            LatLng latLng = results[0].geometry.location; // とりあえず一番上のデータを使う
            System.out.println("緯度 : " + latLng.lat);
            System.out.println("経度 : " + latLng.lng);
        }
    }

    public static GeocodingResult[] getResults(String address) throws ApiException, InterruptedException, IOException {
        GeocodingApiRequest req = GeocodingApi.newRequest(context)
                .address(address)
                // .components(ComponentFilter.country("JP"))
                .language("ja");

        try {
            GeocodingResult[] results = req.await();
            if (results == null || results.length == 0) {
                // ZERO_RESULTSはresults.length==0の空配列がsuccessful扱いで返ってくるっぽい
                System.out.println("zero results.");
            }
            return results;
        } catch (ApiException e) {
            // ZERO_RESULTS以外のApiExceptionはこっちで
            System.out.println("geocode failed.");
            System.out.println(e);
            return null;
        } catch (Exception e) {
            System.out.println("error.");
            System.out.println(e);
            return null;
        }
    }

}

所感

きになったところ

その1

ステータスコードZERO_RESULTSのときは
ApiExceptionにひっかからないで空の配列が返ってくるっぽかった

その2

GeocodingApiRequest でコンポーネントフィルタかけてリザルトを国内のみに限定したら
.components(ComponentFilter.country("JP"))のところ)
ヘンテコな住所送った時にZERO_RESULTSにならずに
「日本」のジオコード結果が返ってきてしまって不便だったのでコメントアウトしている…

よいところ

その1

リザルトオブジェクトとか用意してくれてるのがうれし〜

その2

プレミアムプランのときの認証が楽
リクエストURL署名までしてくれるのがうれし〜

一言

快適なジオコーディングでQOLをあげていく( ◜◡^)

おわり

グラデーションをコードで書いてみた@Swift4

こんちは。渡部です。

現在社内アプリを(勝手に)作っています。
が、徐々にモチベーションが下がってきたので
気分が乗るようにデザイナーに画面作ってくれ!と依頼しました。

そして上がってきた画面がこれ
f:id:watabe1028:20171010164511p:plain

あらオシャレ。
女性のデザイナーなのですがブッチャーあたりを入れてくるのはさすがです。
きっとみんな知りません。
そんな方はこちら(閲覧注意)。
 
 
イマドキっぽいデザインで良いのですが、
機能が少々追加されてますね。。。
 
 
それにデザイナーはみんな忙しそうです。
あと7画面、複数サイズ、素材の切り出しとか依頼したら
ヒールホールドで足を破壊されそうです。
 
 
仕方ない、自分でやるか。と思いSketchを立ち上げましたが
普通にやっても面白くありません。というかSketchが使いこなせません。
 
そうだ!京都に行こうコーディングしよう!
だって一応コーダーですし。おすし。
 
ということでこんな感じでやりました。
(前置き長ぇ)
 

 

白黒でグラデーションを書いてみる

適当にプロジェクトを用意してviewDidLoad内にこんな感じでコードを書きます。

override func viewDidLoad() {
    super.viewDidLoad()
        
    // 白黒半々になる
    let startColor = UIColor(white: 1, alpha: 0).cgColor
    let endColor = UIColor(white: 0, alpha: 1).cgColor

    let layer = CAGradientLayer()
    layer.colors = [startColor, endColor]
    layer.frame = view.bounds

    view.layer.addSublayer(layer)
}

実行するとこんな感じです。
f:id:watabe1028:20171010171027p:plain

シンプルですね。

 

それっぽい色でやってみる

次にもらったデザインの色でやってみます。
同じく以下のようにコードを書きます。

override func viewDidLoad() {
    super.viewDidLoad()
    // もらったデザインの色を指定
    // 02ADB0 ~ 00CDAC
    let gradientLayer:CAGradientLayer = CAGradientLayer()
    let startColor = UIColor(red: 2/255.0, green: 173/255.0, blue: 176/255.0, alpha: 1.0).cgColor
    let endColor = UIColor(red: 0/255.0, green: 205/255.0, blue: 172/255.0, alpha: 1.0).cgColor

    // viewの左上から右下へ
    gradientLayer.colors = [startColor, endColor]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
    gradientLayer.frame = view.bounds

    view.layer.addSublayer(gradientLayer)
}

実行すると・・・
f:id:watabe1028:20171010171058p:plain

それっぽくなりました。
値の調整をすれば良い感じになりそうです。

 

昔のインスタのログイン画面も
f:id:watabe1028:20171010171203p:plain

こんな感じでやると

override func viewDidLoad() {
    super.viewDidLoad()
    // 昔のインスタ風
    //97347F ~ 873BA0
    let gradientLayer:CAGradientLayer = CAGradientLayer()
    let startColor = UIColor(red: 151/255.0, green: 52/255.0, blue: 127/255.0, alpha: 1.0).cgColor
    let endColor = UIColor(red: 135/255.0, green: 59/255.0, blue: 160/255.0, alpha: 1.0).cgColor

    // viewの左から右へ
    gradientLayer.colors = [startColor, endColor]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
    gradientLayer.frame = view.bounds

    view.layer.addSublayer(gradientLayer)
}

こう!
f:id:watabe1028:20171010171227p:plain

うぇい!
 
 

まとめ

コーダーは全部コードでやりたいですよね。
Storyboardができる以前のInterfaceBuilderは使い物になりませんでした使いこなせませんでした。
なのでボタンから何から全部コードで書いたのも懐かしいです。
今回はソースを見てもらえばわかる通り、
CAGradientLayerを使えば一発でグラデーションが描画できます、という記事です。

この書き方は単色のグラデーションなら可能ですが
複数色の場合は諦めて画像作ってもらいましょう!

Angular@4.1.2のCSSカプセル化について

こんにちは、ゴーリスト開発のモリツグです。

最近Angular向けのWijmo5をためしているのですが、Wijmo5の中の人はFlex愛好家だと思います。
全国に30人はいるであろうFlex愛好家が海外にも居たんだと勝手に思っています。
ブログ投稿のたびに書いてますが、Angularは悲しみに暮れるFlex愛好家にはピッタリなのでお勧めです。

今回はAngularのCSSカプセル化について書いてみたいと思います。
とはいえ、すでに先達の方々がこちらのリンクなどで詳しく説明されているので、styleUrlsまわりに絞って紹介したいと思います。
デモソースのplunkerはこちらです。

styleUrls

とにかくCSSをカプセル化したい!という場合はcssを用意してstyleUrlsで指定します。

@Component({
  selector: 'my-app',
  styleUrls : [
    `./my-app.css`,
  ],
  // encapsulation:ViewEncapsulation.Emulated
  template: `
    <div>demo</div>
    <child-comp></child-comp>
    <child-comp class="test-red"></child-comp>
  `
})

コメントアウトされているencapsulationについて説明します。
encapsulationに設定できる値はViewEncapsulation.None/Emulated/Nativeの3種類があります。
・None: カプセル化を行わない。
・Emulated: カプセル化を行う。
・Native: Shadow DOMを使う。人類にはまだちょっと早い。

encapsulationのデフォルト値はstyleUrlsの有無によって以下のように変わります。
・styleUrlsを定義しない場合: None
・styleUrlsを定義した場合: Emulated

したがって、先の例ではstyleUrlsが定義されているので、encapsulation:ViewEncapsulation.Emulatedをコメントアウトしてもしなくても同じです。

CSSカプセル化の実現方法

先ほどのコンポーネントがhtmlになった状態を見ると、以下のように<head>の中に<style>でmy-app.cssが埋め込まれており、_nghost-*や_ngcontent-*が各要素と.titleに付与されています。

<head>
 ...
  <style>
    @charset "UTF-8";
  
    .title[_ngcontent-c0] {
      color: red;
    }
  </style>
  ...
</head>
...
<my-app _nghost-c0="" ng-version="4.1.2">
  <div _ngcontent-c0="" class="title">demo</div>
  <child-comp _ngcontent-c0="" _nghost-c1="">
    <div _ngcontent-c1="">Child Component</div>
  </child-comp>
  <child-comp _ngcontent-c0="" class="test-red" _nghost-c1="">
    <div _ngcontent-c1="">Child Component</div>
  </child-comp>
</my-app>

Angularではこのように<style>を埋め込んでcssセレクタで_nghost-*や_ngcontent-*が一致する要素にだけcssが反映されてカプセル化を実現しているようです。

呼び出したコンポーネントの見た目を場合によって変えたい

今回の場合だと<child-comp class="test-red">のtest-redが効くようにしたい場合があると思います。
方法としては以下があります。
①:host-context疑似セレクタを使う。(今回は説明しません)
②:host疑似セレクタを使う。
③::ng-deep(以前は/deep/)セレクタを使う。(Shadow-Piercing自体があらゆるブラウザで無かったことにされるようなので使わないほうが良さそう、Angularのこの件についての公式説明はこちら)

①か②の方法が良さそうですが、今回は②の方法を紹介したいと思います。
なんか感覚的には呼び出し元のmy-app.cssで以下のように書けばcssが効きそうですが、効きません。

 /* my-app.css */
.test-red div {
  color: red;
}

正しくは<child-comp>のcssで以下のように指定します。

 /* child-comp.css */
:host(.test-red) div {
  color: red;
}

先ほど_nghost-*や_ngcontent-*という要素が挿入されていましたが、:hostは自分自身の_nghost-*に対応します。
詳しくはこちらのplunkerで確認してください。

まとめ

今回はAngularのCSSカプセル化についてザックリと紹介しました。
冒頭のQiitaのリンクが詳しくて参考になります。
本当は③の方法が一番綺麗な気がしますが、①か②の方が安全かなぁと思います。

Gradleを使ってAWS SDKのバージョン・依存関係を管理する

はじめに

こんにちは。ゴーリスト開発のイイオです。

ゴーリストでは大変にAWSを使っていて、400台以上のEC2インスタンスが蠢いてます。
(常時起動なのはそこまで多くありません)
そんなわけでAWS SDK for Javaもズンドコと使っています。

最近こころみにGradleでJavaプロジェクトを作ってSDKを使ったので
今回は「Gradleを使ってSDKのバージョン・依存関係を管理する手順」を書きたいと思います。

公式ドキュメントはこちら

docs.aws.amazon.com

準備

1. Gradleインストール

MacならHomebrewでイッパツ

brew update && brew install gradle

gradle.org

2. GradleでJavaプロジェクト作成

任意の場所でプロジェクト作るだけ

gradle init

build.gradle設定

1. SDKモジュールの依存関係管理のために Dependency management プラグインを導入

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "io.spring.gradle:dependency-management-plugin:1.0.0.RC2"
    }
}

apply plugin: "io.spring.dependency-management"

2. Dependency management で AWS SDK BOM を追加

SDKのバージョンはここで指定できる

dependencyManagement {
    imports {
        mavenBom 'com.amazonaws:aws-java-sdk-bom:1.10.77'
    }
}

Maven Repository: com.amazonaws » aws-java-sdk-bom

3. dependenciesセクションでプロジェクト上で利用するSDKモジュールを指定

dependencies {
    compile 'com.amazonaws:aws-java-sdk-s3'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

以上をまとめるとこんな感じに

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "io.spring.gradle:dependency-management-plugin:1.0.0.RC2"
    }
}
apply plugin: "io.spring.dependency-management"
dependencyManagement {
    imports {
        mavenBom 'com.amazonaws:aws-java-sdk-bom:1.10.77'
    }
}

apply plugin: 'java'

sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

repositories {
    jcenter()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.22'
    testCompile 'junit:junit:4.12'
    compile 'com.amazonaws:aws-java-sdk-s3'
}

これでOKワーイズンドコズンドコ

あとは

お好きなIDEで開発するのみ

第38章 Eclipse プラグイン

第39章 IDEAプラグイン

Gradle初心者ですんで何か間違っている部分あれば、ぜひに報告をいただきたいです。

Doma-GenでMySQLのテーブルCOMMENTを取得する(Doma2)

どうも。ゴーリストのJPです。

今回はDoma2でJavaソースを自動生成する際に、MySQLのテーブルCOMMENTが取れなくて困ったので、解決方法を書きたいと思います。

Doma2とは

JavaのORマッパーです。
DBを読み込み、EntityやDaoクラスのJavaソースの自動生成もしてくれます。
Welcome to Doma — Doma 2.0 ドキュメント

環境情報

  • MacOS Sierra 10.12.6
  • Java8
  • doma-gen 2.16.1
  • mysql 5.6.29

何が起きたのか

Entityクラスのテンプレートファイル(Free Marker)のJavadocにテーブルCOMMENTを使用します。
テーブルCOMMENTにはテーブルの論理名が登録されています。

  • テーブル名:sample_table
  • テーブルCOMMENT:サンプル

entity.ftl

/**
 * ${comment}テーブルエンティティ
 */
// @アノテーションは省略
public class <#if entityPrefix??>${entityPrefix}</#if>${simpleName} implements BaseTableEntity, EntityTrail {

実行結果

/**
 * テーブルエンティティ
 */
// @アノテーションは省略
public class SampleTable implements BaseTableEntity, EntityTrail {

テーブルCOMMENTが反映されていません。。。
本来なら「サンプルテーブルエンティティ」となってほしいです。

原因

MySQL用のjdbcDriverの不具合のようです。

MySQL Bugs: #65213: Connector/J does not retrieve the table comment in a InnoDB table

対策

DB接続時のuseInformationSchemaプロパティを「true」に指定すると不具合を回避できるようです。
具体的には、以下の手順で指定します。

GlobalFactoryを自作する

GlobalFactoryとはGenタスクで使用する各種クラスを生成するクラスです。

doma-gen/GlobalFactory.java at master · domaframework/doma-gen · GitHub

上記のGlobalFactoryを継承して、DataSourceクラスを生成するメソッドをOverrideします。
ここで、useInformationSchemaプロパティを「true」に指定します。

public class SampleGlobalFactory extends GlobalFactory {

  @Override
  public DataSource createDataSource(Driver driver, String user, String password, String url) {
    MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
    mysqlXaDataSource.setUseInformationSchema(true);
    mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    mysqlXaDataSource.setUrl(url + "?useUnicode=true&characterEncoding=UTF-8");
    mysqlXaDataSource.setUser(user);
    mysqlXaDataSource.setPassword(password);
    return mysqlXaDataSource;
  }

}

Genタスクのパラメータを追加する

Genタスクの書かれたbuild.gradleにglobalFactoryClassNameパラメータを追加します。

task gen << {
  // 省略
  ant.gen(
        url: 'jdbc: sample',
        // 省略
        globalFactoryClassName: 'path.to.SampleGlobalFactory'
  )
}

Genタスクで使用するGlobalFactorySampleGlobalFactoryに変更してくれます。
これにより、useInformationSchemaプロパティが「true」のDataSourceを使用してMySQLに接続してくれるようになります。

再実行

ちゃんと出ました。

/**
 * サンプルテーブルエンティティ
 */
// @アノテーションは省略
public class SampleTable implements BaseTableEntity, EntityTrail {

まとめ

意外と調べてもこの方法が出てこなかったので、書いてみました。
調べるまでもないことなのか、テーブルCOMMENTはGenタスクで使われないのか、調べる能力が足りないのか、、、
この記事が誰かの役に立つことを願っています。

ファイルを出力してアプリ内に保存してみた@Swift3

こんちは。渡部です。

f:id:watabe1028:20170915114242p:plain

現在社内アプリを(勝手に)作っています。
そこでファイル出力させようとしたんですが
Objective-CではよくやってたんですがSwiftになってからは初めてだったんで
まとめてみました。
今回は任意のタイミングでテキストファイルやPDFファイルを作成する方法です。
アプリ内に保存した後、そのファイルの確認方法もついでに載せときます。
 

 

準備

まずは適当なプロジェクトのViewControllerに弊社サービスを牛耳る(三回目)ふろぐんのimageViewを貼り付けます。
ついでにUILabelも。
 
f:id:watabe1028:20170914150407p:plain

準備はこれだけです。
このあとはコーディングです。

 
 

文字列をテキストファイルに出力する方法

適当な文字列をテキストファイルに出力します。
今回は「HRogよろしくね!」という文字列を「sample.text」というファイルに出力します。

override func viewDidLoad() {
    super.viewDidLoad()
        
    self.createTextFromString(aString: "HRogよろしくね!", saveToDocumentsWithFileName: "sample.text")
}

// textファイルの出力
func createTextFromString(aString: String, saveToDocumentsWithFileName fileName: String) {
        
    if let documentDirectoryFileURL = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last {
            
        // 書き込むファイルのパス
        let targetTextFilePath = documentDirectoryFileURL + "/" + fileName
            
        do {
            try aString.write(toFile: targetTextFilePath, atomically: true, encoding: String.Encoding.utf8)
        } catch let error as NSError {
            print("failed to write: \(error)")
        }
    }
}

パスの指定はいくつか方法がありますが今回はStringで指定しています。
基本的にはStringかURLで指定するのが一般的だと思います。多分。
 
 

表示中の画面をPDFファイルに出力する方法

現在表示中の画面をPDFファイルに出力します。
「現在表示中の画面==self.view」ということにします。
ファイル名は「view.pdf」です。

override func viewDidLoad() {
    super.viewDidLoad()
                
    // textファイルの出力
    self.createTextFromString(aString: "HRogよろしくね!", saveToDocumentsWithFileName: "sample.text")

    // PDFファイルの出力
    self.createPdfFromView(aView: self.view, saveToDocumentsWithFileName: "view.pdf")
}

// pdfファイルの出力
func createPdfFromView(aView: UIView, saveToDocumentsWithFileName fileName: String) {
        
    let pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil)
    UIGraphicsBeginPDFPage()
        
    guard let pdfContext = UIGraphicsGetCurrentContext() else { return }
        
    aView.layer.render(in: pdfContext)
    UIGraphicsEndPDFContext()
        
    if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
        let documentsFileName = documentDirectories + "/" + fileName
        pdfData.write(toFile: documentsFileName, atomically: true)
    }
}

テキストファイルとは違い
・StringではなくData
・UIGraphicsBeginPDFContextToData
を使います。

基本的にソースをコピペすればそのまま使えるように書いてるので
詳細な説明は面倒なのでしません。

このソースで実行するとアプリ起動時にファイルを作成し、
アプリ内に保存されます。  
 

アプリ内に保存したファイルを確認する方法

先ほど保存したアプリ内のファイルをMacで取り出して確認します。
想定した通りに動けば「Documents」直下に「sample.text」と「view.pdf」が保存されてるはずです。
それでは確認手順です。
今回は実機で試した場合です。

iPhoneをMacに繋いだ状態で
Xcodeの「Window」「Devices」を選択します。

f:id:watabe1028:20170914154617p:plain

すると「Devices」ウィンドウが開くので
左ペインの「DEVICES」で自分のiPhoneを選択します。
「Installed Apps」から実行したアプリを選択し、
下の設定ボタンを押下。

f:id:watabe1028:20170914154642p:plain

「Download Container」で適当な場所にファイルをダウンロードします。

f:id:watabe1028:20170914153602p:plain

ダウンロードしたファイルを右クリックし、
「パッケージの内容を表示」を選択します。

f:id:watabe1028:20170914153637p:plain

するとアプリ内のファイル格納ディレクトリがあるので
「Documents」まで行くと、、、

f:id:watabe1028:20170914154034p:plain

テキストファイル、ちゃんとできてる!

f:id:watabe1028:20170914153725p:plain

PDFもできてる!

f:id:watabe1028:20170914153742p:plain

うぇい
 
 

ちなみにアプリ内にファイルを保存できるディレクトリは3つあります。
各ディレクトリに意味があるので注意が必要です。
もちろんリジェクト対象です。
 
Documents
・ユーザが作成したデータを保存する場所
・ユーザがアクセスできる
・ゆえに見せても構わないファイルのみにすること
・iTunesでバックアップされる

Library
・ユーザのデータファイル以外を保存する場所
・ユーザに見せたくないファイルを保存する
・ユーザデータのファイル保存には使ってはいけない
・iTunesでバックアップされる

temp
・一時ファイルを書き込むための場所
・次に起動するときには消えてる
・アプリが動作していないとき、システムが全て消去することがある
・iTunesでバックアップされない

 
 

まとめ

基本的にユーザのデータはUserDefaultsに保存してますが
たまにファイルに書き出して保存したい時があります。
そんなときにファイルどうやって書き出すんだっけ?というのを防ぐために
自分のためだけに書きました。
うぇい!