Is it possible to have lazy behaviour in enum's function? - swift

Swift provides a very handy lazy var
However, I was wondering, can we achieve similar lazy functionality for Enum's function?
For instance,
class Utils {
static let userDataDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
}
enum UserDataDirectory : String {
case Extract = "extract"
case Camera = "camera"
case Mic = "mic"
case Attachment = "attachment"
case Recording = "recording"
case RestoreAttachment = "restore_attachment"
case RestoreRecording = "restore_recording"
func get() -> URL {
return Utils.userDataDirectory.appendingPathComponent(self.rawValue)
}
}
Is it ever possible, to turn enum UserDataDirectory's get function to have lazy evaluation behaviour.
Or, is there a way to avoid evaluation of appendingPathComponent every-time, since Utils.userDataDirectory and self.rawValue is constant?

You just mean that you want a lazily-evaluated static value. That's straightforward; add a static cache:
enum UserDataDirectory : String {
// ...
// Define storage
private static var urls: [Self: URL] = [:]
func url() -> URL {
// Check the cache
if let url = Self.urls[self] {
return url
} else {
// Compute and cache
let url = Utils.userDataDirectory.appendingPathComponent(self.rawValue)
Self.urls[self] = url
return url
}
}
}

Related

Swift - switch between Core ML Model

I'm trying to compare predictions from different MLModels in SwiftUI. To do that I have to switch between them, but can't because every ML variable has its own class, so I get the error:
Cannot assign value of type 'ModelOne' to type 'ModelTwo'
Here's an example code:
import Foundation
import CoreML
import SwiftUI
let modelone = { //declaration model 1
do {
let config = MLModelConfiguration()
return try ModelOne(configuration: config)
} catch {
/*...*/
}
}()
let modeltwo = { //declaration model 2
do {
let config = MLModelConfiguration()
return try ModelTwo(configuration: config)
} catch {
/*...*/
}
}()
var imageused : UIImage! //image to classify
var modelstring = "" //string of model user chosen
var modelchosen = modelone
Button(action: { //button user decide to use model two
modelstring = "Model Two"
}) {/*...*/}
/*...*/
func classifyphoto() {
guard let image = imageused as UIImage?,
let imagebuffer = image.convertToBuffer() else {
return
}
if modelstring == "Model Two" { //if the user chosen model two, use ModelTwo
modelchosen = modeltwo // Error: Cannot assign value of type 'ModelOne' to type 'ModelTwo'
} else {
modelchosen = modelone}
let output = try? modelchosen.prediction(image: imagebuffer) //prediction with model chosen
if let output = output {
let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
_ = results.map { /*...*/
}
}
}
Thank you!
The issue is that the two model classes do not have a common class or common inherited class. There are several ways to implement what you want. I think this is the best way based on your example.
class MyModel {
var model: MLModel? = nil
init(modelName: String) {
let bundle = Bundle.main
if let modelURL = bundle.url(forResource: modelName, withExtension:"mlmodelc") {
do {
self.model = try MLModel(contentsOf: modelURL)
}
catch {
print("Unable to open MLModel: \(error)")
}
}
}
}
class TestModel {
class func testModels() {
let modelOne = MyModel(modelName: "ModelOne")
let modelTwo = MyModel(modelName: "ModelTwo")
var selectedModel = modelOne
selectedModel = modelTwo
}
}
Swift is a statically typed language which means that in the general case you cannot assign a variable of one type to a variable of another type:
var int: Int = 42
int = "Hello, world!" // Not allowed: cannot assign String to Int
The problem is that modelchosen is of type ModelOne since it is initialized with modelone, thus, you cannot later assign modeltwo to it as you are trying to do.
To make that working, you have first to identify the common capabilities of ModelOne and ModelTwo. Take a look at their definition. For instance, do their .predict(image:) method return the same type? It looks like you are trying to do image classification, so a common capability could be the capability to return a String describing the image (or a list of potential objects, etc.).
When you'll have identified the common capability, you'll be able to define the common interface of your different types. This common interface can be expressed in many ways:
Using a base class
Using a protocol
Using an enum with payloads (union)
The following examples suppose that the common capabilities are:
The two networks can both be initialized with a MLModelConfiuration
They are used for image classification, i.e. they predict label (a String) describing a given image
Using a base class
The base class definition expresses those requirements like this:
class MLClassifier {
init(from config: MLModelConfig) {
fatalError("not implemented")
}
func classify(image: ImageBuffer) -> String {
fatalError("not implemented")
}
}
You then derive this base class for the two models (example with the first one:
final class ModelOne: MLClassifier {
init(from config: MLModelConfig) {
// the specific implementation for `ModelOne`...
}
func classify(image: ImageBuffer) -> String {
// the specific implementation for `ModelOne`..
}
}
Finally, you can make the variable modelchosen to be of type MLClassifier to erase the underlying concrete type of the model:
var modelchosen: MLClassifier = ModelOne(from: config1)
As MLClassifier is a common base class for both ModelOne and ModelTwo you can dynamically change the type of modelchosen whenever you need:
// Later...
modelchosen = ModelTwo(from: config2)
The variable modelchosen being of type MLClassifier ensures that you can call the .classify(image:) method whatever the concrete model type is:
func classifyphoto() {
guard let image = imageused as UIImage?,
let imagebuffer = image.convertToBuffer() else {
return
}
let output = modelchosen.classify(image: imageBuffer)
// Update the UI...
}
Using protocols
Protocols are the modern and preferred way of expressing common interfaces in Swift, they should be used over classes when possible:
protocol MLClassifier {
init(from config: MLModelConfig)
func classify(image: ImageBuffer) -> String
}
// Implement the protocol for your models
struct ModelOne: MLClassifier {
init(from config: MLModelConfig) { ... }
func classify(image: ImageBuffer) -> String { ... }
}
// Store an instance of any `MLClassfier` using an existential
var classifier: any MLClassifier = ModelOne(from: config1)
// Later...
classifier = ModelTwo(from: config2)
To sum up, the key is to identify the common capabilities of the different types you are trying to unify. For instance, if the two models output at some point a classLabelProbs of the same type, then you could use this as the common abstraction.
As a last resort, you could wrap everything in a big if-else statement, event though it is not recommended since it is not very readable, is not a good way to encapsulate common behavior and leads to a lot of code repetition:
func classifyphoto() {
guard let image = imageused as UIImage?,
let imagebuffer = image.convertToBuffer() else {
return
}
if modelstring == "Model Two" {
// Use modeltwo
let output = try? modeltwo.prediction(image: imagebuffer)
if let output = output {
let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
_ = results.map { /*...*/ }
} else {
// Use modelone
let output = try? modelone.prediction(image: imagebuffer)
if let output = output {
let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
_ = results.map { /*...*/ }
}
}

Enum int with string value in swift

I have enum of API endpoints which i use during the base url creation.
one of the API which has pagination is like pagination = "/api/pagination_test/%#?"
Now during pagination i want to pass value to enum initalizer and create an enum which will be accepted by the base-url creation function.
enum APIEndPoints{
case registerUser = "/register"
case login = "/login"
case getData = "/data?"
case pagination = "/api/pagination_test/%#?"
}
func callPaginationAPI(pagenumber: Int){
//make enum with pagenumber, i am stuck in this line.
//let enum =
//call main service method,pass enum as argument.
mainService(endPoint: .pagination)
// this expect an enum of pagination with proper pagenumber
}
func mainService(endpoint: APIEndpoints){
//create base url here
let url = requestUrl()
//do nsurlsession with prepared url
}
func requestUrl( endPoint: APIEndPoints) -> String {
let baseURL = self.requestBaseUrl()
return baseURL + endPoint.rawValue
}
How can i create a pagination enum with value one - am expecting enum as /api/pagination_test/1? , /api/pagination_test/2?
First of all the enum should be update so we can use String(format:)
enum APIEndPoints: String {
case registerUser = "/register"
case login = "/login"
case getData = "/data?"
case pagination = "/api/pagination_test/%d?"
}
To avoid having to pass the page number as a parameter through all methods it is probably better to wrap the enum inside a struct together with the (optional) page number and have a computed property that gets the endpoint as a string
struct EndPoint {
let apiEndPoint: APIEndPoints
let page: Int?
var endPointValue: String {
switch apiEndPoint {
case .pagination:
return String(format: apiEndPoint.rawValue, page ?? 1)
default:
return apiEndPoint.rawValue
}
}
init(_ endPoint: APIEndPoints, page: Int? = nil) {
apiEndPoint = endPoint
self.page = page
}
}
And then pass an instance of this struct instead
func callPaginationAPI(pagenumber: Int){
mainService(endpoint: EndPoint(.pagination, page: pagenumber))
}
And use the computed property when creating the url
func requestUrl(endPoint: EndPoint) -> String {
let baseURL = self.requestBaseUrl()
return baseURL + endPoint.endPointValue
}
And an example to use the struct without a page number
func callLoginAPI() {
mainService(endpoint: EndPoint(.login))
}

Is there a better way to build many different URLs?

I am working on building a framework to connect to a specific API and will need to build a lot of different paths. My current setup is using an Enum for returning a URL, which works pretty well for the most part. My only problem I have with this approach is there will be a lot of different cases (~30 total) by the time I'm done. I was wondering if anyone has a better solution?
enum API {
var baseURL: URL {
return URL(string: "https://api.example.com")!
}
case user
case emails
case posts
case post(id: String)
// etc . . .
}
extension API: Path {
func appendPathComponent(_ string: String) -> URL {
return baseURL.appendingPathComponent(string)
}
var url: URL {
switch self {
case .user: return baseURL
case .emails: return appendPathComponent("email")
case .posts: return appendPathComponent("posts")
case .post(let id): return appendPathComponent(id)
// etc
}
}
}
// call site
let url = API.emails.url
I would turn this around the same way that Notification.Name does. Make these extensions on URL:
extension URL {
static let apiBase = URL(string: "https://api.example.com")!
static let apiUser = apiBase
static let apiEmails = apiBase.appendingPathComponent("email")
static let apiPosts = apiBase.appendingPathComponent("posts")
static func apiPost(id: String) -> URL { return apiBase.appendingPathComponent(id) }
}
Then calling it is just:
let x = URL.apiEmails
And in cases where URL is known, you don't even have to include that:
let task = URLSession.shared.dataTask(with: .apiEmails)
or
let task = URLSession.shared.dataTask(with: .apiPost(id: "123"))

Can you simultaneously define and instantiate implicit types in Swift?

Just messing around with the language thinking of how I want to structure some UserDefaults that automatically generate keys based on the hierarchy. That got me wondering... Is it possible to simultaneously define, and instantiate a type, like this?
let myUserSettings = {
let formatting = {
var lastUsedFormat:String
}
}
let lastUsedFormat = myUserSettings.formatting.lastUsedFormat
Note: I can't use statics because I specifically need instancing so nested structs/classes with static members will not work for my case.
Here's the closest thing I could come up with, but I hate that I have to create initializers to set the members. I'm hoping for something a little less verbose.
class DefaultsScope {
init(_ userDefaults:UserDefaults){
self.userDefaults = userDefaults
}
let userDefaults:UserDefaults
func keyForSelf(property:String = #function) -> String {
return "\(String(reflecting: self)).\(property)"
}
}
let sharedDefaults = SharedDefaults(UserDefaults(suiteName: "A")!)
class SharedDefaults : DefaultsScope {
override init(_ userDefaults:UserDefaults){
formatting = Formatting(userDefaults)
misc = Misc(userDefaults)
super.init(userDefaults)
}
let formatting:Formatting
class Formatting:DefaultsScope {
let maxLastUsedFormats = 5
fileprivate(set) var lastUsedFormats:[String]{
get { return userDefaults.stringArray(forKey:keyForSelf()) ?? [] }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
func appendFormat(_ format:String) -> [String] {
var updatedListOfFormats = Array<String>(lastUsedFormats.suffix(maxLastUsedFormats - 1))
updatedListOfFormats.append(format)
lastUsedFormats = updatedListOfFormats
return updatedListOfFormats
}
}
let misc:Misc
class Misc:DefaultsScope {
var someBool:Bool{
get { return userDefaults.bool(forKey:keyForSelf()) }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
}
}
So is there a simpler way?
Disclaimer: this is, probably, just an abstract solution that should not be used in real life :)
enum x {
enum y {
static func success() {
print("Success")
}
}
}
x.y.success()
Update: Sorry, folks, I can't stop experimenting. This one looks pretty awful :)
let x2= [
"y2": [
"success": {
print("Success")
}
]
]
x2["y2"]?["success"]?()
Update 2: One more try, this time with tuples. And since tuples must have at least two values, I had to add some dummies in there. Also, tuples cannot have mutating functions.
let x3 = (
y3: (
success: {
print("Success")
},
failure: {
print("Failure")
}
),
z3: 0
)
x3.y3.success()
How about you try nesting some swift structs?
struct x {
struct y {
static func success() {
print("success")
}
}
}
x.y.success()
You cannot have that kind of structure but you cant access y from inside x, since y is only visible inside the scope of x and so is success inside the scope of y. There is no way that you can access them from outside
One other alternative is to have higher order function like so, which return closure which is callable.
let x = {
{
{
print("Success")
}
}
}
let y = x()
let success = y()
success()
or
x()()()
The real world usage of higher order function for userdefaults could be something like this,
typealias StringType = (String) -> ((String) -> Void)
typealias IntType = (String) -> ((Int) -> Void)
typealias BoolType = (String) -> ((Bool) -> Void)
typealias StringValue = (String) -> String?
typealias IntValue = (String) -> Int?
typealias BoolValue = (String) -> Bool?
func userDefaults<T>(_ defaults: UserDefaults) -> (String) -> ((T) -> Void) {
return { key in
return { value in
defaults.setValue(value, forKey: key)
}
}
}
func getDefaultsValue<T>(_ defaults: UserDefaults) -> (String) -> T? {
return { key in
return defaults.value(forKey: key) as? T
}
}
let setStringDefaults: StringType = userDefaults(.standard)
setStringDefaults("Name")("Jack Jones")
setStringDefaults("Address")("Australia")
let setIntDefaults: IntType = userDefaults(.standard)
setIntDefaults("Age")(35)
setIntDefaults("Salary")(2000)
let setBoolDefaults: BoolType = userDefaults(.standard)
setBoolDefaults("Married")(false)
setBoolDefaults("Employed")(true)
let getStringValue: StringValue = getDefaultsValue(.standard)
let name = getStringValue("Name")
let address = getStringValue("Address")
let getIntValue: IntValue = getDefaultsValue(.standard)
let age = getIntValue("Age")
let salary = getIntValue("Salary")
let getBoolValue: BoolValue = getDefaultsValue(.standard)
let married = getBoolValue("Married")
let employed = getBoolValue("Employed")
I am not sure if you like the pattern, but it has some good use cases as you can see from below, setStringDefaults you can set strings value to string key and all of them are typesafe.
You can extend this for your use case. But, you could use struct as well and use imperative code, which could be easier to understand. I see beauty in this as well.
Ok, I think I've figured it out. This first class can go in some common library that you use for all your apps.
class SettingsScopeBase {
private init(){}
static func getKey(setting:String = #function) -> String {
return "\(String(reflecting:self)).\(setting)"
}
}
The next part is a pair of classes:
The 'Scoping' class where you define which user defaults instance to use (along with anything else you may want to specify for this particular settings instance)
The actual hierarchy that defines your settings
Here's the first. I'm setting this up for my shared settings between my application and it's extension:
class SharedSettingsScope : SettingsScopeBase{
static let defaults = UserDefaults(suiteName: "group.com.myco.myappgroup")!
}
And finally, here's how you 'set up' your hierarchy as well as how you implement the properties' bodies.
class SharedSettings:SharedSettingsScope{
class Formatting:SharedSettingsScope{
static var groupsOnWhitespaceOnlyLines:Bool{
get { return defaults.bool(forKey: getKey()) }
set { defaults.set(newValue, forKey: getKey()) }
}
}
}
And here's how you use them...
let x = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// x = false
SharedSettings.Formatting.groupsOnWhitespaceOnlyLines = true
let y = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// y = true
I'm going to see if I can refine/optimize it a little more, but this is pretty close to where I want to be. No hard-coded strings, keys defined by the hierarchy where they're used, and only setting the specific UserDefaults instance in one place.

Constant string with interpolation

I have a string with interpolation like this
let userID = 123
let userProfileUrl = "website.com/user/\(userID)"
I would like make website.com/user/\(userID) a constant but still remain its interpolation, so that I can interpolate it with an userID.
I wonder if anyone knows a good way to do that
You can make userProfileUrl a lazy var. In this case you would need to specify the type of the userProfileUrl (i.e. String) and would need to use self.userID instead of userID
let userID = 123
lazy var userProfileUrl: String = "website.com/user/\(self.userID)"
Or if both properties are constants, and don't depend on an instance of the class you can place them outside of the class definition and it should work:
let userID = 123
let userProfileUrl = "website.com/user/\(userID)"
class MyClass {
}
You can also make userProfileUrl a computed property
let userID = 123
var userProfileUrl: String {
return "website.com/user/\(userID)"
}
If you don't like the extra lines that the computed property adds you could format it so that it's all on one line
let userID = 123
var userProfileUrl: String { return "website.com/user/\(userID)" }
var userProfileURL: (String) -> String = {
return "website.com/user/\($0)"
}
userProfileURL(userID)
This works but I would consider using an enum. You can now create a new case per endpoint.
enum Website {
case UserProfile(Int)
var base: String { return "http://website.com" }
var path: String {
switch self {
case let .UserProfile(userID):
return "user/\(userID)"
}
}
var url: URL { return URL(string: "\(base)/\(path)")! }
}
let userProfileUrl = Website.UserProfile(123).url
This might be a place where you want to use NSString's bridging to String and it's -initWithFormat:
let userProfileUrl = String(format: "website.com/user/%d", userId)