You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

560 lines
14 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
2 years ago
6 years ago
  1. # SwiftyJSON
  2. [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![CocoaPods](https://img.shields.io/cocoapods/v/SwiftyJSON.svg) ![Platform](https://img.shields.io/badge/platforms-iOS%208.0%20%7C%20macOS%2010.10%20%7C%20tvOS%209.0%20%7C%20watchOS%203.0-F28D00.svg) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
  3. SwiftyJSON makes it easy to deal with JSON data in Swift.
  4. Platform | Build Status
  5. ---------| --------------|
  6. *OS | [![Travis CI](https://travis-ci.org/SwiftyJSON/SwiftyJSON.svg?branch=master)](https://travis-ci.org/SwiftyJSON/SwiftyJSON) |
  7. [Linux](https://github.com/IBM-Swift/SwiftyJSON) | [![Build Status](https://travis-ci.org/IBM-Swift/SwiftyJSON.svg?branch=master)](https://travis-ci.org/IBM-Swift/SwiftyJSON) |
  8. 1. [Why is the typical JSON handling in Swift NOT good](#why-is-the-typical-json-handling-in-swift-not-good)
  9. 2. [Requirements](#requirements)
  10. 3. [Integration](#integration)
  11. 4. [Usage](#usage)
  12. - [Initialization](#initialization)
  13. - [Subscript](#subscript)
  14. - [Loop](#loop)
  15. - [Error](#error)
  16. - [Optional getter](#optional-getter)
  17. - [Non-optional getter](#non-optional-getter)
  18. - [Setter](#setter)
  19. - [Raw object](#raw-object)
  20. - [Literal convertibles](#literal-convertibles)
  21. - [Merging](#merging)
  22. 5. [Work with Alamofire](#work-with-alamofire)
  23. 6. [Work with Moya](#work-with-moya)
  24. 7. [SwiftyJSON Model Generator](#swiftyjson-model-generator)
  25. ## Why is the typical JSON handling in Swift NOT good?
  26. Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types.
  27. Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to [Twitter's API](https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-home_timeline)).
  28. The code would look like this:
  29. ```swift
  30. if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
  31. let user = statusesArray[0]["user"] as? [String: Any],
  32. let username = user["name"] as? String {
  33. // Finally we got the username
  34. }
  35. ```
  36. It's not good.
  37. Even if we use optional chaining, it would be messy:
  38. ```swift
  39. if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
  40. let username = (JSONObject[0]["user"] as? [String: Any])?["name"] as? String {
  41. // There's our username
  42. }
  43. ```
  44. An unreadable mess--for something that should really be simple!
  45. With SwiftyJSON all you have to do is:
  46. ```swift
  47. let json = JSON(data: dataFromNetworking)
  48. if let userName = json[0]["user"]["name"].string {
  49. //Now you got your value
  50. }
  51. ```
  52. And don't worry about the Optional Wrapping thing. It's done for you automatically.
  53. ```swift
  54. let json = JSON(data: dataFromNetworking)
  55. let result = json[999999]["wrong_key"]["wrong_name"]
  56. if let userName = result.string {
  57. //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety
  58. } else {
  59. //Print the error
  60. print(result.error)
  61. }
  62. ```
  63. ## Requirements
  64. - iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+
  65. - Xcode 8
  66. ## Integration
  67. #### CocoaPods (iOS 8+, OS X 10.9+)
  68. You can use [CocoaPods](http://cocoapods.org/) to install `SwiftyJSON` by adding it to your `Podfile`:
  69. ```ruby
  70. platform :ios, '8.0'
  71. use_frameworks!
  72. target 'MyApp' do
  73. pod 'SwiftyJSON', '~> 4.0'
  74. end
  75. ```
  76. #### Carthage (iOS 8+, OS X 10.9+)
  77. You can use [Carthage](https://github.com/Carthage/Carthage) to install `SwiftyJSON` by adding it to your `Cartfile`:
  78. ```
  79. github "SwiftyJSON/SwiftyJSON" ~> 4.0
  80. ```
  81. If you use Carthage to build your dependencies, make sure you have added `SwiftyJSON.framework` to the "Linked Frameworks and Libraries" section of your target, and have included them in your Carthage framework copying build phase.
  82. #### Swift Package Manager
  83. You can use [The Swift Package Manager](https://swift.org/package-manager) to install `SwiftyJSON` by adding the proper description to your `Package.swift` file:
  84. ```swift
  85. // swift-tools-version:4.0
  86. import PackageDescription
  87. let package = Package(
  88. name: "YOUR_PROJECT_NAME",
  89. dependencies: [
  90. .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
  91. ]
  92. )
  93. ```
  94. Then run `swift build` whenever you get prepared.
  95. #### Manually (iOS 7+, OS X 10.9+)
  96. To use this library in your project manually you may:
  97. 1. for Projects, just drag SwiftyJSON.swift to the project tree
  98. 2. for Workspaces, include the whole SwiftyJSON.xcodeproj
  99. ## Usage
  100. #### Initialization
  101. ```swift
  102. import SwiftyJSON
  103. ```
  104. ```swift
  105. let json = JSON(data: dataFromNetworking)
  106. ```
  107. Or
  108. ```swift
  109. let json = JSON(jsonObject)
  110. ```
  111. Or
  112. ```swift
  113. if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) {
  114. let json = JSON(data: dataFromString)
  115. }
  116. ```
  117. #### Subscript
  118. ```swift
  119. // Getting a double from a JSON Array
  120. let name = json[0].double
  121. ```
  122. ```swift
  123. // Getting an array of string from a JSON Array
  124. let arrayNames = json["users"].arrayValue.map {$0["name"].stringValue}
  125. ```
  126. ```swift
  127. // Getting a string from a JSON Dictionary
  128. let name = json["name"].stringValue
  129. ```
  130. ```swift
  131. // Getting a string using a path to the element
  132. let path: [JSONSubscriptType] = [1,"list",2,"name"]
  133. let name = json[path].string
  134. // Just the same
  135. let name = json[1]["list"][2]["name"].string
  136. // Alternatively
  137. let name = json[1,"list",2,"name"].string
  138. ```
  139. ```swift
  140. // With a hard way
  141. let name = json[].string
  142. ```
  143. ```swift
  144. // With a custom way
  145. let keys:[JSONSubscriptType] = [1,"list",2,"name"]
  146. let name = json[keys].string
  147. ```
  148. #### Loop
  149. ```swift
  150. // If json is .Dictionary
  151. for (key,subJson):(String, JSON) in json {
  152. // Do something you want
  153. }
  154. ```
  155. *The first element is always a String, even if the JSON is an Array*
  156. ```swift
  157. // If json is .Array
  158. // The `index` is 0..<json.count's string value
  159. for (index,subJson):(String, JSON) in json {
  160. // Do something you want
  161. }
  162. ```
  163. #### Error
  164. ##### SwiftyJSON 4.x
  165. SwiftyJSON 4.x introduces an enum type called `SwiftyJSONError`, which includes `unsupportedType`, `indexOutOfBounds`, `elementTooDeep`, `wrongType`, `notExist` and `invalidJSON`, at the same time, `ErrorDomain` are being replaced by `SwiftyJSONError.errorDomain`.
  166. Note: Those old error types are deprecated in SwiftyJSON 4.x and will be removed in the future release.
  167. ##### SwiftyJSON 3.x
  168. Use a subscript to get/set a value in an Array or Dictionary
  169. If the JSON is:
  170. * an array, the app may crash with "index out-of-bounds."
  171. * a dictionary, it will be assigned to `nil` without a reason.
  172. * not an array or a dictionary, the app may crash with an "unrecognised selector" exception.
  173. This will never happen in SwiftyJSON.
  174. ```swift
  175. let json = JSON(["name", "age"])
  176. if let name = json[999].string {
  177. // Do something you want
  178. } else {
  179. print(json[999].error!) // "Array[999] is out of bounds"
  180. }
  181. ```
  182. ```swift
  183. let json = JSON(["name":"Jack", "age": 25])
  184. if let name = json["address"].string {
  185. // Do something you want
  186. } else {
  187. print(json["address"].error!) // "Dictionary["address"] does not exist"
  188. }
  189. ```
  190. ```swift
  191. let json = JSON(12345)
  192. if let age = json[0].string {
  193. // Do something you want
  194. } else {
  195. print(json[0]) // "Array[0] failure, It is not an array"
  196. print(json[0].error!) // "Array[0] failure, It is not an array"
  197. }
  198. if let name = json["name"].string {
  199. // Do something you want
  200. } else {
  201. print(json["name"]) // "Dictionary[\"name"] failure, It is not an dictionary"
  202. print(json["name"].error!) // "Dictionary[\"name"] failure, It is not an dictionary"
  203. }
  204. ```
  205. #### Optional getter
  206. ```swift
  207. // NSNumber
  208. if let id = json["user"]["favourites_count"].number {
  209. // Do something you want
  210. } else {
  211. // Print the error
  212. print(json["user"]["favourites_count"].error!)
  213. }
  214. ```
  215. ```swift
  216. // String
  217. if let id = json["user"]["name"].string {
  218. // Do something you want
  219. } else {
  220. // Print the error
  221. print(json["user"]["name"].error!)
  222. }
  223. ```
  224. ```swift
  225. // Bool
  226. if let id = json["user"]["is_translator"].bool {
  227. // Do something you want
  228. } else {
  229. // Print the error
  230. print(json["user"]["is_translator"].error!)
  231. }
  232. ```
  233. ```swift
  234. // Int
  235. if let id = json["user"]["id"].int {
  236. // Do something you want
  237. } else {
  238. // Print the error
  239. print(json["user"]["id"].error!)
  240. }
  241. ...
  242. ```
  243. #### Non-optional getter
  244. Non-optional getter is named `xxxValue`
  245. ```swift
  246. // If not a Number or nil, return 0
  247. let id: Int = json["id"].intValue
  248. ```
  249. ```swift
  250. // If not a String or nil, return ""
  251. let name: String = json["name"].stringValue
  252. ```
  253. ```swift
  254. // If not an Array or nil, return []
  255. let list: Array<JSON> = json["list"].arrayValue
  256. ```
  257. ```swift
  258. // If not a Dictionary or nil, return [:]
  259. let user: Dictionary<String, JSON> = json["user"].dictionaryValue
  260. ```
  261. #### Setter
  262. ```swift
  263. json["name"] = JSON("new-name")
  264. json[0] = JSON(1)
  265. ```
  266. ```swift
  267. json["id"].int = 1234567890
  268. json["coordinate"].double = 8766.766
  269. json["name"].string = "Jack"
  270. json.arrayObject = [1,2,3,4]
  271. json.dictionaryObject = ["name":"Jack", "age":25]
  272. ```
  273. #### Raw object
  274. ```swift
  275. let rawObject: Any = json.object
  276. ```
  277. ```swift
  278. let rawValue: Any = json.rawValue
  279. ```
  280. ```swift
  281. //convert the JSON to raw NSData
  282. do {
  283. let rawData = try json.rawData()
  284. //Do something you want
  285. } catch {
  286. print("Error \(error)")
  287. }
  288. ```
  289. ```swift
  290. //convert the JSON to a raw String
  291. if let rawString = json.rawString() {
  292. //Do something you want
  293. } else {
  294. print("json.rawString is nil")
  295. }
  296. ```
  297. #### Existence
  298. ```swift
  299. // shows you whether value specified in JSON or not
  300. if json["name"].exists()
  301. ```
  302. #### Literal convertibles
  303. For more info about literal convertibles: [Swift Literal Convertibles](http://nshipster.com/swift-literal-convertible/)
  304. ```swift
  305. // StringLiteralConvertible
  306. let json: JSON = "I'm a json"
  307. ```
  308. ```swift
  309. / /IntegerLiteralConvertible
  310. let json: JSON = 12345
  311. ```
  312. ```swift
  313. // BooleanLiteralConvertible
  314. let json: JSON = true
  315. ```
  316. ```swift
  317. // FloatLiteralConvertible
  318. let json: JSON = 2.8765
  319. ```
  320. ```swift
  321. // DictionaryLiteralConvertible
  322. let json: JSON = ["I":"am", "a":"json"]
  323. ```
  324. ```swift
  325. // ArrayLiteralConvertible
  326. let json: JSON = ["I", "am", "a", "json"]
  327. ```
  328. ```swift
  329. // With subscript in array
  330. var json: JSON = [1,2,3]
  331. json[0] = 100
  332. json[1] = 200
  333. json[2] = 300
  334. json[999] = 300 // Don't worry, nothing will happen
  335. ```
  336. ```swift
  337. // With subscript in dictionary
  338. var json: JSON = ["name": "Jack", "age": 25]
  339. json["name"] = "Mike"
  340. json["age"] = "25" // It's OK to set String
  341. json["address"] = "L.A." // Add the "address": "L.A." in json
  342. ```
  343. ```swift
  344. // Array & Dictionary
  345. var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]]
  346. json["list"][3]["what"] = "that"
  347. json["list",3,"what"] = "that"
  348. let path: [JSONSubscriptType] = ["list",3,"what"]
  349. json[path] = "that"
  350. ```
  351. ```swift
  352. // With other JSON objects
  353. let user: JSON = ["username" : "Steve", "password": "supersecurepassword"]
  354. let auth: JSON = [
  355. "user": user.object, // use user.object instead of just user
  356. "apikey": "supersecretapitoken"
  357. ]
  358. ```
  359. #### Merging
  360. It is possible to merge one JSON into another JSON. Merging a JSON into another JSON adds all non existing values to the original JSON which are only present in the `other` JSON.
  361. If both JSONs contain a value for the same key, _mostly_ this value gets overwritten in the original JSON, but there are two cases where it provides some special treatment:
  362. - In case of both values being a `JSON.Type.array` the values form the array found in the `other` JSON getting appended to the original JSON's array value.
  363. - In case of both values being a `JSON.Type.dictionary` both JSON-values are getting merged the same way the encapsulating JSON is merged.
  364. In a case where two fields in a JSON have different types, the value will get always overwritten.
  365. There are two different fashions for merging: `merge` modifies the original JSON, whereas `merged` works non-destructively on a copy.
  366. ```swift
  367. let original: JSON = [
  368. "first_name": "John",
  369. "age": 20,
  370. "skills": ["Coding", "Reading"],
  371. "address": [
  372. "street": "Front St",
  373. "zip": "12345",
  374. ]
  375. ]
  376. let update: JSON = [
  377. "last_name": "Doe",
  378. "age": 21,
  379. "skills": ["Writing"],
  380. "address": [
  381. "zip": "12342",
  382. "city": "New York City"
  383. ]
  384. ]
  385. let updated = original.merge(with: update)
  386. // [
  387. // "first_name": "John",
  388. // "last_name": "Doe",
  389. // "age": 21,
  390. // "skills": ["Coding", "Reading", "Writing"],
  391. // "address": [
  392. // "street": "Front St",
  393. // "zip": "12342",
  394. // "city": "New York City"
  395. // ]
  396. // ]
  397. ```
  398. ## String representation
  399. There are two options available:
  400. - use the default Swift one
  401. - use a custom one that will handle optionals well and represent `nil` as `"null"`:
  402. ```swift
  403. let dict = ["1":2, "2":"two", "3": nil] as [String: Any?]
  404. let json = JSON(dict)
  405. let representation = json.rawString(options: [.castNilToNSNull: true])
  406. // representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null}
  407. ```
  408. ## Work with [Alamofire](https://github.com/Alamofire/Alamofire)
  409. SwiftyJSON nicely wraps the result of the Alamofire JSON response handler:
  410. ```swift
  411. Alamofire.request(url, method: .get).validate().responseJSON { response in
  412. switch response.result {
  413. case .success(let value):
  414. let json = JSON(value)
  415. print("JSON: \(json)")
  416. case .failure(let error):
  417. print(error)
  418. }
  419. }
  420. ```
  421. We also provide an extension of Alamofire for serializing NSData to SwiftyJSON's JSON.
  422. See: [Alamofire-SwiftyJSON](https://github.com/SwiftyJSON/Alamofire-SwiftyJSON)
  423. ## Work with [Moya](https://github.com/Moya/Moya)
  424. SwiftyJSON parse data to JSON:
  425. ```swift
  426. let provider = MoyaProvider<Backend>()
  427. provider.request(.showProducts) { result in
  428. switch result {
  429. case let .success(moyaResponse):
  430. let data = moyaResponse.data
  431. let json = JSON(data: data) // convert network data to json
  432. print(json)
  433. case let .failure(error):
  434. print("error: \(error)")
  435. }
  436. }
  437. ```
  438. ## SwiftyJSON Model Generator
  439. Tools to generate SwiftyJSON Models
  440. * [JSON Cafe](http://www.jsoncafe.com/)
  441. * [JSON Export](https://github.com/Ahmed-Ali/JSONExport)