20210907のSwiftに関する記事は12件です。

UITextViewのtextに表示する文字列を改行して読みやすくする。

今回の内容 表示する文字列を改行して、読みやすくします。 今回、私が使った改行方法についてだけを投稿しています。 コードと簡単解説 緯度と経度を取得して、住所などに変換した値をUITextViewのtextに表示しています。 表示したい文字列などを,下記のコードのように"""で囲むと上の画像の様に改行します。 ~~~~~~~~~~省略~~~~~~~~~~ @IBOutlet weak var currentLocationTextView: UITextView! ~~~~~~~~~~省略~~~~~~~~~~ //使用者の現在地の緯度と経度を取得して、住所か建物の名前などに変換 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: (locations.first?.coordinate.latitude)!, longitude: (locations.first?.coordinate.longitude)!)) { placeMark, error in if error != nil{ return } if let resultPlaceMark = placeMark?.first{ if resultPlaceMark.administrativeArea != nil || resultPlaceMark.locality != nil{ self.currentLocationTextView.text = """ 緯度 [\((locations.first?.coordinate.latitude)!)] 経度 [\((locations.first?.coordinate.longitude)!)] 場所 [\(resultPlaceMark.administrativeArea!)] [\(resultPlaceMark.subLocality!)] [\(resultPlaceMark.name!)] """ }else{ self.currentLocationTextView.text = resultPlaceMark.name! } } } } 終わり 間違いの指摘、ご質問などありましたら、コメントまでお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NCMBのSwift SDKを使ったランキング機能に日付絞り込み機能を追加する

先日、NCMBのSwift SDKを使ってゲームのランキング機能を実装する - Qiitaという記事をアップしましたが、その後こんなツイートを拝見しました。 ランキングを時間軸で絞り込むことで、今月のランキングや先週のランキングなどを実現します。確かに、圧倒的な順位の人が君臨してしまうとやる気をなくしそうですが、週間ランキングなどがあれば順位変動が激しくて良さそうです。 ということで、Swift SDKを使ったランキング機能にも、過去30日と7日間でランキングを絞り込めるようにしてみました。この実装はNCMBのSwift SDKを使ってゲームのランキング機能を実装する - Qiitaを前提としています。また、コードはGitHub - NCMBMania/Swift_Ranking_Demo: Swift SDKを使って実装したランキング機能のデモですにアップしてあります。 変更点について まずEnumで時間のレンジを定義しています。 enum RankingRange { case All, Month, Week } そしてボタンを追加して、それぞれレンジを引数に送るようにします。 Button("過去30日", action: { getRanking(range: RankingRange.Month) }) Button("過去7日", action: { getRanking(range: RankingRange.Week) }) if rankings.count > 0 { List { ForEach(Array(rankings.enumerated()), id: \.offset) { i, ranking in Text(listString(index: i, ranking: ranking)) } } } func listString(index: Int, ranking: NCMBObject) -> String { let displayName: String = ranking["displayName"] ?? "" let score: Int = ranking["score"] ?? 0 return "\(index + 1)位 \(displayName)さん (\(score)点)" } ランキングの取得 ランキングの取得時には、指定されたレンジに応じて日時オブジェクトを作成しています。 func getRanking(range: RankingRange = .All) { var date: Date? let calendar = Calendar.current let today = Date() switch range { case .Month: date = calendar.date(byAdding: .day, value: -30, to: today) break case .Week: date = calendar.date(byAdding: .day, value: -7, to: today) break default: break } // 下に続く そして、日付が指定されている場合のみ greaterThanOrEqualTo を使って指定された日付以降のデータだけを対象とする絞り込みを行っています。 // ランキングデータ検索用のクエリオブジェクトを作成 var query = NCMBQuery.getQuery(className: "Ranking") // 並び順はスコアの高い順です query.order = ["-score"] // 100件のデータを取得します query.limit = 100 // 日付が指定されていれば、その日以降のデータに限定する if date != nil { query.where(field: "createDate", greaterThanOrEqualTo: date!) } // 検索実行 let results = query.find() switch results { case let .success(ary): // 検索成功したら、それをrankingsに適用します rankings = ary case .failure(_): break } } 表示時の注意 ランキングの内容が変わるので、 rankings.indice だとうまく反映されませんでした(最初に10件表示していると、次に2件に変わった時にも最初にi = 10になってしまう)。そこで、以下のようにForEachの内容を変えています。 // 元 List { ForEach(rankings.indices) { i in Text(listString(index: i)) } } // 変更後 List { ForEach(Array(rankings.enumerated()), id: \.offset) { i, ranking in Text(listString(index: i, ranking: ranking)) } } これに伴ってlistStringも多少変わっています。 // ランキングの順位、ゲームユーザ名、スコアを返します func listString(index: Int, ranking: NCMBObject) -> String { let displayName: String = ranking["displayName"] ?? "" let score: Int = ranking["score"] ?? 0 return "\(index + 1)位 \(displayName)さん (\(score)点)" } まとめ NCMBの以上、以下と言った検索のオペランドを使うことで、指定日以降のランキングを作ったり、先月だけのランキングを作ると言ったことも簡単に実現できます。ぜひSwiftを使ったゲーム開発に活かしてください。 ドキュメント : 開発者向けドキュメント | ニフクラ mobile backend
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swiftで競プロをやりたい初心者向けのまとめ(随時更新予定)

初心者向けswift競プロ備忘録 今回は競プロ初心者である私がswiftで競プロをする際に必ず使うような処理の仕方を備忘録としてまとめていきたいと思います。 入力編 標準入力をString型で使いたい時 let input = readLine()! 標準入力をInt型で使いたい時 let input = Int(readLine()!)! 標準入力がスペースによって区切られている時 let input = readLine()! let splited_input = input.split(separator: " ") let a = splited_input[0] let b = splited_input[1] //キャストする場合は let num_a = Int(a) //このように書くことができます。 このように書くと一つずつ定数として取得することができます。 またaやbをInt型にキャストすれば数字として扱うことも可能です。 標準入力が改行によって区切られている時 let input1 = readLine()! let input2 = readLine()! このように改行された行数分入力を繰り返すと取得することができます。 メソッド編 指定した文字列が含まれているかの判定 import Foundation let string:String = "Hello World" if string.contains("Hello") { print("Helloが含まれる") } //trueが返されるので"Helloが含まれる"がprintされる 指定した文字列を繰り返す let a = String(repeating: "あいうえお", count: 3) print(a) //あいうえおあいうえおあいうえお 指定した文字列を置換する import Foundation let before = "りんごが食べたいな" let after = before.replacingOccurrences(of: "りんご", with: "ぶどう") print(after) //ぶどうが食べたいな Int型配列の全要素の合計値を求める let array: [Int] = [1, 2, 3, 4, 5] let result: Int = array.reduce(0) { $0 + $1 } print(result) //15 reduce( )の中身が自由に決められる初期値です。 その後の$0や$1に関しては私も曖昧なので知りたい人はぜひ調べてみてください。 たまに使う小技編 改行せずにprintする print("ABC", terminator: "") print("DEF") //ABCDEF terminator:" "と書くとスペースを空けてprintすることができます。 絶対値を求める let a:Int = -123 print(abs(a)) //123 let b:Double = -4.56 print(fabs(b)) //4.56 Int型に対してはabsを使ってキャストしてください。 またDouble型やFloat型に対してはfabsを使ってキャストしてください。 平方根を求める let a:Double = 4.0 print(sqrt(a)) //2.0 let b:Double = 2.0 print(sqrt(b)) //1.41421356... Double型に対してsqrtでキャストをすると平方根を求めることができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】ComplitionHandlerを自作してみる

はじめに 最近は設計に意識してコードを書くように意識しています。MVCを勉強している中でModel内でAPI通信を行い情報を取得、画面への描画はController内で行うという流れを作りたくてComplitionHandlerを自作するに至りました。備忘録的に記しておきます。 解決したかったこと 下図③のレスポンスが返ってきてから④の描画を行うこと。 コード Model 関数の引数にクロージャを設定する。→通信が終わったところでcomplitionHandler()でクロージャを実行とする。 import Foundation final class PokemonAPIModel { private(set) var pokemonName: String? private(set) var pokemonImageData: Data? func getPokemonName(id: Int, complitionHandler: @escaping () -> Void) { guard let url = URL(string: "https://pokeapi.co/api/v2/pokemon/\(id)/") else { return } let task = URLSession.shared.dataTask(with: url) { data, response, error in if error != nil { print("error") } guard let data = data, response != nil else { print("data or response are nil") return } do { guard let name = try? JSONDecoder().decode(Pokemon.self, from: data).name else { return } guard let image = try? JSONDecoder().decode(Pokemon.self, from: data).sprites else { return } let url = URL(string: image.frontImage) self.pokemonImageData = try? Data(contentsOf: url!) self.pokemonName = name print(name) print("2") } complitionHandler() } task.resume() } } Controller Modelのインスタンスを生成し、メソッドを呼ぶ。complisionHandlerの部分に描画のコードを書く。 import UIKit final class ViewController: UIViewController { @IBOutlet private weak var imageView: UIImageView! @IBOutlet private weak var pokemonNameLabel: UILabel! @IBOutlet private weak var serchButton: UIButton! @IBOutlet private weak var pokemonNumberTextField: UITextField! private let model = PokemonAPIModel() private var searchPokemonId: Int? @IBAction private func didTapSearchButton(_ sender: UIButton) { guard let id = Int(pokemonNumberTextField.text ?? "") else { showAlert() return } model.getPokemonName(id: id) { DispatchQueue.main.async { [self] in imageView.image = UIImage(data: model.pokemonImageData!) pokemonNameLabel.text = model.pokemonName } } } private func showAlert() { let alert = UIAlertController(title: "エラー", message: "数字を入力してください", preferredStyle: .alert) let confirm = UIAlertAction(title: "OK", style: .default) alert.addAction(confirm) present(alert, animated: true, completion: nil) } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】isEmptyの挙動メモ

isEmptyを文字列や配列に使うことがあると思いますが、たまにごちゃっとなるのでメモします。 同じように「どうだったけ?」という方の参考になれば幸いです! 実行環境 Swift 5.4.2 nilがない場合 let case1 = "" print(case1.isEmpty) // true let case2 = " " print(case2.isEmpty) // false let case3 = [""] print(case3.isEmpty) // false let case4 = [" "] print(case4.isEmpty) // false let case5:[String] = [] print(case5.isEmpty) // true nilがある場合 let case1: String? = nil print(case1?.isEmpty) //warningが出ます // nil let case2: String? = nil print(case2!.isEmpty) // nilを強制アンラップするためクラッシュします let temp: String? = nil let case3 = [temp] print(case3.isEmpty) // false let temp: String? = nil let case4 = [temp, ""] print(case4.isEmpty) // false let temp1: String? = nil let temp2: String? = nil let case5 = [temp1, temp2] print(case5.isEmpty) // false
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】CollectionViewを重ねる方法

こんにちは。 今回は、Twittterでみかけるスペース機能みたいな感じの、アイコンが重なってる感じのやつを作りたいと思います。 いざ実装すればめちゃくちゃ簡単だったんですが、調べても全く出てこなかったので、今回書いてみることにしました。 ではいきましょう! では、CollectionViewを用意しましょう(当たり前) からの、次のコードを書いてください(インジケータの設定は任意です)↓ import UIKit class ViewController: UIViewController,UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout { @IBOutlet weak var collectionView: UICollectionView! let items = ["1","2","3","4","5","6"] override func viewDidLoad() { super.viewDidLoad() collectionView.delegate = self collectionView.dataSource = self       collectionView.showsHorizontalScrollIndicator = false//任意 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) let imageView = cell.contentView.viewWithTag(1) as? UIImageView imageView?.image = UIImage(named: items[indexPath.row]) imageView?.contentMode = .scaleAspectFill imageView?.layer.masksToBounds = true imageView?.layer.cornerRadius = (imageView?.frame.size.width)!/2 return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 80, height: 80) } } itemsの配列は、わかりやすいように番号で適当に決めてます。そして、適当に外部から取ってきた画像に1~6の番号をつけ、cellForItemAtの中でImageViewにして入れてます。 他は、画像を丸くしたりサイズを決めたりしてるだけの単純な処理です。 ここまでできたら、あとは次の処理を追加するだけで、重なってくれます。 import UIKit class ViewController: UIViewController,UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout { @IBOutlet weak var collectionView: UICollectionView! let items = ["1","2","3","4","5","6"] override func viewDidLoad() { super.viewDidLoad() collectionView.delegate = self collectionView.dataSource = self       collectionView.showsHorizontalScrollIndicator = false//任意 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) let imageView = cell.contentView.viewWithTag(1) as? UIImageView imageView?.image = UIImage(named: items[indexPath.row]) imageView?.contentMode = .scaleAspectFill imageView?.layer.masksToBounds = true imageView?.layer.cornerRadius = (imageView?.frame.size.width)!/2 return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 80, height: 80) } //これを追加    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return -22 } } 終わったら、こんな感じになってるかと。 最後に お疲れさまです。 仮にもっと見た目をよくしたいとかであれば、ボーダーを追加して、それに色をつけたりとかをやってみてください。 では!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIActivityViewControllerで動画をシェアする

Xcode Version 12.5.1 カメラロール内の動画をUIActivityViewControllerでシェアできるようにしました。 3日詰まりました。。 ImageはこんなかんじでactivityItemsにそのまま渡してやれば良いので、簡単です。 import SwiftUI import PhotosUI //忘れずに struct ImageShareSheet: UIViewControllerRepresentable { @State var image: UIImage func makeUIViewController(context: Context) -> UIActivityViewController { let text = "Image" let activityItems: [Any] = [image, text] let controller = UIActivityViewController( activityItems: activityItems, applicationActivities: nil) return controller } func updateUIViewController(_ vc: UIActivityViewController, context: Context) { } } 問題は動画です。 動画はURLで渡すのですが、そのまま渡すと「サンドボックス拡張機能の発行に失敗しました」というようなエラーが出ました。 私は元ファイルのURLを@StateでStringで受けとって使用していましたが、なんだろう、揮発するのかな?上手くいきませんでした。 正確に言うと、AirDropとMessageアプリでは送れるのに、メールやLINEで送れませんでした。(写真は送れたけど動画が送れず) 元になるPHAssetファイルのURLをstruct MovieShareSheet内で改めて取得し、fileManager.copyItemでTempディレクトリにコピーしてから、コピーしたファイルに対して操作を実行することで、うまく行くようになりました。やったー! struct MovieShareSheet: UIViewControllerRepresentable { @State var photo: PHAsset func makeURLandCopy() -> URL { let fileURL = photo.getURL(ofPhotoWith: photo) print("fileURL(元): \(String(describing: fileURL))") let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(fileURL!.lastPathComponent) print("temporaryFileURL(コピー先):\(temporaryFileURL)") copyToTmp(fileURL: fileURL!, temporaryFileURL: temporaryFileURL) return temporaryFileURL } func copyToTmp(fileURL: URL, temporaryFileURL: URL){ let fileManager = FileManager.default do{try fileManager.copyItem(at: fileURL, to: temporaryFileURL)} catch{} return } func makeUIViewController(context: Context) -> UIActivityViewController { let fileURL = makeURLandCopy() let activityItems: [Any] = [fileURL] let controller = UIActivityViewController( activityItems: activityItems, applicationActivities: nil) return controller } func updateUIViewController(_ vc: UIActivityViewController, context: Context) { } } extension PHAsset{ func getURL(ofPhotoWith mPhasset: PHAsset) -> URL?{ var url = URL(string:"test") let semaphore = DispatchSemaphore(value: 0) let options=PHVideoRequestOptions() options.version = .current options.isNetworkAccessAllowed = true PHImageManager.default().requestAVAsset(forVideo: mPhasset, options: options) {(asset:AVAsset?,audioMix, info:[AnyHashable:Any]?) -> Void in if let urlAsset = asset as? AVURLAsset{ let localURL = urlAsset.url as URL url = localURL print(url!) }else{ print("url asset nil") } do {semaphore.signal() } } if semaphore.wait(timeout: DispatchTime.distantFuture) == .success { print("url success") return url! }else{ print("url timeout") return url! } } } (Tmpファイルは自分で消さなくて大丈夫なのかな…?気になっています) お役に立てれば幸いです! もっと良い美しい方法があればぜひご教示ください! 参考 https://try2explore.com/questions/12413739 https://stackoverflow.com/questions/57798968/didfinishpickingmediawithinfo-returns-different-url-in-ios-13
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iTunesSearchAPIをSwiftで使って、一覧表示させてみた

初めに SwiftでiTunesSearchAPIを使って楽曲一覧アプリを作ってみたいと思います。 初心者にもわかりやすく、AutoLayoutの設定、デザインパターン、コードの可読性もしっかり守っているので、APIの入門記事としてはぴったりかなと。 まず完成形はこちら! UIの設計 このように配置していきます。 制約をつけていきます。 SearchViewController,ListTableViewCellを作り、IBOutlet,IBAction接続します。 SearchViewController.swift import UIKit class SearchViewController: UIViewController{ @IBOutlet weak var searchBar: UISearchBar!{ didSet{ searchBar.delegate = self } } @IBOutlet weak var tableView: UITableView!{ didSet{ tableView.delegate = self tableView.dataSource = self } } } ListTableViewCell.swift import UIKit class ListTableViewCell: UITableViewCell { @IBOutlet weak var artistImageView: UIImageView! @IBOutlet private weak var songNameLabel: UILabel! @IBOutlet private weak var artistNameLabel: UILabel! } 全体設計 UIができた後に、今回のアプリの設計を行なっていく。 APIの取得 まず、APIの取得からやっていきたいと思います。 iTunesAPIを使います。 今回は,https://itunes.apple.com/search?term=\(text)&entity=song&country=jpを使っていきます。 こちらでこのように(https://itunes.apple.com/search?term=西野カナ&entity=song&country=jp)APIを叩くと、JSONデータを変換してくれます。 これらのデータをうまく使い今回はアプリを作成していきます。 iTunesAPI 今回のAPIにおいてのロジックを管理するiTunesAPIを書いていきます。 iTunesAPI.swift import Foundation import Reachability class iTunesAPI { private static var task: URLSessionTask? enum SearchRepositoryError: Error { case wrong case network case parse } static func searchRepository(text: String, completionHandler: @escaping (Result<[Song], SearchRepositoryError>) -> Void) { if !text.isEmpty { let reachability = try! Reachability() if reachability.connection == .unavailable { completionHandler(.failure(SearchRepositoryError.network)) return } let urlString = "https://itunes.apple.com/search?term=\(text)&entity=song&country=jp".addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! guard let url = URL(string: urlString) else { completionHandler(.failure(SearchRepositoryError.wrong)) return } let task = URLSession.shared.dataTask(with: url) { (data, res, err) in if err != nil { completionHandler(.failure(SearchRepositoryError.network)) return } guard let date = data else {return} if let result = try? jsonStrategyDecoder.decode(Songs.self, from: date) { completionHandler(.success(result.results)) } else { completionHandler(.failure(SearchRepositoryError.parse)) } } task.resume() } } static private var jsonStrategyDecoder: JSONDecoder { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase return decoder } static func taskCancel() { task?.cancel() } } Songs レスポンスしたデータをデコードするためSongsを作ります。 Songs.swift import Foundation struct Songs :Codable{ let results:[Song] } struct Song :Codable{ //アーティスト名 let artistName :String? //楽曲名 let trackCensoredName:String?   //音源URL let previewUrl:String? //ジャケ写URL let artworkUrl100:String var artworkImageUrl100:URL?{ return URL(string: artworkUrl100) } } SearchViewController 取得したデータをViewに反映させる、またTableView,SearchBarの操作のためにSearchViewControllerを作っていきます。 SearchViewController.swift import UIKit import JGProgressHUD import AVFoundation class SearchViewController: UIViewController{ @IBOutlet weak var searchBar: UISearchBar!{ didSet{ searchBar.delegate = self } } @IBOutlet weak var tableView: UITableView!{ didSet{ tableView.delegate = self tableView.dataSource = self } } private var songs: [Song] = [] override func viewDidLoad() { super.viewDidLoad() let nib = UINib(nibName: ListTableViewCell.cellIdentifier, bundle: nil) tableView.register(nib, forCellReuseIdentifier: ListTableViewCell.cellIdentifier) tableView.rowHeight = UITableView.automaticDimension } } extension SearchViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return songs.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: ListTableViewCell.cellIdentifier, for: indexPath) as! ListTableViewCell let song = songs[indexPath.row] cell.setup(song:song) return cell } } extension SearchViewController: UISearchBarDelegate{ func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { return true } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { iTunesAPI.taskCancel() } func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { guard !(searchBar.text?.isEmpty ?? true) else { return } searchBar.resignFirstResponder() let word = searchBar.text! let progressHUD = JGProgressHUD() progressHUD.show(in: self.view) iTunesAPI.searchRepository(text: word) { result in DispatchQueue.main.async { progressHUD.dismiss() } switch result { case .success(let results): self.songs = results DispatchQueue.main.async { self.tableView.reloadData() } case .failure(let error): DispatchQueue.main.async { switch error { case .wrong : let alert = ErrorAlert.wrongWordError() self.present(alert, animated: true, completion: nil) return case .network: let alert = ErrorAlert.networkError() self.present(alert, animated: true, completion: nil) return case .parse: let alert = ErrorAlert.parseError() self.present(alert, animated: true, completion: nil) return } } } } return } } ListTableViewCell tableViewCellの配置を行うListTableViewCellを作っていきます。 その前に画像のキャッシュのために便利なSDWebImageというライブラリを使いたいと思います。 SDWebImageの詳しい説明、導入の仕方などはこれらの記事を見るとわかると思います。 ListTableViewCell.swift import UIKit import SDWebImage class ListTableViewCell: UITableViewCell { @IBOutlet private weak var artistImageView: UIImageView! @IBOutlet private weak var songNameLabel: UILabel! @IBOutlet private weak var artistNameLabel: UILabel! static let cellIdentifier = String(describing: ListTableViewCell.self) func setup(song: Song) { songNameLabel.text = song.trackCensoredName ?? "" artistNameLabel.text = song.artistName ?? "" if let url = song.artworkImageUrl100 { artistImageView.sd_setImage(with: url, completed: nil) } else { artistImageView.image = nil } } } ErrorAlert 最後に、様々なエラー処理のためのクラスを作っていきたいと思います。 ErrorAlert.swift import UIKit class ErrorAlert { private static func errorAlert(title: String, message: String = "") -> UIAlertController { let alert: UIAlertController = UIAlertController(title: title, message : message, preferredStyle: UIAlertController.Style.alert) let defaultAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) alert.addAction(defaultAction) return alert } static func wrongWordError() -> UIAlertController { return errorAlert(title: "不正なワードが入力されました", message: "検索ワードを確認してください") } static func networkError() -> UIAlertController { return errorAlert(title: "インターネットに接続されていません", message: "接続状況を確認してください") } static func parseError() -> UIAlertController { return errorAlert(title: "データの表示に失敗しました") } } 終わりに 以上でこのようなアプリが作成できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift で Floyd-Warshall Algorithm (ワーシャル・フロイド法)

掲題のアルゴリズムの備忘のメモ // init var dp = [[Int]](repeating: [Int](repeating: Int.max, count: N), count: N) // set inf for i in 0..<N { dp[i][i] = 0 } // zero reset for (from, to, cost) in list { dp[from][to] = cost } // set initial val // floyd-warshall for k in 0..<N { for i in 0..<N { for j in 0..<N { dp[i][j] = min(dp[i][j], add(dp[i][k], dp[k][j])) } } } // helper func add(_ a: Int, _ b: Int) -> Int { return a == Int.max || b == Int.max ? Int.max : a + b }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OAuth2.0とは

OAuth2.0とは OAuth2.0は身近なアプリ等で多く使用されています。 例えばアプリ内で、インスタグラムに投稿したり、クレジット決済をしたい場合等など、他のサービスの機能を使いたいときが多々あると思います。その際に、ユーザの情報等を安全に扱う仕組みの1つにOAuth2.0があります。他にも様々な仕組みはありますが、OAuth2.0が使用されているサービスは結構多く、QiitaやGitHub、FaceBookやSpotifyなどもOAuth2.0を採用しています。 例 説明だけだとイメージが湧きにくいので、身近なFacebookログインを備えたアプリを例に挙げてみます。 FaceBookログインがあるアプリ: 「XYZ」 ログインするユーザー: 「ABC」 XYZ: Facebookさん!!ABCさんの情報をアプリで使ってもいいでしょうか! Facebook: あなたにABCさんの情報を使っていい許可はあるのですか?XYZさんには教えられません。 Facebookからすれば、よくわからない怪しいアプリにユーザーの情報等を使わせろと言われても、応じないのが普通ですよね。これを安全に裏付けを行なっているのがOAuth2.0の仕組みです。 仕組み 以上では概要について説明しましたが、OAuth2.0がどのような仕組みで動いているのかを図を交えながら説明していきます! ユーザー : ①ログインなどのアクションを行う アプリ : ②ユーザのアクションを受け取り、Facebook(連携したいサービス)に連携APIを送る。 Facebook: ③アプリさん!あなたに認可コードを渡すから、ユーザーにそれを持って連携代行確認web(窓口的な)に来るように伝えてね! アプリ: ④もらった認可コード渡すから、窓口行ってきてね!(あ、リダイレクトurlも渡すからアプリを開くときはここから開いてね) ユーザー(ブラウザ): ⑤Facebookさん、これがもらってきた認可コードです!アクセストークン下さい! Facebook: ⑥認可コードあってます!アクセストークンあげます ユーザー(ブラウザ): ⑦リダイレクトURLからアプリを開いてアクセストークンを渡そう! アプリ: ⑧アクセストークン受け取りました!これで色々操作できるぞ! 終わりに QiitaAPIとかGitHubAPIを触ったら結構勉強になりました!ぜひ参考にしてみてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift で Bellman-Ford Algorithm (ベルマンフォード法)

備忘用メモ Time Complexity: O(VE) (V: number of vertexes, E: number of edges) // List starts from 1-index not zero-index. func bellmanford (_ N: Int, _ graph: [Edge], _ start: Int) -> Int { var dist = [Int](repeating: Int.max, count: N+1) dist[start] = 0 for i in 1...N { // O(V) var updated = false for e in graph { // O(E) if dist[e.from] == Int.max { continue } if dist[e.to] > dist[e.from] + e.weight { dist[e.to] = dist[e.from] + e.weight updated = true } } if !updated { break } if updated && i==N { return -1 } } return dist[N] } struct Edge { let from: Int let to: Int let weight: Int }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

型キャスト(as, as!, as?)について

Swiftの勉強をしていて、asが出て来ることが多いので、自分なりに調べてまとめてみました。 参考記事 ・https://fukatsu.tech/swift-cast ・https://qiita.com/entry909/items/3c4c9a20bce0c903dc39 ・http://blog.andgenie.jp/articles/803 キャストについて キャストとは、asという演算子を用いて変数や定数を具体的な型にはめ込むことです。 asについて asは、演算子の一種で2種類の変換を実行することができます。 保証変換 強制変換 保証変換 保証変換とは、別の型に変換するときに、コンパイラ可能な場合キャストの成功が保証されるような変換です。 例えば、アップキャストというのがあって、クラスの継承やプロトコルの準拠などの階層関係がある型同士においてキャストを行うものです。 let str = "アイウエオ" let any = str as Any 強制変換 強制変換とは、変換するときにコンパイラで安全性が保証できないので実行時にエラーを引き起こす可能性があるキャストのことです。この強制変換では、ダウンキャストというものがあります。 ダウンキャストとは ダウンキャストとは、as!、as?のに種類があって、as!は強制的にコンパイラの安全性を無視してキャストを行います。 let any: Any = "アイウエオ" let str = any as! String //String型"アイウエオ" let num = any as! Int //Int型にキャスト失敗(実行時エラー) as?は、キャスト自体が成功するかわからない時に使います。 成功時はOptional型になり、失敗時はnil型になります。 let any: Any = "アイウエオ" let str = any as? String //Optional("アイウエオ") let num = any as? Int //nil
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む