import Foundation let fullName = "First Last" let fullNameArr = fullName.components(separatedBy: " ") let name = fullNameArr[0] let surname = fullNameArr[1]
2019年11月13日
Swift String Splite
2019年9月5日
SQLite資料庫,不覆蓋舊資料庫的流程,建議方式 for Swift
完整範例
compareDb.swift
呼叫方式:
compareDbVersion(iDbName: "db",iDbType: "sqlite")
在製作資料庫的過程,通常可能因為一段時間的使用,使得某些因素,讓我們不得不去建立新的資料表或者是欄位,但覆蓋資料庫不是一個好的作法,以下是說如何在不動到原本的資料情況下,建立新的資料表和欄位。
以下是寫的流程:
程式碼如下:
compareDb.swift
呼叫方式:
compareDbVersion(iDbName: "db",iDbType: "sqlite")
如果想知道原理可以看以下說明:
以下是寫的流程:
1. 我們將利用比對的方式去查資料庫的資料是否有不一樣。
if(getLocalDbVersion() != getNewDbVersion())
{
compareDB()
//最後修改版本
update(updateStatementString: "UPDATE dbVersion SET version='\(getNewDbVersion())'")
}else{
print("資料庫版本一樣:\(dbVersion)|\(getNewDbVersion())")
}
2.可以先檢查兩個資料庫版本的不一樣「本機資料庫」、「新的資料庫」。
假設,你的資料庫是叫 db.sqlite2.1 在「本機資料庫」開啟的方式,這個只能用iTunes才看得到。
//開啟本機資料庫
func openLocalDatabase() -> OpaquePointer? {
var db: OpaquePointer? = nil
let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)
let docsDir:String! = dirPaths[0]
let destPath = (docsDir as NSString).appendingPathComponent("/db.sqlite")
if sqlite3_open(destPath, &db) == SQLITE_OK {
//print("Successfully opened connection to database at \(destPath)")
return db
}
return nil
}
2.2 「新的資料庫」開啟的方式,這個的意思是你放在app裡面的db.sqlite,如圖1:
//開啟新的sqlite資料
func openNewDatabase() -> OpaquePointer?{
var db: OpaquePointer? = nil
let path = Bundle.main.path(forResource: "db", ofType:"sqlite")
if sqlite3_open(path, &db) == SQLITE_OK {
//print("Successfully opened connection to database at \(path)")
return db
} else {
}
return nil
}
![]() |
| 圖1、App的db.sqlite |
3.判斷這兩個版本是否一樣,不一樣的話,才進行「第4步」之後的比對動作。以下是取得「本機資料庫」和「新的資料庫」版本方式
3.1 「本機資料庫」版本的程式
//取得本機版本
func getLocalDbVersion()->String{
var Version = ""
var db = openLocalDatabase()
var queryStatementString = "SELECT version FROM dbVersion"
var queryStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
if sqlite3_step(queryStatement) == SQLITE_ROW {
let queryResultCol1 = sqlite3_column_text(queryStatement, 0)
Version = String(cString: queryResultCol1!)
} else {
print("Query returned no results")
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
return Version
}
3.2 「新的資料庫」版本的程式
//取得新的資料庫的資料
func getNewDbVersion()->String
{
var Version = ""
var db = openNewDatabase()
var queryStatementString = "SELECT version FROM dbVersion"
var queryStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
if sqlite3_step(queryStatement) == SQLITE_ROW {
let queryResultCol1 = sqlite3_column_text(queryStatement, 0)
Version = String(cString: queryResultCol1!)
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
return Version
}
4.當版本的比對不一樣時,我們就開始以下的動作。
4.1 利用 SELECT name, sql FROM sqlite_master WHERE type='table' 取得「新的資料庫」各「資料表的名稱」和「Create Table SQL語法」,圖2。
4.2 利用迴圈查詢「本機資料庫」的資料表是否和「新的資料表」的資料表相符的資料,語法:SELECT name FROM sqlite_master WHERE type='table' AND name='\(name)'。如果符合,就繼續「檢查欄位」(4.5),如果沒有,就直接利用「Create Table SQL語法」建立新得資料表(4.3、4.4)。
![]() |
| 圖2 |
4.3 比對資料表與欄位的程式碼
//當資料版本不一樣的時候,會進行,比對db
func compareDB()
{
var dbLocal = openLocalDatabase()//手機本身的sqlite
var dbNew = openNewDatabase()//新的資料庫
var queryStatementString = "SELECT name, sql FROM sqlite_master WHERE type='table'"
var queryStatementLocal: OpaquePointer? = nil
var queryStatementNew: OpaquePointer? = nil
//取得新資料庫的資料表名稱和Crate Table SQL語法
if sqlite3_prepare_v2(dbNew, queryStatementString, -1, &queryStatementNew, nil) == SQLITE_OK {
//
while sqlite3_step(queryStatementNew) == SQLITE_ROW {
//取得「新資料庫」資料表名稱
let queryResultCol1 = sqlite3_column_text(queryStatementNew, 0)
let name = String(cString: queryResultCol1!)
//開始比對「本機資料庫」是否有相對應的資料表
queryStatementString = "SELECT name FROM sqlite_master WHERE type='table' AND name='\(name)'"
//
if sqlite3_prepare_v2(dbLocal, queryStatementString, -1, &queryStatementLocal, nil) == SQLITE_OK {
if sqlite3_step(queryStatementLocal) == SQLITE_ROW {
//如果「本機資料庫」有該比資料,那就再去比對「欄位」
compareColumn(tableName: name)
}else{
//如果「本機資料庫」沒有該資料表,那就取得Create Table SQL語法,建立新的
let queryResultCol1 = sqlite3_column_text(queryStatementLocal, 1)
let createTableString = String(cString: queryResultCol1!)
createTable(createTableString: createTableString)
}
}
}
} else {
print("SELECT statement could not be prepared")
}
// 6
sqlite3_finalize(queryStatementNew)
sqlite3_finalize(queryStatementLocal)
}
4.3 建立資料表的Swift寫法如下:
func createTable(createTableString:String) {
var db = openLocalDatabase()
var createTableStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
if sqlite3_step(createTableStatement) == SQLITE_DONE {
print("Contact table created.")
} else {
print("Contact table could not be created.")
}
} else {
print("CREATE TABLE statement could not be prepared.")
}
sqlite3_finalize(createTableStatement)
}
4.4 比對欄位的寫法:
//比較欄位
func compareColumn(tableName:String)
{
var dbNew = openNewDatabase()
var queryStatementString = "pragma table_info(\(tableName))"
var queryStatementNew: OpaquePointer? = nil
//取得新的資料表的資料欄位
if sqlite3_prepare_v2(dbNew, queryStatementString, -1, &queryStatementNew, nil) == SQLITE_OK {
while sqlite3_step(queryStatementNew) == SQLITE_ROW {
//取得所有欄位名稱
let queryResultCol1 = sqlite3_column_text(queryStatementNew, 1)
let fieldName = String(cString: queryResultCol1!)
//判斷本機資料庫欄位是否存在
if(!isFieldExist(tableName: tableName, fieldName: fieldName))
{
//如果不存在,就在該資料表建立新的欄位
let queryResultCol1 = sqlite3_column_text(queryStatementNew, 1)
let col1 = String(cString: queryResultCol1!)
let queryResultCol2 = sqlite3_column_text(queryStatementNew, 2)
let col2 = String(cString: queryResultCol2!)
let col3 = sqlite3_column_int(queryStatementNew, 3)
let col5 = sqlite3_column_int(queryStatementNew, 5)
var sql = "ALTER TABLE \(tableName) ADD COLUMN \(col1) \(col2) ";
if col3 == 1 {
sql = sql + " not null"
}
if col5 == 1 {
sql = sql + " PRIMARY KEY(\(tableName))";
}
//建立新的欄位
createTable(createTableString: sql)
}else{
print("表格:\(tableName),欄位:\(fieldName) 存在")
}
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatementNew)
}
4.5 檢查「本機資料庫」是否有相對應的欄位:
//檢查本機是否有這個欄位
func isFieldExist(tableName:String,fieldName:String)->Bool{
var isExist :Bool = false
var dbLocal = openLocalDatabase()
var queryStatementString = "pragma table_info(\(tableName))"
var queryStatementLocal: OpaquePointer? = nil
if sqlite3_prepare_v2(dbLocal, queryStatementString, -1, &queryStatementLocal, nil) == SQLITE_OK {
while sqlite3_step(queryStatementLocal) == SQLITE_ROW {
//檢查「本機資料庫」所有欄位名稱
let queryResultCol1 = sqlite3_column_text(queryStatementLocal,1)
let field = String(cString: queryResultCol1!)
if fieldName == field
{
//如果一樣的欄位就直接離開。
isExist = true
break
}
}
}
sqlite3_finalize(queryStatementLocal)
return isExist
}
5.最後、最後,更新本機端的版本號
檢查完畢後,取得「新的資料庫」版本號,進行「本機資料庫」的更新。程式碼如下:
//最後修改版本
update(updateStatementString: "UPDATE dbVersion SET version='(getNewDbVersion())'")
func update(updateStatementString:String) {
var db = openLocalDatabase()
var updateStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
if sqlite3_step(updateStatement) == SQLITE_DONE {
print("Successfully updated row.")
} else {
print("Could not update row.")
}
} else {
print("UPDATE statement could not be prepared")
}
sqlite3_finalize(updateStatement)
}
2019年9月4日
Swift 一個教學如何使用 sqlite ,select、update、insert、delete 的教學網站。
繼續昨天的問題,https://pippenhsiao.blogspot.com/2019/09/swift-copy-sqlite-mobile-device.html 建立好 .sqlite 之後,在 Swift 又要如何使用呢?
之前在寫的時候,有發現一個寫得很好的網站,所以,做個記錄。包含:spatialite 也能用,但要加一些東西,有空再記錄吧!
2019年9月3日
Swift 如何 Copy 一份.sqlite 到Mobile Device
一、先把建好的 .sqlite 放到自己建立好的資料夾裡,資料夾什麼名稱都沒關係。

二、填入下列程式碼,就可以了。

二、填入下列程式碼,就可以了。
let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)
let docsDir:String! = dirPaths[0]
let destPath = (docsDir as NSString).appendingPathComponent("/db.sqlite")//這裡填入你的.sqlite
let fileMgr = FileManager.default
if let path = Bundle.main.path(forResource: "db", ofType:"sqlite")
{
if !fileMgr.fileExists(atPath: destPath)//如果db不存在,那就直接複製一份
{
do {
try fileMgr.copyItem(atPath: path, toPath: destPath)
print("success")//
}
catch let error as NSError
{
print(error.localizedDescription)
}
}else{
print(".sqlite已存在")//
}
}
2019年5月3日
Swift 5 讀取 json 檔案及解析
//CheckList.json
{
"CheckList": [
{
"CheckItem": "施工計畫書之安全衛生管理計畫",
"Major": 1,
"CheckContent": [
{
"Major": 1,
"Minor": 1,
"CheckItem": "安全衛生管理計畫各項安全檢查"
},
{
"Major": 1,
"Minor": 2,
"CheckItem": "工地潛在危險狀況分析及採取相對應之防範措施"
}
]
}
]
}
//========================================================================================================
//讀取 Json 資料
func readJson()
{
if let path = Bundle.main.path(forResource: "JsonFile/CheckList", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
if let myJSON = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] {
//檢查項目
if let data = myJSON["CheckList"] as? [[String: Any]] {
for jsonDict in data {
var CheckItem = jsonDict["CheckItem"] as? String
print("CheckItem:" + CheckItem!)
var Major = jsonDict["Major"] as? Int
print("Major:" + String(Major!))
//檢查內容
if let CheckContent = jsonDict["CheckContent"] as? [[String: Any]] {
for CheckContentDict in CheckContent {
var CheckItem = CheckContentDict["CheckItem"] as? String
print("CheckItem:" + CheckItem!)
var Major = CheckContentDict["Major"] as? Int
print("Major:" + String(Major!))
var Minor = CheckContentDict["Minor"] as? Int
print("Minor:" + String(Minor!))
}
}
}
}
}
} catch let error {
print("parse error: \(error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}
}
2018年10月9日
Swift 2D Array add 1D Array(使用 append)
var test2arr:[[Float]] = []
var test1arr:[Float] = [1.0,2.0,3.0]
test2arr.append(test1arr)
test1arr = [4.0,5.0,6.0]
test2arr.append(test1arr)
print(test2arr)
//[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Swift 如何回傳 兩個以上的 參數及如何接收。
//回傳方式
//接收方式
func parserE3D(str:String)->([Float],[Int])
{
var Vertexs:[Float] = []
var Faces:[Int] = []
.... 一堆程式
return (Vertexs,Faces)
//接收方式
var Vertexs:[Float] = []
var Faces:[Int] = []
(Vertexs,Faces) = parserE3D(str: datas.hexEncodedString())
2018年10月8日
Swift 16進位(Hex) 轉 Float (依據 IEEE 754)

以 3e4ccccd 轉成 0.2 為範例
Hex2Float(HexStr: "3e4ccccd")
//依據 IEEE 754 https://www.h-schmidt.net/FloatConverter/IEEE754.html,16進位轉Float
func Hex2Float(HexStr:String)
{
let binary = Array(get8Hex2Binary(HexStr:HexStr))
let Sign = binary[0...0]
let Exponent = binary[1...8]
let Mantissa = binary[9...(binary.count-1)]
print("Sign:" + Sign) //0
print("Exponent:" + Exponent) //01111100
print("Mantissa:" + Mantissa) //10011001100110011001101
var s = 1.0
if String(Sign) == "1"
{
s = -1.0
}
let e = Binary2Int(binary: String(Exponent)) - 127
print("e:" + String(e)) //-3
let _2_e = powf(2,Float(e))
print(_2_e) //0.125
let CalcMant = CalculationMantissa(Mantissa: String(Mantissa))
let toFloat = Float(s) * _2_e * CalcMant
print("toFloat:" + String(toFloat)) //0.2
}
//計算Mantissa
func CalculationMantissa(Mantissa:String) -> Float{
var calu_m_sum = 0.0
var e = 0
for m in Mantissa {
//println(char)
e = e - 1
if m == "1"
{
let d = powf(2.0,Float(e))
calu_m_sum = calu_m_sum + Double(d)
}
}
print("calu_m_sum:" + String(1.0 + calu_m_sum)) //1.600000023841858
return Float(1.0 + calu_m_sum)
}
//8個位元字串轉成Binary
func get8Hex2Binary(HexStr:String) -> String{
var binary = String(Int(HexStr, radix: 16)!, radix: 2)
print("binary:" + binary) //111110010011001100110011001101 未補滿
if(binary.count != 32)//要補滿32個位元
{
for index in binary.count..<32 {
binary = "0" + binary
}
}
print("binary:" + binary) //00111110010011001100110011001101 補滿32位元
return binary
}
//Binary轉Int
func Binary2Int(binary:String)->Int
{
let number = Int(binary, radix: 2)
return number!
}
2018年10月5日
Swift String Array 之間轉換及取得字串或陣列中的前5筆資料。
//原始資料
let sample_str = "0123456789"
print(sample_str) //....0123456789
//String 取得前5個字串
let sample_sring_5 = sample_str[..<sample_str.index(sample_str.startIndex, offsetBy: 5)]
print(sample_sring_5) //....01234
//String 轉 Array
var sample_str_array = Array(sample_str)
print(sample_str_array) //....["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
//Array 取得前5筆陣列值
let sample_array_5 = sample_str_array[0...4]
print(sample_array_5) //....["0", "1", "2", "3", "4"]
//Array 轉 String
let sample_array_2_str = String(sample_array_5)
print(sample_array_2_str) //....01234
2016年1月18日
Swift Ajax 出現 Error Domain=kCFErrorDomainCFNetwork Code=-1022 問題。
找到 info.plist 加入
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
訂閱:
文章 (Atom)


