Commit aec93ad6 authored by Elias Häußler's avatar Elias Häußler 🐛 Committed by Elias Häußler

Initial commit

parents
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
MIT License
Copyright (c) 2018 Elias Häußler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:MultiConvert.xcodeproj">
</FileRef>
</Workspace>
//
// AppDelegate.swift
// MultiConvert
//
// Created by Elias Häußler on 02.03.17.
// Copyright © 2017 Elias Häußler. All rights reserved.
//
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "MultiConvert")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
//
// Area.swift
// MultiConvert
//
// Created by Elias Häußler on 18.03.17.
// Copyright © 2017 Elias Häußler. All rights reserved.
//
import UIKit
class Area: NSObject, QuantityProtocol {
static let placeholder = "256.9"
static var units: [UnitObj] = [
UnitObj(key: "SQKM", symbol: "km\u{00B2}", name: "square kilometres", value: 1e-6),
UnitObj(key: "SQM", symbol: "m\u{00B2}", name: "square metres", value: Quantities.BASE),
UnitObj(key: "SQCM", symbol: "cm\u{00B2}", name: "square centimetres", value: 1e4),
UnitObj(key: "SQMI", symbol: "mi\u{00B2}", name: "square miles", value: 1/2589988.110336),
UnitObj(key: "SQYD", symbol: "yd\u{00B2}", name: "square yards", value: 1/0.83612736),
UnitObj(key: "SQFT", symbol: "ft\u{00B2}", name: "square foot", value: 1/0.09290304),
UnitObj(key: "SQIN", symbol: "in\u{00B2}", name: "square inches", value: 1/0.00064516),
UnitObj(key: "AR", symbol: "a", name: "ares", value: 1e-2),
UnitObj(key: "HEKTAR", symbol: "ha", name: "hectares", value: 1e-4),
UnitObj(key: "ACRE", symbol: "ac", name: "acres", value: 1/4046.8564224)
]
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon-App-57x57@1x.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon-App-57x57@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50x50@1x.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50x50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-App-72x72@1x.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-App-72x72@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "Sorting Arrows Horizontal-32.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Sorting Arrows Horizontal-64.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Sorting Arrows Horizontal-96.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
This diff is collapsed.
//
// ConversionObj.swift
// MultiConvert
//
// Created by Elias Häußler on 25.03.17.
// Copyright © 2017 Elias Häußler. All rights reserved.
//
import Foundation
struct ConversionObj
{
/** Quantity */
var quantity: QuantityObj
/** Conversions */
var conversion: [Converter.Values]
}
//
// Converter.swift
// MultiConvert
//
// Created by Elias Häußler on 18.03.17.
// Copyright © 2017 Elias Häußler. All rights reserved.
//
import UIKit
class Converter: NSObject {
/** Class for displaying a conversion */
class Values
{
/** Selected quantity for conversion */
public var quantity: QuantityObj? = nil
/** Selected base unit for conversion */
var inputUnit: UnitObj? = nil
/** Selected target unit for conversion */
var outputUnit: UnitObj? = nil
/** Input value for conversion */
var input: Double? = nil
/** Result of conversion */
var output: Double? = nil
/** Date of conversion */
var date: Date = Date()
/** Initialize object of Converter.Values class */
init(quantity: QuantityObj, inputUnit: UnitObj, outputUnit: UnitObj, input: Double)
{
self.quantity = quantity
self.inputUnit = inputUnit
self.outputUnit = outputUnit
self.input = input
}
}
/** Convert a given value from base to target units with a given quantity */
static func convert(_ input: Double, quantity: QuantityObj, from inputUnit: UnitObj, to outputUnit: UnitObj) throws -> Values
{
if inputUnit != outputUnit
{
// Edit input
let input = Double(String(input).replacingOccurrences(of: ",", with: ".")) ?? input
// Output
let output = Values(quantity: quantity, inputUnit: inputUnit, outputUnit: outputUnit, input: input)
// Conversion settings
let base = try Quantities.getBase(forQuantity: quantity.name)
let units = try Quantities.getUnits(forQuantity: quantity.name, withBase: false)
// Check if input and output units are convertable
if inputUnit == base || units.contains(inputUnit)
{
if units.contains(outputUnit)
{
// Convert if input unit is base unit
if inputUnit == base {
output.output = input * outputUnit.value
}
// Convert from different base unit
else {
output.output = input * outputUnit.value / inputUnit.value
}
} else if outputUnit == base {
// Convert to base unit
output.output = input / inputUnit.value
} else {
// No compatible output unit found
throw MCError.noCompatibleOutputUnit
}
} else {
// No compatible input unit found
throw MCError.noCompatibleInputUnit
}
return output
}
throw MCError.noDifferentUnits
}
}
//
// Currency.swift
// MultiConvert
//
// Created by Elias Häußler on 02.03.17.
// Copyright © 2017 Elias Häußler. All rights reserved.
//
import UIKit
import CoreData
class Currency: NSObject, QuantityProtocol {
//-- CONSTANTS
/** API url to catch latest currencies */
static let API_URL = "https://api.fixer.io/latest"
/** API keys for section rates */
static let API_KEY_RATES = "rates"
/** API keys for section date */
static let API_KEY_DATE = "date"
/** API keys for section base */
static let API_KEY_BASE = "base"
/** API update date format */
static let API_DATE_FORMAT = "yyyy-MM-dd"
/** UI elements */
static let placeholder = "45.99"
//-- VARIABLES
/** JSON currency data */
static var jsonData: Data? = nil
/** Update date */
static var date: Date? = nil
/** Units */
static var units: [UnitObj] = []
//-- FUNCTIONS
/** Get latest exchange rates from fixer API and save them into CoreData */
static func update() throws -> Date?
{
do
{
// Read json data
try jsonData = Data(contentsOf: URL(string: API_URL)!)
// Convert json data
guard let json = try? JSONSerialization.jsonObject(with: jsonData!, options: []) else {
throw MCError.noData
}
// Read json data values
if let jsonResult = json as? [String: Any]
{
// Reset units
units = []
// Update date
if let jsonDate = jsonResult[API_KEY_DATE] as? String
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = API_DATE_FORMAT
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
date = dateFormatter.date(from: jsonDate)
}
// Base currency
if let jsonBase = jsonResult[API_KEY_BASE] as? String {
units.append( UnitObj(key: jsonBase, symbol: jsonBase, name: jsonBase, value: Quantities.BASE) )
}
// Currency rates and symbols
if let jsonRates = jsonResult[API_KEY_RATES] as? [String: Any]
{
// Get units
for (currency, rate) in jsonRates {
units.append( UnitObj(key: currency, symbol: currency, name: currency, value: rate as! Double) )
}
}
}
// Sort units
units = units.sorted(by: { $0 < $1 })
// Write to Core Data
write()
return date
}
catch
{
// No internet connection established